Roo/bootstrap/MessageBox.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:400,
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             
8616             }
8617             
8618             if(this.labelWidth > 12){
8619                 labelCfg.style = "width: " + this.labelWidth + 'px';
8620             }
8621             
8622             if(this.labelWidth < 13 && this.labelmd == 0){
8623                 this.labelmd = this.labelWidth;
8624             }
8625             
8626             if(this.labellg > 0){
8627                 labelCfg.cls += ' col-lg-' + this.labellg;
8628                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8629             }
8630             
8631             if(this.labelmd > 0){
8632                 labelCfg.cls += ' col-md-' + this.labelmd;
8633                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8634             }
8635             
8636             if(this.labelsm > 0){
8637                 labelCfg.cls += ' col-sm-' + this.labelsm;
8638                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8639             }
8640             
8641             if(this.labelxs > 0){
8642                 labelCfg.cls += ' col-xs-' + this.labelxs;
8643                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8644             }
8645             
8646             
8647         } else if ( this.fieldLabel.length) {
8648                 
8649             cfg.cn = [
8650                 {
8651                     tag : 'i',
8652                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8653                     tooltip : 'This field is required'
8654                 },
8655                 {
8656                     tag: 'label',
8657                    //cls : 'input-group-addon',
8658                     html : this.fieldLabel
8659
8660                 },
8661
8662                inputblock
8663
8664            ];
8665            
8666            if(this.indicatorpos == 'right'){
8667                 
8668                 cfg.cn = [
8669                     {
8670                         tag: 'label',
8671                        //cls : 'input-group-addon',
8672                         html : this.fieldLabel
8673
8674                     },
8675                     {
8676                         tag : 'i',
8677                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8678                         tooltip : 'This field is required'
8679                     },
8680
8681                    inputblock
8682
8683                ];
8684
8685             }
8686
8687         } else {
8688             
8689             cfg.cn = [
8690
8691                     inputblock
8692
8693             ];
8694                 
8695                 
8696         };
8697         
8698         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8699            cfg.cls += ' navbar-form';
8700         }
8701         
8702         if (this.parentType === 'NavGroup') {
8703            cfg.cls += ' navbar-form';
8704            cfg.tag = 'li';
8705         }
8706         
8707         return cfg;
8708         
8709     },
8710     /**
8711      * return the real input element.
8712      */
8713     inputEl: function ()
8714     {
8715         return this.el.select('input.form-control',true).first();
8716     },
8717     
8718     tooltipEl : function()
8719     {
8720         return this.inputEl();
8721     },
8722     
8723     indicatorEl : function()
8724     {
8725         var indicator = this.el.select('i.roo-required-indicator',true).first();
8726         
8727         if(!indicator){
8728             return false;
8729         }
8730         
8731         return indicator;
8732         
8733     },
8734     
8735     setDisabled : function(v)
8736     {
8737         var i  = this.inputEl().dom;
8738         if (!v) {
8739             i.removeAttribute('disabled');
8740             return;
8741             
8742         }
8743         i.setAttribute('disabled','true');
8744     },
8745     initEvents : function()
8746     {
8747           
8748         this.inputEl().on("keydown" , this.fireKey,  this);
8749         this.inputEl().on("focus", this.onFocus,  this);
8750         this.inputEl().on("blur", this.onBlur,  this);
8751         
8752         this.inputEl().relayEvent('keyup', this);
8753         
8754         this.indicator = this.indicatorEl();
8755         
8756         if(this.indicator){
8757             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8758             this.indicator.hide();
8759         }
8760  
8761         // reference to original value for reset
8762         this.originalValue = this.getValue();
8763         //Roo.form.TextField.superclass.initEvents.call(this);
8764         if(this.validationEvent == 'keyup'){
8765             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8766             this.inputEl().on('keyup', this.filterValidation, this);
8767         }
8768         else if(this.validationEvent !== false){
8769             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8770         }
8771         
8772         if(this.selectOnFocus){
8773             this.on("focus", this.preFocus, this);
8774             
8775         }
8776         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8777             this.inputEl().on("keypress", this.filterKeys, this);
8778         } else {
8779             this.inputEl().relayEvent('keypress', this);
8780         }
8781        /* if(this.grow){
8782             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8783             this.el.on("click", this.autoSize,  this);
8784         }
8785         */
8786         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8787             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8788         }
8789         
8790         if (typeof(this.before) == 'object') {
8791             this.before.render(this.el.select('.roo-input-before',true).first());
8792         }
8793         if (typeof(this.after) == 'object') {
8794             this.after.render(this.el.select('.roo-input-after',true).first());
8795         }
8796         
8797         
8798     },
8799     filterValidation : function(e){
8800         if(!e.isNavKeyPress()){
8801             this.validationTask.delay(this.validationDelay);
8802         }
8803     },
8804      /**
8805      * Validates the field value
8806      * @return {Boolean} True if the value is valid, else false
8807      */
8808     validate : function(){
8809         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8810         if(this.disabled || this.validateValue(this.getRawValue())){
8811             this.markValid();
8812             return true;
8813         }
8814         
8815         this.markInvalid();
8816         return false;
8817     },
8818     
8819     
8820     /**
8821      * Validates a value according to the field's validation rules and marks the field as invalid
8822      * if the validation fails
8823      * @param {Mixed} value The value to validate
8824      * @return {Boolean} True if the value is valid, else false
8825      */
8826     validateValue : function(value){
8827         if(value.length < 1)  { // if it's blank
8828             if(this.allowBlank){
8829                 return true;
8830             }
8831             return false;
8832         }
8833         
8834         if(value.length < this.minLength){
8835             return false;
8836         }
8837         if(value.length > this.maxLength){
8838             return false;
8839         }
8840         if(this.vtype){
8841             var vt = Roo.form.VTypes;
8842             if(!vt[this.vtype](value, this)){
8843                 return false;
8844             }
8845         }
8846         if(typeof this.validator == "function"){
8847             var msg = this.validator(value);
8848             if(msg !== true){
8849                 return false;
8850             }
8851         }
8852         
8853         if(this.regex && !this.regex.test(value)){
8854             return false;
8855         }
8856         
8857         return true;
8858     },
8859
8860     
8861     
8862      // private
8863     fireKey : function(e){
8864         //Roo.log('field ' + e.getKey());
8865         if(e.isNavKeyPress()){
8866             this.fireEvent("specialkey", this, e);
8867         }
8868     },
8869     focus : function (selectText){
8870         if(this.rendered){
8871             this.inputEl().focus();
8872             if(selectText === true){
8873                 this.inputEl().dom.select();
8874             }
8875         }
8876         return this;
8877     } ,
8878     
8879     onFocus : function(){
8880         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8881            // this.el.addClass(this.focusClass);
8882         }
8883         if(!this.hasFocus){
8884             this.hasFocus = true;
8885             this.startValue = this.getValue();
8886             this.fireEvent("focus", this);
8887         }
8888     },
8889     
8890     beforeBlur : Roo.emptyFn,
8891
8892     
8893     // private
8894     onBlur : function(){
8895         this.beforeBlur();
8896         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8897             //this.el.removeClass(this.focusClass);
8898         }
8899         this.hasFocus = false;
8900         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8901             this.validate();
8902         }
8903         var v = this.getValue();
8904         if(String(v) !== String(this.startValue)){
8905             this.fireEvent('change', this, v, this.startValue);
8906         }
8907         this.fireEvent("blur", this);
8908     },
8909     
8910     /**
8911      * Resets the current field value to the originally loaded value and clears any validation messages
8912      */
8913     reset : function(){
8914         this.setValue(this.originalValue);
8915         this.validate();
8916     },
8917      /**
8918      * Returns the name of the field
8919      * @return {Mixed} name The name field
8920      */
8921     getName: function(){
8922         return this.name;
8923     },
8924      /**
8925      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8926      * @return {Mixed} value The field value
8927      */
8928     getValue : function(){
8929         
8930         var v = this.inputEl().getValue();
8931         
8932         return v;
8933     },
8934     /**
8935      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8936      * @return {Mixed} value The field value
8937      */
8938     getRawValue : function(){
8939         var v = this.inputEl().getValue();
8940         
8941         return v;
8942     },
8943     
8944     /**
8945      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8946      * @param {Mixed} value The value to set
8947      */
8948     setRawValue : function(v){
8949         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8950     },
8951     
8952     selectText : function(start, end){
8953         var v = this.getRawValue();
8954         if(v.length > 0){
8955             start = start === undefined ? 0 : start;
8956             end = end === undefined ? v.length : end;
8957             var d = this.inputEl().dom;
8958             if(d.setSelectionRange){
8959                 d.setSelectionRange(start, end);
8960             }else if(d.createTextRange){
8961                 var range = d.createTextRange();
8962                 range.moveStart("character", start);
8963                 range.moveEnd("character", v.length-end);
8964                 range.select();
8965             }
8966         }
8967     },
8968     
8969     /**
8970      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8971      * @param {Mixed} value The value to set
8972      */
8973     setValue : function(v){
8974         this.value = v;
8975         if(this.rendered){
8976             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8977             this.validate();
8978         }
8979     },
8980     
8981     /*
8982     processValue : function(value){
8983         if(this.stripCharsRe){
8984             var newValue = value.replace(this.stripCharsRe, '');
8985             if(newValue !== value){
8986                 this.setRawValue(newValue);
8987                 return newValue;
8988             }
8989         }
8990         return value;
8991     },
8992   */
8993     preFocus : function(){
8994         
8995         if(this.selectOnFocus){
8996             this.inputEl().dom.select();
8997         }
8998     },
8999     filterKeys : function(e){
9000         var k = e.getKey();
9001         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9002             return;
9003         }
9004         var c = e.getCharCode(), cc = String.fromCharCode(c);
9005         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9006             return;
9007         }
9008         if(!this.maskRe.test(cc)){
9009             e.stopEvent();
9010         }
9011     },
9012      /**
9013      * Clear any invalid styles/messages for this field
9014      */
9015     clearInvalid : function(){
9016         
9017         if(!this.el || this.preventMark){ // not rendered
9018             return;
9019         }
9020         
9021         if(this.indicator){
9022             this.indicator.hide();
9023         }
9024         
9025         this.el.removeClass(this.invalidClass);
9026         
9027         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9028             
9029             var feedback = this.el.select('.form-control-feedback', true).first();
9030             
9031             if(feedback){
9032                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9033             }
9034             
9035         }
9036         
9037         this.fireEvent('valid', this);
9038     },
9039     
9040      /**
9041      * Mark this field as valid
9042      */
9043     markValid : function()
9044     {
9045         if(!this.el  || this.preventMark){ // not rendered
9046             return;
9047         }
9048         
9049         this.el.removeClass([this.invalidClass, this.validClass]);
9050         
9051         var feedback = this.el.select('.form-control-feedback', true).first();
9052             
9053         if(feedback){
9054             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9055         }
9056
9057         if(this.disabled){
9058             return;
9059         }
9060         
9061         if(this.allowBlank && !this.getRawValue().length){
9062             return;
9063         }
9064         
9065         if(this.indicator){
9066             this.indicator.hide();
9067         }
9068         
9069         this.el.addClass(this.validClass);
9070         
9071         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9072             
9073             var feedback = this.el.select('.form-control-feedback', true).first();
9074             
9075             if(feedback){
9076                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9077                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9078             }
9079             
9080         }
9081         
9082         this.fireEvent('valid', this);
9083     },
9084     
9085      /**
9086      * Mark this field as invalid
9087      * @param {String} msg The validation message
9088      */
9089     markInvalid : function(msg)
9090     {
9091         if(!this.el  || this.preventMark){ // not rendered
9092             return;
9093         }
9094         
9095         this.el.removeClass([this.invalidClass, this.validClass]);
9096         
9097         var feedback = this.el.select('.form-control-feedback', true).first();
9098             
9099         if(feedback){
9100             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9101         }
9102
9103         if(this.disabled){
9104             return;
9105         }
9106         
9107         if(this.allowBlank && !this.getRawValue().length){
9108             return;
9109         }
9110         
9111         if(this.indicator){
9112             this.indicator.show();
9113         }
9114         
9115         this.el.addClass(this.invalidClass);
9116         
9117         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9118             
9119             var feedback = this.el.select('.form-control-feedback', true).first();
9120             
9121             if(feedback){
9122                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9123                 
9124                 if(this.getValue().length || this.forceFeedback){
9125                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9126                 }
9127                 
9128             }
9129             
9130         }
9131         
9132         this.fireEvent('invalid', this, msg);
9133     },
9134     // private
9135     SafariOnKeyDown : function(event)
9136     {
9137         // this is a workaround for a password hang bug on chrome/ webkit.
9138         if (this.inputEl().dom.type != 'password') {
9139             return;
9140         }
9141         
9142         var isSelectAll = false;
9143         
9144         if(this.inputEl().dom.selectionEnd > 0){
9145             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9146         }
9147         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9148             event.preventDefault();
9149             this.setValue('');
9150             return;
9151         }
9152         
9153         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9154             
9155             event.preventDefault();
9156             // this is very hacky as keydown always get's upper case.
9157             //
9158             var cc = String.fromCharCode(event.getCharCode());
9159             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9160             
9161         }
9162     },
9163     adjustWidth : function(tag, w){
9164         tag = tag.toLowerCase();
9165         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9166             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9167                 if(tag == 'input'){
9168                     return w + 2;
9169                 }
9170                 if(tag == 'textarea'){
9171                     return w-2;
9172                 }
9173             }else if(Roo.isOpera){
9174                 if(tag == 'input'){
9175                     return w + 2;
9176                 }
9177                 if(tag == 'textarea'){
9178                     return w-2;
9179                 }
9180             }
9181         }
9182         return w;
9183     }
9184     
9185 });
9186
9187  
9188 /*
9189  * - LGPL
9190  *
9191  * Input
9192  * 
9193  */
9194
9195 /**
9196  * @class Roo.bootstrap.TextArea
9197  * @extends Roo.bootstrap.Input
9198  * Bootstrap TextArea class
9199  * @cfg {Number} cols Specifies the visible width of a text area
9200  * @cfg {Number} rows Specifies the visible number of lines in a text area
9201  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9202  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9203  * @cfg {string} html text
9204  * 
9205  * @constructor
9206  * Create a new TextArea
9207  * @param {Object} config The config object
9208  */
9209
9210 Roo.bootstrap.TextArea = function(config){
9211     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9212    
9213 };
9214
9215 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9216      
9217     cols : false,
9218     rows : 5,
9219     readOnly : false,
9220     warp : 'soft',
9221     resize : false,
9222     value: false,
9223     html: false,
9224     
9225     getAutoCreate : function(){
9226         
9227         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9228         
9229         var id = Roo.id();
9230         
9231         var cfg = {};
9232         
9233         var input =  {
9234             tag: 'textarea',
9235             id : id,
9236             warp : this.warp,
9237             rows : this.rows,
9238             value : this.value || '',
9239             html: this.html || '',
9240             cls : 'form-control',
9241             placeholder : this.placeholder || '' 
9242             
9243         };
9244         
9245         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9246             input.maxLength = this.maxLength;
9247         }
9248         
9249         if(this.resize){
9250             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9251         }
9252         
9253         if(this.cols){
9254             input.cols = this.cols;
9255         }
9256         
9257         if (this.readOnly) {
9258             input.readonly = true;
9259         }
9260         
9261         if (this.name) {
9262             input.name = this.name;
9263         }
9264         
9265         if (this.size) {
9266             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9267         }
9268         
9269         var settings=this;
9270         ['xs','sm','md','lg'].map(function(size){
9271             if (settings[size]) {
9272                 cfg.cls += ' col-' + size + '-' + settings[size];
9273             }
9274         });
9275         
9276         var inputblock = input;
9277         
9278         if(this.hasFeedback && !this.allowBlank){
9279             
9280             var feedback = {
9281                 tag: 'span',
9282                 cls: 'glyphicon form-control-feedback'
9283             };
9284
9285             inputblock = {
9286                 cls : 'has-feedback',
9287                 cn :  [
9288                     input,
9289                     feedback
9290                 ] 
9291             };  
9292         }
9293         
9294         
9295         if (this.before || this.after) {
9296             
9297             inputblock = {
9298                 cls : 'input-group',
9299                 cn :  [] 
9300             };
9301             if (this.before) {
9302                 inputblock.cn.push({
9303                     tag :'span',
9304                     cls : 'input-group-addon',
9305                     html : this.before
9306                 });
9307             }
9308             
9309             inputblock.cn.push(input);
9310             
9311             if(this.hasFeedback && !this.allowBlank){
9312                 inputblock.cls += ' has-feedback';
9313                 inputblock.cn.push(feedback);
9314             }
9315             
9316             if (this.after) {
9317                 inputblock.cn.push({
9318                     tag :'span',
9319                     cls : 'input-group-addon',
9320                     html : this.after
9321                 });
9322             }
9323             
9324         }
9325         
9326         if (align ==='left' && this.fieldLabel.length) {
9327             cfg.cn = [
9328                 {
9329                     tag: 'label',
9330                     'for' :  id,
9331                     cls : 'control-label',
9332                     html : this.fieldLabel
9333                 },
9334                 {
9335                     cls : "",
9336                     cn: [
9337                         inputblock
9338                     ]
9339                 }
9340
9341             ];
9342             
9343             if(this.labelWidth > 12){
9344                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9345             }
9346
9347             if(this.labelWidth < 13 && this.labelmd == 0){
9348                 this.labelmd = this.labelWidth;
9349             }
9350
9351             if(this.labellg > 0){
9352                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9353                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9354             }
9355
9356             if(this.labelmd > 0){
9357                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9358                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9359             }
9360
9361             if(this.labelsm > 0){
9362                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9363                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9364             }
9365
9366             if(this.labelxs > 0){
9367                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9368                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9369             }
9370             
9371         } else if ( this.fieldLabel.length) {
9372             cfg.cn = [
9373
9374                {
9375                    tag: 'label',
9376                    //cls : 'input-group-addon',
9377                    html : this.fieldLabel
9378
9379                },
9380
9381                inputblock
9382
9383            ];
9384
9385         } else {
9386
9387             cfg.cn = [
9388
9389                 inputblock
9390
9391             ];
9392                 
9393         }
9394         
9395         if (this.disabled) {
9396             input.disabled=true;
9397         }
9398         
9399         return cfg;
9400         
9401     },
9402     /**
9403      * return the real textarea element.
9404      */
9405     inputEl: function ()
9406     {
9407         return this.el.select('textarea.form-control',true).first();
9408     },
9409     
9410     /**
9411      * Clear any invalid styles/messages for this field
9412      */
9413     clearInvalid : function()
9414     {
9415         
9416         if(!this.el || this.preventMark){ // not rendered
9417             return;
9418         }
9419         
9420         var label = this.el.select('label', true).first();
9421         var icon = this.el.select('i.fa-star', true).first();
9422         
9423         if(label && icon){
9424             icon.remove();
9425         }
9426         
9427         this.el.removeClass(this.invalidClass);
9428         
9429         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9430             
9431             var feedback = this.el.select('.form-control-feedback', true).first();
9432             
9433             if(feedback){
9434                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9435             }
9436             
9437         }
9438         
9439         this.fireEvent('valid', this);
9440     },
9441     
9442      /**
9443      * Mark this field as valid
9444      */
9445     markValid : function()
9446     {
9447         if(!this.el  || this.preventMark){ // not rendered
9448             return;
9449         }
9450         
9451         this.el.removeClass([this.invalidClass, this.validClass]);
9452         
9453         var feedback = this.el.select('.form-control-feedback', true).first();
9454             
9455         if(feedback){
9456             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9457         }
9458
9459         if(this.disabled || this.allowBlank){
9460             return;
9461         }
9462         
9463         var label = this.el.select('label', true).first();
9464         var icon = this.el.select('i.fa-star', true).first();
9465         
9466         if(label && icon){
9467             icon.remove();
9468         }
9469         
9470         this.el.addClass(this.validClass);
9471         
9472         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9473             
9474             var feedback = this.el.select('.form-control-feedback', true).first();
9475             
9476             if(feedback){
9477                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9478                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9479             }
9480             
9481         }
9482         
9483         this.fireEvent('valid', this);
9484     },
9485     
9486      /**
9487      * Mark this field as invalid
9488      * @param {String} msg The validation message
9489      */
9490     markInvalid : function(msg)
9491     {
9492         if(!this.el  || this.preventMark){ // not rendered
9493             return;
9494         }
9495         
9496         this.el.removeClass([this.invalidClass, this.validClass]);
9497         
9498         var feedback = this.el.select('.form-control-feedback', true).first();
9499             
9500         if(feedback){
9501             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9502         }
9503
9504         if(this.disabled || this.allowBlank){
9505             return;
9506         }
9507         
9508         var label = this.el.select('label', true).first();
9509         var icon = this.el.select('i.fa-star', true).first();
9510         
9511         if(!this.getValue().length && label && !icon){
9512             this.el.createChild({
9513                 tag : 'i',
9514                 cls : 'text-danger fa fa-lg fa-star',
9515                 tooltip : 'This field is required',
9516                 style : 'margin-right:5px;'
9517             }, label, true);
9518         }
9519
9520         this.el.addClass(this.invalidClass);
9521         
9522         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9523             
9524             var feedback = this.el.select('.form-control-feedback', true).first();
9525             
9526             if(feedback){
9527                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9528                 
9529                 if(this.getValue().length || this.forceFeedback){
9530                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9531                 }
9532                 
9533             }
9534             
9535         }
9536         
9537         this.fireEvent('invalid', this, msg);
9538     }
9539 });
9540
9541  
9542 /*
9543  * - LGPL
9544  *
9545  * trigger field - base class for combo..
9546  * 
9547  */
9548  
9549 /**
9550  * @class Roo.bootstrap.TriggerField
9551  * @extends Roo.bootstrap.Input
9552  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9553  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9554  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9555  * for which you can provide a custom implementation.  For example:
9556  * <pre><code>
9557 var trigger = new Roo.bootstrap.TriggerField();
9558 trigger.onTriggerClick = myTriggerFn;
9559 trigger.applyTo('my-field');
9560 </code></pre>
9561  *
9562  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9563  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9564  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9565  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9566  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9567
9568  * @constructor
9569  * Create a new TriggerField.
9570  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9571  * to the base TextField)
9572  */
9573 Roo.bootstrap.TriggerField = function(config){
9574     this.mimicing = false;
9575     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9576 };
9577
9578 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9579     /**
9580      * @cfg {String} triggerClass A CSS class to apply to the trigger
9581      */
9582      /**
9583      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9584      */
9585     hideTrigger:false,
9586
9587     /**
9588      * @cfg {Boolean} removable (true|false) special filter default false
9589      */
9590     removable : false,
9591     
9592     /** @cfg {Boolean} grow @hide */
9593     /** @cfg {Number} growMin @hide */
9594     /** @cfg {Number} growMax @hide */
9595
9596     /**
9597      * @hide 
9598      * @method
9599      */
9600     autoSize: Roo.emptyFn,
9601     // private
9602     monitorTab : true,
9603     // private
9604     deferHeight : true,
9605
9606     
9607     actionMode : 'wrap',
9608     
9609     caret : false,
9610     
9611     
9612     getAutoCreate : function(){
9613        
9614         var align = this.labelAlign || this.parentLabelAlign();
9615         
9616         var id = Roo.id();
9617         
9618         var cfg = {
9619             cls: 'form-group' //input-group
9620         };
9621         
9622         
9623         var input =  {
9624             tag: 'input',
9625             id : id,
9626             type : this.inputType,
9627             cls : 'form-control',
9628             autocomplete: 'new-password',
9629             placeholder : this.placeholder || '' 
9630             
9631         };
9632         if (this.name) {
9633             input.name = this.name;
9634         }
9635         if (this.size) {
9636             input.cls += ' input-' + this.size;
9637         }
9638         
9639         if (this.disabled) {
9640             input.disabled=true;
9641         }
9642         
9643         var inputblock = input;
9644         
9645         if(this.hasFeedback && !this.allowBlank){
9646             
9647             var feedback = {
9648                 tag: 'span',
9649                 cls: 'glyphicon form-control-feedback'
9650             };
9651             
9652             if(this.removable && !this.editable && !this.tickable){
9653                 inputblock = {
9654                     cls : 'has-feedback',
9655                     cn :  [
9656                         inputblock,
9657                         {
9658                             tag: 'button',
9659                             html : 'x',
9660                             cls : 'roo-combo-removable-btn close'
9661                         },
9662                         feedback
9663                     ] 
9664                 };
9665             } else {
9666                 inputblock = {
9667                     cls : 'has-feedback',
9668                     cn :  [
9669                         inputblock,
9670                         feedback
9671                     ] 
9672                 };
9673             }
9674
9675         } else {
9676             if(this.removable && !this.editable && !this.tickable){
9677                 inputblock = {
9678                     cls : 'roo-removable',
9679                     cn :  [
9680                         inputblock,
9681                         {
9682                             tag: 'button',
9683                             html : 'x',
9684                             cls : 'roo-combo-removable-btn close'
9685                         }
9686                     ] 
9687                 };
9688             }
9689         }
9690         
9691         if (this.before || this.after) {
9692             
9693             inputblock = {
9694                 cls : 'input-group',
9695                 cn :  [] 
9696             };
9697             if (this.before) {
9698                 inputblock.cn.push({
9699                     tag :'span',
9700                     cls : 'input-group-addon',
9701                     html : this.before
9702                 });
9703             }
9704             
9705             inputblock.cn.push(input);
9706             
9707             if(this.hasFeedback && !this.allowBlank){
9708                 inputblock.cls += ' has-feedback';
9709                 inputblock.cn.push(feedback);
9710             }
9711             
9712             if (this.after) {
9713                 inputblock.cn.push({
9714                     tag :'span',
9715                     cls : 'input-group-addon',
9716                     html : this.after
9717                 });
9718             }
9719             
9720         };
9721         
9722         var box = {
9723             tag: 'div',
9724             cn: [
9725                 {
9726                     tag: 'input',
9727                     type : 'hidden',
9728                     cls: 'form-hidden-field'
9729                 },
9730                 inputblock
9731             ]
9732             
9733         };
9734         
9735         if(this.multiple){
9736             box = {
9737                 tag: 'div',
9738                 cn: [
9739                     {
9740                         tag: 'input',
9741                         type : 'hidden',
9742                         cls: 'form-hidden-field'
9743                     },
9744                     {
9745                         tag: 'ul',
9746                         cls: 'roo-select2-choices',
9747                         cn:[
9748                             {
9749                                 tag: 'li',
9750                                 cls: 'roo-select2-search-field',
9751                                 cn: [
9752
9753                                     inputblock
9754                                 ]
9755                             }
9756                         ]
9757                     }
9758                 ]
9759             }
9760         };
9761         
9762         var combobox = {
9763             cls: 'roo-select2-container input-group',
9764             cn: [
9765                 box
9766 //                {
9767 //                    tag: 'ul',
9768 //                    cls: 'typeahead typeahead-long dropdown-menu',
9769 //                    style: 'display:none'
9770 //                }
9771             ]
9772         };
9773         
9774         if(!this.multiple && this.showToggleBtn){
9775             
9776             var caret = {
9777                         tag: 'span',
9778                         cls: 'caret'
9779              };
9780             if (this.caret != false) {
9781                 caret = {
9782                      tag: 'i',
9783                      cls: 'fa fa-' + this.caret
9784                 };
9785                 
9786             }
9787             
9788             combobox.cn.push({
9789                 tag :'span',
9790                 cls : 'input-group-addon btn dropdown-toggle',
9791                 cn : [
9792                     caret,
9793                     {
9794                         tag: 'span',
9795                         cls: 'combobox-clear',
9796                         cn  : [
9797                             {
9798                                 tag : 'i',
9799                                 cls: 'icon-remove'
9800                             }
9801                         ]
9802                     }
9803                 ]
9804
9805             })
9806         }
9807         
9808         if(this.multiple){
9809             combobox.cls += ' roo-select2-container-multi';
9810         }
9811         
9812         if (align ==='left' && this.fieldLabel.length) {
9813             
9814             cfg.cn = [
9815                 {
9816                     tag : 'i',
9817                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9818                     tooltip : 'This field is required'
9819                 },
9820                 {
9821                     tag: 'label',
9822                     'for' :  id,
9823                     cls : 'control-label',
9824                     html : this.fieldLabel
9825
9826                 },
9827                 {
9828                     cls : "", 
9829                     cn: [
9830                         combobox
9831                     ]
9832                 }
9833
9834             ];
9835             
9836             var labelCfg = cfg.cn[1];
9837             var contentCfg = cfg.cn[2];
9838             
9839             if(this.indicatorpos == 'right'){
9840                 cfg.cn = [
9841                     {
9842                         tag: 'label',
9843                         'for' :  id,
9844                         cls : 'control-label',
9845                         html : this.fieldLabel
9846
9847                     },
9848                     {
9849                         tag : 'i',
9850                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9851                         tooltip : 'This field is required'
9852                     },
9853                     {
9854                         cls : "", 
9855                         cn: [
9856                             combobox
9857                         ]
9858                     }
9859
9860                 ];
9861                 
9862                 labelCfg = cfg.cn[0];
9863             }
9864             
9865             if(this.labelWidth > 12){
9866                 labelCfg.style = "width: " + this.labelWidth + 'px';
9867             }
9868             
9869             if(this.labelWidth < 13 && this.labelmd == 0){
9870                 this.labelmd = this.labelWidth;
9871             }
9872             
9873             if(this.labellg > 0){
9874                 labelCfg.cls += ' col-lg-' + this.labellg;
9875                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9876             }
9877             
9878             if(this.labelmd > 0){
9879                 labelCfg.cls += ' col-md-' + this.labelmd;
9880                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9881             }
9882             
9883             if(this.labelsm > 0){
9884                 labelCfg.cls += ' col-sm-' + this.labelsm;
9885                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9886             }
9887             
9888             if(this.labelxs > 0){
9889                 labelCfg.cls += ' col-xs-' + this.labelxs;
9890                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9891             }
9892             
9893         } else if ( this.fieldLabel.length) {
9894 //                Roo.log(" label");
9895             cfg.cn = [
9896                 {
9897                    tag : 'i',
9898                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9899                    tooltip : 'This field is required'
9900                },
9901                {
9902                    tag: 'label',
9903                    //cls : 'input-group-addon',
9904                    html : this.fieldLabel
9905
9906                },
9907
9908                combobox
9909
9910             ];
9911             
9912             if(this.indicatorpos == 'right'){
9913                 
9914                 cfg.cn = [
9915                     {
9916                        tag: 'label',
9917                        //cls : 'input-group-addon',
9918                        html : this.fieldLabel
9919
9920                     },
9921                     {
9922                        tag : 'i',
9923                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9924                        tooltip : 'This field is required'
9925                     },
9926                     
9927                     combobox
9928
9929                 ];
9930
9931             }
9932
9933         } else {
9934             
9935 //                Roo.log(" no label && no align");
9936                 cfg = combobox
9937                      
9938                 
9939         }
9940          
9941         var settings=this;
9942         ['xs','sm','md','lg'].map(function(size){
9943             if (settings[size]) {
9944                 cfg.cls += ' col-' + size + '-' + settings[size];
9945             }
9946         });
9947         
9948         return cfg;
9949         
9950     },
9951     
9952     
9953     
9954     // private
9955     onResize : function(w, h){
9956 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9957 //        if(typeof w == 'number'){
9958 //            var x = w - this.trigger.getWidth();
9959 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9960 //            this.trigger.setStyle('left', x+'px');
9961 //        }
9962     },
9963
9964     // private
9965     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9966
9967     // private
9968     getResizeEl : function(){
9969         return this.inputEl();
9970     },
9971
9972     // private
9973     getPositionEl : function(){
9974         return this.inputEl();
9975     },
9976
9977     // private
9978     alignErrorIcon : function(){
9979         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9980     },
9981
9982     // private
9983     initEvents : function(){
9984         
9985         this.createList();
9986         
9987         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9988         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9989         if(!this.multiple && this.showToggleBtn){
9990             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9991             if(this.hideTrigger){
9992                 this.trigger.setDisplayed(false);
9993             }
9994             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9995         }
9996         
9997         if(this.multiple){
9998             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9999         }
10000         
10001         if(this.removable && !this.editable && !this.tickable){
10002             var close = this.closeTriggerEl();
10003             
10004             if(close){
10005                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10006                 close.on('click', this.removeBtnClick, this, close);
10007             }
10008         }
10009         
10010         //this.trigger.addClassOnOver('x-form-trigger-over');
10011         //this.trigger.addClassOnClick('x-form-trigger-click');
10012         
10013         //if(!this.width){
10014         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10015         //}
10016     },
10017     
10018     closeTriggerEl : function()
10019     {
10020         var close = this.el.select('.roo-combo-removable-btn', true).first();
10021         return close ? close : false;
10022     },
10023     
10024     removeBtnClick : function(e, h, el)
10025     {
10026         e.preventDefault();
10027         
10028         if(this.fireEvent("remove", this) !== false){
10029             this.reset();
10030             this.fireEvent("afterremove", this)
10031         }
10032     },
10033     
10034     createList : function()
10035     {
10036         this.list = Roo.get(document.body).createChild({
10037             tag: 'ul',
10038             cls: 'typeahead typeahead-long dropdown-menu',
10039             style: 'display:none'
10040         });
10041         
10042         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10043         
10044     },
10045
10046     // private
10047     initTrigger : function(){
10048        
10049     },
10050
10051     // private
10052     onDestroy : function(){
10053         if(this.trigger){
10054             this.trigger.removeAllListeners();
10055           //  this.trigger.remove();
10056         }
10057         //if(this.wrap){
10058         //    this.wrap.remove();
10059         //}
10060         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10061     },
10062
10063     // private
10064     onFocus : function(){
10065         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10066         /*
10067         if(!this.mimicing){
10068             this.wrap.addClass('x-trigger-wrap-focus');
10069             this.mimicing = true;
10070             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10071             if(this.monitorTab){
10072                 this.el.on("keydown", this.checkTab, this);
10073             }
10074         }
10075         */
10076     },
10077
10078     // private
10079     checkTab : function(e){
10080         if(e.getKey() == e.TAB){
10081             this.triggerBlur();
10082         }
10083     },
10084
10085     // private
10086     onBlur : function(){
10087         // do nothing
10088     },
10089
10090     // private
10091     mimicBlur : function(e, t){
10092         /*
10093         if(!this.wrap.contains(t) && this.validateBlur()){
10094             this.triggerBlur();
10095         }
10096         */
10097     },
10098
10099     // private
10100     triggerBlur : function(){
10101         this.mimicing = false;
10102         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10103         if(this.monitorTab){
10104             this.el.un("keydown", this.checkTab, this);
10105         }
10106         //this.wrap.removeClass('x-trigger-wrap-focus');
10107         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10108     },
10109
10110     // private
10111     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10112     validateBlur : function(e, t){
10113         return true;
10114     },
10115
10116     // private
10117     onDisable : function(){
10118         this.inputEl().dom.disabled = true;
10119         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10120         //if(this.wrap){
10121         //    this.wrap.addClass('x-item-disabled');
10122         //}
10123     },
10124
10125     // private
10126     onEnable : function(){
10127         this.inputEl().dom.disabled = false;
10128         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10129         //if(this.wrap){
10130         //    this.el.removeClass('x-item-disabled');
10131         //}
10132     },
10133
10134     // private
10135     onShow : function(){
10136         var ae = this.getActionEl();
10137         
10138         if(ae){
10139             ae.dom.style.display = '';
10140             ae.dom.style.visibility = 'visible';
10141         }
10142     },
10143
10144     // private
10145     
10146     onHide : function(){
10147         var ae = this.getActionEl();
10148         ae.dom.style.display = 'none';
10149     },
10150
10151     /**
10152      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10153      * by an implementing function.
10154      * @method
10155      * @param {EventObject} e
10156      */
10157     onTriggerClick : Roo.emptyFn
10158 });
10159  /*
10160  * Based on:
10161  * Ext JS Library 1.1.1
10162  * Copyright(c) 2006-2007, Ext JS, LLC.
10163  *
10164  * Originally Released Under LGPL - original licence link has changed is not relivant.
10165  *
10166  * Fork - LGPL
10167  * <script type="text/javascript">
10168  */
10169
10170
10171 /**
10172  * @class Roo.data.SortTypes
10173  * @singleton
10174  * Defines the default sorting (casting?) comparison functions used when sorting data.
10175  */
10176 Roo.data.SortTypes = {
10177     /**
10178      * Default sort that does nothing
10179      * @param {Mixed} s The value being converted
10180      * @return {Mixed} The comparison value
10181      */
10182     none : function(s){
10183         return s;
10184     },
10185     
10186     /**
10187      * The regular expression used to strip tags
10188      * @type {RegExp}
10189      * @property
10190      */
10191     stripTagsRE : /<\/?[^>]+>/gi,
10192     
10193     /**
10194      * Strips all HTML tags to sort on text only
10195      * @param {Mixed} s The value being converted
10196      * @return {String} The comparison value
10197      */
10198     asText : function(s){
10199         return String(s).replace(this.stripTagsRE, "");
10200     },
10201     
10202     /**
10203      * Strips all HTML tags to sort on text only - Case insensitive
10204      * @param {Mixed} s The value being converted
10205      * @return {String} The comparison value
10206      */
10207     asUCText : function(s){
10208         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10209     },
10210     
10211     /**
10212      * Case insensitive string
10213      * @param {Mixed} s The value being converted
10214      * @return {String} The comparison value
10215      */
10216     asUCString : function(s) {
10217         return String(s).toUpperCase();
10218     },
10219     
10220     /**
10221      * Date sorting
10222      * @param {Mixed} s The value being converted
10223      * @return {Number} The comparison value
10224      */
10225     asDate : function(s) {
10226         if(!s){
10227             return 0;
10228         }
10229         if(s instanceof Date){
10230             return s.getTime();
10231         }
10232         return Date.parse(String(s));
10233     },
10234     
10235     /**
10236      * Float sorting
10237      * @param {Mixed} s The value being converted
10238      * @return {Float} The comparison value
10239      */
10240     asFloat : function(s) {
10241         var val = parseFloat(String(s).replace(/,/g, ""));
10242         if(isNaN(val)) {
10243             val = 0;
10244         }
10245         return val;
10246     },
10247     
10248     /**
10249      * Integer sorting
10250      * @param {Mixed} s The value being converted
10251      * @return {Number} The comparison value
10252      */
10253     asInt : function(s) {
10254         var val = parseInt(String(s).replace(/,/g, ""));
10255         if(isNaN(val)) {
10256             val = 0;
10257         }
10258         return val;
10259     }
10260 };/*
10261  * Based on:
10262  * Ext JS Library 1.1.1
10263  * Copyright(c) 2006-2007, Ext JS, LLC.
10264  *
10265  * Originally Released Under LGPL - original licence link has changed is not relivant.
10266  *
10267  * Fork - LGPL
10268  * <script type="text/javascript">
10269  */
10270
10271 /**
10272 * @class Roo.data.Record
10273  * Instances of this class encapsulate both record <em>definition</em> information, and record
10274  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10275  * to access Records cached in an {@link Roo.data.Store} object.<br>
10276  * <p>
10277  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10278  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10279  * objects.<br>
10280  * <p>
10281  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10282  * @constructor
10283  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10284  * {@link #create}. The parameters are the same.
10285  * @param {Array} data An associative Array of data values keyed by the field name.
10286  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10287  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10288  * not specified an integer id is generated.
10289  */
10290 Roo.data.Record = function(data, id){
10291     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10292     this.data = data;
10293 };
10294
10295 /**
10296  * Generate a constructor for a specific record layout.
10297  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10298  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10299  * Each field definition object may contain the following properties: <ul>
10300  * <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,
10301  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10302  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10303  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10304  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10305  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10306  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10307  * this may be omitted.</p></li>
10308  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10309  * <ul><li>auto (Default, implies no conversion)</li>
10310  * <li>string</li>
10311  * <li>int</li>
10312  * <li>float</li>
10313  * <li>boolean</li>
10314  * <li>date</li></ul></p></li>
10315  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10316  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10317  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10318  * by the Reader into an object that will be stored in the Record. It is passed the
10319  * following parameters:<ul>
10320  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10321  * </ul></p></li>
10322  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10323  * </ul>
10324  * <br>usage:<br><pre><code>
10325 var TopicRecord = Roo.data.Record.create(
10326     {name: 'title', mapping: 'topic_title'},
10327     {name: 'author', mapping: 'username'},
10328     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10329     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10330     {name: 'lastPoster', mapping: 'user2'},
10331     {name: 'excerpt', mapping: 'post_text'}
10332 );
10333
10334 var myNewRecord = new TopicRecord({
10335     title: 'Do my job please',
10336     author: 'noobie',
10337     totalPosts: 1,
10338     lastPost: new Date(),
10339     lastPoster: 'Animal',
10340     excerpt: 'No way dude!'
10341 });
10342 myStore.add(myNewRecord);
10343 </code></pre>
10344  * @method create
10345  * @static
10346  */
10347 Roo.data.Record.create = function(o){
10348     var f = function(){
10349         f.superclass.constructor.apply(this, arguments);
10350     };
10351     Roo.extend(f, Roo.data.Record);
10352     var p = f.prototype;
10353     p.fields = new Roo.util.MixedCollection(false, function(field){
10354         return field.name;
10355     });
10356     for(var i = 0, len = o.length; i < len; i++){
10357         p.fields.add(new Roo.data.Field(o[i]));
10358     }
10359     f.getField = function(name){
10360         return p.fields.get(name);  
10361     };
10362     return f;
10363 };
10364
10365 Roo.data.Record.AUTO_ID = 1000;
10366 Roo.data.Record.EDIT = 'edit';
10367 Roo.data.Record.REJECT = 'reject';
10368 Roo.data.Record.COMMIT = 'commit';
10369
10370 Roo.data.Record.prototype = {
10371     /**
10372      * Readonly flag - true if this record has been modified.
10373      * @type Boolean
10374      */
10375     dirty : false,
10376     editing : false,
10377     error: null,
10378     modified: null,
10379
10380     // private
10381     join : function(store){
10382         this.store = store;
10383     },
10384
10385     /**
10386      * Set the named field to the specified value.
10387      * @param {String} name The name of the field to set.
10388      * @param {Object} value The value to set the field to.
10389      */
10390     set : function(name, value){
10391         if(this.data[name] == value){
10392             return;
10393         }
10394         this.dirty = true;
10395         if(!this.modified){
10396             this.modified = {};
10397         }
10398         if(typeof this.modified[name] == 'undefined'){
10399             this.modified[name] = this.data[name];
10400         }
10401         this.data[name] = value;
10402         if(!this.editing && this.store){
10403             this.store.afterEdit(this);
10404         }       
10405     },
10406
10407     /**
10408      * Get the value of the named field.
10409      * @param {String} name The name of the field to get the value of.
10410      * @return {Object} The value of the field.
10411      */
10412     get : function(name){
10413         return this.data[name]; 
10414     },
10415
10416     // private
10417     beginEdit : function(){
10418         this.editing = true;
10419         this.modified = {}; 
10420     },
10421
10422     // private
10423     cancelEdit : function(){
10424         this.editing = false;
10425         delete this.modified;
10426     },
10427
10428     // private
10429     endEdit : function(){
10430         this.editing = false;
10431         if(this.dirty && this.store){
10432             this.store.afterEdit(this);
10433         }
10434     },
10435
10436     /**
10437      * Usually called by the {@link Roo.data.Store} which owns the Record.
10438      * Rejects all changes made to the Record since either creation, or the last commit operation.
10439      * Modified fields are reverted to their original values.
10440      * <p>
10441      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10442      * of reject operations.
10443      */
10444     reject : function(){
10445         var m = this.modified;
10446         for(var n in m){
10447             if(typeof m[n] != "function"){
10448                 this.data[n] = m[n];
10449             }
10450         }
10451         this.dirty = false;
10452         delete this.modified;
10453         this.editing = false;
10454         if(this.store){
10455             this.store.afterReject(this);
10456         }
10457     },
10458
10459     /**
10460      * Usually called by the {@link Roo.data.Store} which owns the Record.
10461      * Commits all changes made to the Record since either creation, or the last commit operation.
10462      * <p>
10463      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10464      * of commit operations.
10465      */
10466     commit : function(){
10467         this.dirty = false;
10468         delete this.modified;
10469         this.editing = false;
10470         if(this.store){
10471             this.store.afterCommit(this);
10472         }
10473     },
10474
10475     // private
10476     hasError : function(){
10477         return this.error != null;
10478     },
10479
10480     // private
10481     clearError : function(){
10482         this.error = null;
10483     },
10484
10485     /**
10486      * Creates a copy of this record.
10487      * @param {String} id (optional) A new record id if you don't want to use this record's id
10488      * @return {Record}
10489      */
10490     copy : function(newId) {
10491         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10492     }
10493 };/*
10494  * Based on:
10495  * Ext JS Library 1.1.1
10496  * Copyright(c) 2006-2007, Ext JS, LLC.
10497  *
10498  * Originally Released Under LGPL - original licence link has changed is not relivant.
10499  *
10500  * Fork - LGPL
10501  * <script type="text/javascript">
10502  */
10503
10504
10505
10506 /**
10507  * @class Roo.data.Store
10508  * @extends Roo.util.Observable
10509  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10510  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10511  * <p>
10512  * 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
10513  * has no knowledge of the format of the data returned by the Proxy.<br>
10514  * <p>
10515  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10516  * instances from the data object. These records are cached and made available through accessor functions.
10517  * @constructor
10518  * Creates a new Store.
10519  * @param {Object} config A config object containing the objects needed for the Store to access data,
10520  * and read the data into Records.
10521  */
10522 Roo.data.Store = function(config){
10523     this.data = new Roo.util.MixedCollection(false);
10524     this.data.getKey = function(o){
10525         return o.id;
10526     };
10527     this.baseParams = {};
10528     // private
10529     this.paramNames = {
10530         "start" : "start",
10531         "limit" : "limit",
10532         "sort" : "sort",
10533         "dir" : "dir",
10534         "multisort" : "_multisort"
10535     };
10536
10537     if(config && config.data){
10538         this.inlineData = config.data;
10539         delete config.data;
10540     }
10541
10542     Roo.apply(this, config);
10543     
10544     if(this.reader){ // reader passed
10545         this.reader = Roo.factory(this.reader, Roo.data);
10546         this.reader.xmodule = this.xmodule || false;
10547         if(!this.recordType){
10548             this.recordType = this.reader.recordType;
10549         }
10550         if(this.reader.onMetaChange){
10551             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10552         }
10553     }
10554
10555     if(this.recordType){
10556         this.fields = this.recordType.prototype.fields;
10557     }
10558     this.modified = [];
10559
10560     this.addEvents({
10561         /**
10562          * @event datachanged
10563          * Fires when the data cache has changed, and a widget which is using this Store
10564          * as a Record cache should refresh its view.
10565          * @param {Store} this
10566          */
10567         datachanged : true,
10568         /**
10569          * @event metachange
10570          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10571          * @param {Store} this
10572          * @param {Object} meta The JSON metadata
10573          */
10574         metachange : true,
10575         /**
10576          * @event add
10577          * Fires when Records have been added to the Store
10578          * @param {Store} this
10579          * @param {Roo.data.Record[]} records The array of Records added
10580          * @param {Number} index The index at which the record(s) were added
10581          */
10582         add : true,
10583         /**
10584          * @event remove
10585          * Fires when a Record has been removed from the Store
10586          * @param {Store} this
10587          * @param {Roo.data.Record} record The Record that was removed
10588          * @param {Number} index The index at which the record was removed
10589          */
10590         remove : true,
10591         /**
10592          * @event update
10593          * Fires when a Record has been updated
10594          * @param {Store} this
10595          * @param {Roo.data.Record} record The Record that was updated
10596          * @param {String} operation The update operation being performed.  Value may be one of:
10597          * <pre><code>
10598  Roo.data.Record.EDIT
10599  Roo.data.Record.REJECT
10600  Roo.data.Record.COMMIT
10601          * </code></pre>
10602          */
10603         update : true,
10604         /**
10605          * @event clear
10606          * Fires when the data cache has been cleared.
10607          * @param {Store} this
10608          */
10609         clear : true,
10610         /**
10611          * @event beforeload
10612          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10613          * the load action will be canceled.
10614          * @param {Store} this
10615          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10616          */
10617         beforeload : true,
10618         /**
10619          * @event beforeloadadd
10620          * Fires after a new set of Records has been loaded.
10621          * @param {Store} this
10622          * @param {Roo.data.Record[]} records The Records that were loaded
10623          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10624          */
10625         beforeloadadd : true,
10626         /**
10627          * @event load
10628          * Fires after a new set of Records has been loaded, before they are added to the store.
10629          * @param {Store} this
10630          * @param {Roo.data.Record[]} records The Records that were loaded
10631          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10632          * @params {Object} return from reader
10633          */
10634         load : true,
10635         /**
10636          * @event loadexception
10637          * Fires if an exception occurs in the Proxy during loading.
10638          * Called with the signature of the Proxy's "loadexception" event.
10639          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10640          * 
10641          * @param {Proxy} 
10642          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10643          * @param {Object} load options 
10644          * @param {Object} jsonData from your request (normally this contains the Exception)
10645          */
10646         loadexception : true
10647     });
10648     
10649     if(this.proxy){
10650         this.proxy = Roo.factory(this.proxy, Roo.data);
10651         this.proxy.xmodule = this.xmodule || false;
10652         this.relayEvents(this.proxy,  ["loadexception"]);
10653     }
10654     this.sortToggle = {};
10655     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10656
10657     Roo.data.Store.superclass.constructor.call(this);
10658
10659     if(this.inlineData){
10660         this.loadData(this.inlineData);
10661         delete this.inlineData;
10662     }
10663 };
10664
10665 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10666      /**
10667     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10668     * without a remote query - used by combo/forms at present.
10669     */
10670     
10671     /**
10672     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10673     */
10674     /**
10675     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10676     */
10677     /**
10678     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10679     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10680     */
10681     /**
10682     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10683     * on any HTTP request
10684     */
10685     /**
10686     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10687     */
10688     /**
10689     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10690     */
10691     multiSort: false,
10692     /**
10693     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10694     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10695     */
10696     remoteSort : false,
10697
10698     /**
10699     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10700      * loaded or when a record is removed. (defaults to false).
10701     */
10702     pruneModifiedRecords : false,
10703
10704     // private
10705     lastOptions : null,
10706
10707     /**
10708      * Add Records to the Store and fires the add event.
10709      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10710      */
10711     add : function(records){
10712         records = [].concat(records);
10713         for(var i = 0, len = records.length; i < len; i++){
10714             records[i].join(this);
10715         }
10716         var index = this.data.length;
10717         this.data.addAll(records);
10718         this.fireEvent("add", this, records, index);
10719     },
10720
10721     /**
10722      * Remove a Record from the Store and fires the remove event.
10723      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10724      */
10725     remove : function(record){
10726         var index = this.data.indexOf(record);
10727         this.data.removeAt(index);
10728         if(this.pruneModifiedRecords){
10729             this.modified.remove(record);
10730         }
10731         this.fireEvent("remove", this, record, index);
10732     },
10733
10734     /**
10735      * Remove all Records from the Store and fires the clear event.
10736      */
10737     removeAll : function(){
10738         this.data.clear();
10739         if(this.pruneModifiedRecords){
10740             this.modified = [];
10741         }
10742         this.fireEvent("clear", this);
10743     },
10744
10745     /**
10746      * Inserts Records to the Store at the given index and fires the add event.
10747      * @param {Number} index The start index at which to insert the passed Records.
10748      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10749      */
10750     insert : function(index, records){
10751         records = [].concat(records);
10752         for(var i = 0, len = records.length; i < len; i++){
10753             this.data.insert(index, records[i]);
10754             records[i].join(this);
10755         }
10756         this.fireEvent("add", this, records, index);
10757     },
10758
10759     /**
10760      * Get the index within the cache of the passed Record.
10761      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10762      * @return {Number} The index of the passed Record. Returns -1 if not found.
10763      */
10764     indexOf : function(record){
10765         return this.data.indexOf(record);
10766     },
10767
10768     /**
10769      * Get the index within the cache of the Record with the passed id.
10770      * @param {String} id The id of the Record to find.
10771      * @return {Number} The index of the Record. Returns -1 if not found.
10772      */
10773     indexOfId : function(id){
10774         return this.data.indexOfKey(id);
10775     },
10776
10777     /**
10778      * Get the Record with the specified id.
10779      * @param {String} id The id of the Record to find.
10780      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10781      */
10782     getById : function(id){
10783         return this.data.key(id);
10784     },
10785
10786     /**
10787      * Get the Record at the specified index.
10788      * @param {Number} index The index of the Record to find.
10789      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10790      */
10791     getAt : function(index){
10792         return this.data.itemAt(index);
10793     },
10794
10795     /**
10796      * Returns a range of Records between specified indices.
10797      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10798      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10799      * @return {Roo.data.Record[]} An array of Records
10800      */
10801     getRange : function(start, end){
10802         return this.data.getRange(start, end);
10803     },
10804
10805     // private
10806     storeOptions : function(o){
10807         o = Roo.apply({}, o);
10808         delete o.callback;
10809         delete o.scope;
10810         this.lastOptions = o;
10811     },
10812
10813     /**
10814      * Loads the Record cache from the configured Proxy using the configured Reader.
10815      * <p>
10816      * If using remote paging, then the first load call must specify the <em>start</em>
10817      * and <em>limit</em> properties in the options.params property to establish the initial
10818      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10819      * <p>
10820      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10821      * and this call will return before the new data has been loaded. Perform any post-processing
10822      * in a callback function, or in a "load" event handler.</strong>
10823      * <p>
10824      * @param {Object} options An object containing properties which control loading options:<ul>
10825      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10826      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10827      * passed the following arguments:<ul>
10828      * <li>r : Roo.data.Record[]</li>
10829      * <li>options: Options object from the load call</li>
10830      * <li>success: Boolean success indicator</li></ul></li>
10831      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10832      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10833      * </ul>
10834      */
10835     load : function(options){
10836         options = options || {};
10837         if(this.fireEvent("beforeload", this, options) !== false){
10838             this.storeOptions(options);
10839             var p = Roo.apply(options.params || {}, this.baseParams);
10840             // if meta was not loaded from remote source.. try requesting it.
10841             if (!this.reader.metaFromRemote) {
10842                 p._requestMeta = 1;
10843             }
10844             if(this.sortInfo && this.remoteSort){
10845                 var pn = this.paramNames;
10846                 p[pn["sort"]] = this.sortInfo.field;
10847                 p[pn["dir"]] = this.sortInfo.direction;
10848             }
10849             if (this.multiSort) {
10850                 var pn = this.paramNames;
10851                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10852             }
10853             
10854             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10855         }
10856     },
10857
10858     /**
10859      * Reloads the Record cache from the configured Proxy using the configured Reader and
10860      * the options from the last load operation performed.
10861      * @param {Object} options (optional) An object containing properties which may override the options
10862      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10863      * the most recently used options are reused).
10864      */
10865     reload : function(options){
10866         this.load(Roo.applyIf(options||{}, this.lastOptions));
10867     },
10868
10869     // private
10870     // Called as a callback by the Reader during a load operation.
10871     loadRecords : function(o, options, success){
10872         if(!o || success === false){
10873             if(success !== false){
10874                 this.fireEvent("load", this, [], options, o);
10875             }
10876             if(options.callback){
10877                 options.callback.call(options.scope || this, [], options, false);
10878             }
10879             return;
10880         }
10881         // if data returned failure - throw an exception.
10882         if (o.success === false) {
10883             // show a message if no listener is registered.
10884             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10885                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10886             }
10887             // loadmask wil be hooked into this..
10888             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10889             return;
10890         }
10891         var r = o.records, t = o.totalRecords || r.length;
10892         
10893         this.fireEvent("beforeloadadd", this, r, options, o);
10894         
10895         if(!options || options.add !== true){
10896             if(this.pruneModifiedRecords){
10897                 this.modified = [];
10898             }
10899             for(var i = 0, len = r.length; i < len; i++){
10900                 r[i].join(this);
10901             }
10902             if(this.snapshot){
10903                 this.data = this.snapshot;
10904                 delete this.snapshot;
10905             }
10906             this.data.clear();
10907             this.data.addAll(r);
10908             this.totalLength = t;
10909             this.applySort();
10910             this.fireEvent("datachanged", this);
10911         }else{
10912             this.totalLength = Math.max(t, this.data.length+r.length);
10913             this.add(r);
10914         }
10915         this.fireEvent("load", this, r, options, o);
10916         if(options.callback){
10917             options.callback.call(options.scope || this, r, options, true);
10918         }
10919     },
10920
10921
10922     /**
10923      * Loads data from a passed data block. A Reader which understands the format of the data
10924      * must have been configured in the constructor.
10925      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10926      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10927      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10928      */
10929     loadData : function(o, append){
10930         var r = this.reader.readRecords(o);
10931         this.loadRecords(r, {add: append}, true);
10932     },
10933
10934     /**
10935      * Gets the number of cached records.
10936      * <p>
10937      * <em>If using paging, this may not be the total size of the dataset. If the data object
10938      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10939      * the data set size</em>
10940      */
10941     getCount : function(){
10942         return this.data.length || 0;
10943     },
10944
10945     /**
10946      * Gets the total number of records in the dataset as returned by the server.
10947      * <p>
10948      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10949      * the dataset size</em>
10950      */
10951     getTotalCount : function(){
10952         return this.totalLength || 0;
10953     },
10954
10955     /**
10956      * Returns the sort state of the Store as an object with two properties:
10957      * <pre><code>
10958  field {String} The name of the field by which the Records are sorted
10959  direction {String} The sort order, "ASC" or "DESC"
10960      * </code></pre>
10961      */
10962     getSortState : function(){
10963         return this.sortInfo;
10964     },
10965
10966     // private
10967     applySort : function(){
10968         if(this.sortInfo && !this.remoteSort){
10969             var s = this.sortInfo, f = s.field;
10970             var st = this.fields.get(f).sortType;
10971             var fn = function(r1, r2){
10972                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10973                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10974             };
10975             this.data.sort(s.direction, fn);
10976             if(this.snapshot && this.snapshot != this.data){
10977                 this.snapshot.sort(s.direction, fn);
10978             }
10979         }
10980     },
10981
10982     /**
10983      * Sets the default sort column and order to be used by the next load operation.
10984      * @param {String} fieldName The name of the field to sort by.
10985      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10986      */
10987     setDefaultSort : function(field, dir){
10988         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10989     },
10990
10991     /**
10992      * Sort the Records.
10993      * If remote sorting is used, the sort is performed on the server, and the cache is
10994      * reloaded. If local sorting is used, the cache is sorted internally.
10995      * @param {String} fieldName The name of the field to sort by.
10996      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10997      */
10998     sort : function(fieldName, dir){
10999         var f = this.fields.get(fieldName);
11000         if(!dir){
11001             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11002             
11003             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11004                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11005             }else{
11006                 dir = f.sortDir;
11007             }
11008         }
11009         this.sortToggle[f.name] = dir;
11010         this.sortInfo = {field: f.name, direction: dir};
11011         if(!this.remoteSort){
11012             this.applySort();
11013             this.fireEvent("datachanged", this);
11014         }else{
11015             this.load(this.lastOptions);
11016         }
11017     },
11018
11019     /**
11020      * Calls the specified function for each of the Records in the cache.
11021      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11022      * Returning <em>false</em> aborts and exits the iteration.
11023      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11024      */
11025     each : function(fn, scope){
11026         this.data.each(fn, scope);
11027     },
11028
11029     /**
11030      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11031      * (e.g., during paging).
11032      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11033      */
11034     getModifiedRecords : function(){
11035         return this.modified;
11036     },
11037
11038     // private
11039     createFilterFn : function(property, value, anyMatch){
11040         if(!value.exec){ // not a regex
11041             value = String(value);
11042             if(value.length == 0){
11043                 return false;
11044             }
11045             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11046         }
11047         return function(r){
11048             return value.test(r.data[property]);
11049         };
11050     },
11051
11052     /**
11053      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11054      * @param {String} property A field on your records
11055      * @param {Number} start The record index to start at (defaults to 0)
11056      * @param {Number} end The last record index to include (defaults to length - 1)
11057      * @return {Number} The sum
11058      */
11059     sum : function(property, start, end){
11060         var rs = this.data.items, v = 0;
11061         start = start || 0;
11062         end = (end || end === 0) ? end : rs.length-1;
11063
11064         for(var i = start; i <= end; i++){
11065             v += (rs[i].data[property] || 0);
11066         }
11067         return v;
11068     },
11069
11070     /**
11071      * Filter the records by a specified property.
11072      * @param {String} field A field on your records
11073      * @param {String/RegExp} value Either a string that the field
11074      * should start with or a RegExp to test against the field
11075      * @param {Boolean} anyMatch True to match any part not just the beginning
11076      */
11077     filter : function(property, value, anyMatch){
11078         var fn = this.createFilterFn(property, value, anyMatch);
11079         return fn ? this.filterBy(fn) : this.clearFilter();
11080     },
11081
11082     /**
11083      * Filter by a function. The specified function will be called with each
11084      * record in this data source. If the function returns true the record is included,
11085      * otherwise it is filtered.
11086      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11087      * @param {Object} scope (optional) The scope of the function (defaults to this)
11088      */
11089     filterBy : function(fn, scope){
11090         this.snapshot = this.snapshot || this.data;
11091         this.data = this.queryBy(fn, scope||this);
11092         this.fireEvent("datachanged", this);
11093     },
11094
11095     /**
11096      * Query the records by a specified property.
11097      * @param {String} field A field on your records
11098      * @param {String/RegExp} value Either a string that the field
11099      * should start with or a RegExp to test against the field
11100      * @param {Boolean} anyMatch True to match any part not just the beginning
11101      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11102      */
11103     query : function(property, value, anyMatch){
11104         var fn = this.createFilterFn(property, value, anyMatch);
11105         return fn ? this.queryBy(fn) : this.data.clone();
11106     },
11107
11108     /**
11109      * Query by a function. The specified function will be called with each
11110      * record in this data source. If the function returns true the record is included
11111      * in the results.
11112      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11113      * @param {Object} scope (optional) The scope of the function (defaults to this)
11114       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11115      **/
11116     queryBy : function(fn, scope){
11117         var data = this.snapshot || this.data;
11118         return data.filterBy(fn, scope||this);
11119     },
11120
11121     /**
11122      * Collects unique values for a particular dataIndex from this store.
11123      * @param {String} dataIndex The property to collect
11124      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11125      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11126      * @return {Array} An array of the unique values
11127      **/
11128     collect : function(dataIndex, allowNull, bypassFilter){
11129         var d = (bypassFilter === true && this.snapshot) ?
11130                 this.snapshot.items : this.data.items;
11131         var v, sv, r = [], l = {};
11132         for(var i = 0, len = d.length; i < len; i++){
11133             v = d[i].data[dataIndex];
11134             sv = String(v);
11135             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11136                 l[sv] = true;
11137                 r[r.length] = v;
11138             }
11139         }
11140         return r;
11141     },
11142
11143     /**
11144      * Revert to a view of the Record cache with no filtering applied.
11145      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11146      */
11147     clearFilter : function(suppressEvent){
11148         if(this.snapshot && this.snapshot != this.data){
11149             this.data = this.snapshot;
11150             delete this.snapshot;
11151             if(suppressEvent !== true){
11152                 this.fireEvent("datachanged", this);
11153             }
11154         }
11155     },
11156
11157     // private
11158     afterEdit : function(record){
11159         if(this.modified.indexOf(record) == -1){
11160             this.modified.push(record);
11161         }
11162         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11163     },
11164     
11165     // private
11166     afterReject : function(record){
11167         this.modified.remove(record);
11168         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11169     },
11170
11171     // private
11172     afterCommit : function(record){
11173         this.modified.remove(record);
11174         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11175     },
11176
11177     /**
11178      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11179      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11180      */
11181     commitChanges : function(){
11182         var m = this.modified.slice(0);
11183         this.modified = [];
11184         for(var i = 0, len = m.length; i < len; i++){
11185             m[i].commit();
11186         }
11187     },
11188
11189     /**
11190      * Cancel outstanding changes on all changed records.
11191      */
11192     rejectChanges : function(){
11193         var m = this.modified.slice(0);
11194         this.modified = [];
11195         for(var i = 0, len = m.length; i < len; i++){
11196             m[i].reject();
11197         }
11198     },
11199
11200     onMetaChange : function(meta, rtype, o){
11201         this.recordType = rtype;
11202         this.fields = rtype.prototype.fields;
11203         delete this.snapshot;
11204         this.sortInfo = meta.sortInfo || this.sortInfo;
11205         this.modified = [];
11206         this.fireEvent('metachange', this, this.reader.meta);
11207     },
11208     
11209     moveIndex : function(data, type)
11210     {
11211         var index = this.indexOf(data);
11212         
11213         var newIndex = index + type;
11214         
11215         this.remove(data);
11216         
11217         this.insert(newIndex, data);
11218         
11219     }
11220 });/*
11221  * Based on:
11222  * Ext JS Library 1.1.1
11223  * Copyright(c) 2006-2007, Ext JS, LLC.
11224  *
11225  * Originally Released Under LGPL - original licence link has changed is not relivant.
11226  *
11227  * Fork - LGPL
11228  * <script type="text/javascript">
11229  */
11230
11231 /**
11232  * @class Roo.data.SimpleStore
11233  * @extends Roo.data.Store
11234  * Small helper class to make creating Stores from Array data easier.
11235  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11236  * @cfg {Array} fields An array of field definition objects, or field name strings.
11237  * @cfg {Array} data The multi-dimensional array of data
11238  * @constructor
11239  * @param {Object} config
11240  */
11241 Roo.data.SimpleStore = function(config){
11242     Roo.data.SimpleStore.superclass.constructor.call(this, {
11243         isLocal : true,
11244         reader: new Roo.data.ArrayReader({
11245                 id: config.id
11246             },
11247             Roo.data.Record.create(config.fields)
11248         ),
11249         proxy : new Roo.data.MemoryProxy(config.data)
11250     });
11251     this.load();
11252 };
11253 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11254  * Based on:
11255  * Ext JS Library 1.1.1
11256  * Copyright(c) 2006-2007, Ext JS, LLC.
11257  *
11258  * Originally Released Under LGPL - original licence link has changed is not relivant.
11259  *
11260  * Fork - LGPL
11261  * <script type="text/javascript">
11262  */
11263
11264 /**
11265 /**
11266  * @extends Roo.data.Store
11267  * @class Roo.data.JsonStore
11268  * Small helper class to make creating Stores for JSON data easier. <br/>
11269 <pre><code>
11270 var store = new Roo.data.JsonStore({
11271     url: 'get-images.php',
11272     root: 'images',
11273     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11274 });
11275 </code></pre>
11276  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11277  * JsonReader and HttpProxy (unless inline data is provided).</b>
11278  * @cfg {Array} fields An array of field definition objects, or field name strings.
11279  * @constructor
11280  * @param {Object} config
11281  */
11282 Roo.data.JsonStore = function(c){
11283     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11284         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11285         reader: new Roo.data.JsonReader(c, c.fields)
11286     }));
11287 };
11288 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11289  * Based on:
11290  * Ext JS Library 1.1.1
11291  * Copyright(c) 2006-2007, Ext JS, LLC.
11292  *
11293  * Originally Released Under LGPL - original licence link has changed is not relivant.
11294  *
11295  * Fork - LGPL
11296  * <script type="text/javascript">
11297  */
11298
11299  
11300 Roo.data.Field = function(config){
11301     if(typeof config == "string"){
11302         config = {name: config};
11303     }
11304     Roo.apply(this, config);
11305     
11306     if(!this.type){
11307         this.type = "auto";
11308     }
11309     
11310     var st = Roo.data.SortTypes;
11311     // named sortTypes are supported, here we look them up
11312     if(typeof this.sortType == "string"){
11313         this.sortType = st[this.sortType];
11314     }
11315     
11316     // set default sortType for strings and dates
11317     if(!this.sortType){
11318         switch(this.type){
11319             case "string":
11320                 this.sortType = st.asUCString;
11321                 break;
11322             case "date":
11323                 this.sortType = st.asDate;
11324                 break;
11325             default:
11326                 this.sortType = st.none;
11327         }
11328     }
11329
11330     // define once
11331     var stripRe = /[\$,%]/g;
11332
11333     // prebuilt conversion function for this field, instead of
11334     // switching every time we're reading a value
11335     if(!this.convert){
11336         var cv, dateFormat = this.dateFormat;
11337         switch(this.type){
11338             case "":
11339             case "auto":
11340             case undefined:
11341                 cv = function(v){ return v; };
11342                 break;
11343             case "string":
11344                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11345                 break;
11346             case "int":
11347                 cv = function(v){
11348                     return v !== undefined && v !== null && v !== '' ?
11349                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11350                     };
11351                 break;
11352             case "float":
11353                 cv = function(v){
11354                     return v !== undefined && v !== null && v !== '' ?
11355                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11356                     };
11357                 break;
11358             case "bool":
11359             case "boolean":
11360                 cv = function(v){ return v === true || v === "true" || v == 1; };
11361                 break;
11362             case "date":
11363                 cv = function(v){
11364                     if(!v){
11365                         return '';
11366                     }
11367                     if(v instanceof Date){
11368                         return v;
11369                     }
11370                     if(dateFormat){
11371                         if(dateFormat == "timestamp"){
11372                             return new Date(v*1000);
11373                         }
11374                         return Date.parseDate(v, dateFormat);
11375                     }
11376                     var parsed = Date.parse(v);
11377                     return parsed ? new Date(parsed) : null;
11378                 };
11379              break;
11380             
11381         }
11382         this.convert = cv;
11383     }
11384 };
11385
11386 Roo.data.Field.prototype = {
11387     dateFormat: null,
11388     defaultValue: "",
11389     mapping: null,
11390     sortType : null,
11391     sortDir : "ASC"
11392 };/*
11393  * Based on:
11394  * Ext JS Library 1.1.1
11395  * Copyright(c) 2006-2007, Ext JS, LLC.
11396  *
11397  * Originally Released Under LGPL - original licence link has changed is not relivant.
11398  *
11399  * Fork - LGPL
11400  * <script type="text/javascript">
11401  */
11402  
11403 // Base class for reading structured data from a data source.  This class is intended to be
11404 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11405
11406 /**
11407  * @class Roo.data.DataReader
11408  * Base class for reading structured data from a data source.  This class is intended to be
11409  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11410  */
11411
11412 Roo.data.DataReader = function(meta, recordType){
11413     
11414     this.meta = meta;
11415     
11416     this.recordType = recordType instanceof Array ? 
11417         Roo.data.Record.create(recordType) : recordType;
11418 };
11419
11420 Roo.data.DataReader.prototype = {
11421      /**
11422      * Create an empty record
11423      * @param {Object} data (optional) - overlay some values
11424      * @return {Roo.data.Record} record created.
11425      */
11426     newRow :  function(d) {
11427         var da =  {};
11428         this.recordType.prototype.fields.each(function(c) {
11429             switch( c.type) {
11430                 case 'int' : da[c.name] = 0; break;
11431                 case 'date' : da[c.name] = new Date(); break;
11432                 case 'float' : da[c.name] = 0.0; break;
11433                 case 'boolean' : da[c.name] = false; break;
11434                 default : da[c.name] = ""; break;
11435             }
11436             
11437         });
11438         return new this.recordType(Roo.apply(da, d));
11439     }
11440     
11441 };/*
11442  * Based on:
11443  * Ext JS Library 1.1.1
11444  * Copyright(c) 2006-2007, Ext JS, LLC.
11445  *
11446  * Originally Released Under LGPL - original licence link has changed is not relivant.
11447  *
11448  * Fork - LGPL
11449  * <script type="text/javascript">
11450  */
11451
11452 /**
11453  * @class Roo.data.DataProxy
11454  * @extends Roo.data.Observable
11455  * This class is an abstract base class for implementations which provide retrieval of
11456  * unformatted data objects.<br>
11457  * <p>
11458  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11459  * (of the appropriate type which knows how to parse the data object) to provide a block of
11460  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11461  * <p>
11462  * Custom implementations must implement the load method as described in
11463  * {@link Roo.data.HttpProxy#load}.
11464  */
11465 Roo.data.DataProxy = function(){
11466     this.addEvents({
11467         /**
11468          * @event beforeload
11469          * Fires before a network request is made to retrieve a data object.
11470          * @param {Object} This DataProxy object.
11471          * @param {Object} params The params parameter to the load function.
11472          */
11473         beforeload : true,
11474         /**
11475          * @event load
11476          * Fires before the load method's callback is called.
11477          * @param {Object} This DataProxy object.
11478          * @param {Object} o The data object.
11479          * @param {Object} arg The callback argument object passed to the load function.
11480          */
11481         load : true,
11482         /**
11483          * @event loadexception
11484          * Fires if an Exception occurs during data retrieval.
11485          * @param {Object} This DataProxy object.
11486          * @param {Object} o The data object.
11487          * @param {Object} arg The callback argument object passed to the load function.
11488          * @param {Object} e The Exception.
11489          */
11490         loadexception : true
11491     });
11492     Roo.data.DataProxy.superclass.constructor.call(this);
11493 };
11494
11495 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11496
11497     /**
11498      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11499      */
11500 /*
11501  * Based on:
11502  * Ext JS Library 1.1.1
11503  * Copyright(c) 2006-2007, Ext JS, LLC.
11504  *
11505  * Originally Released Under LGPL - original licence link has changed is not relivant.
11506  *
11507  * Fork - LGPL
11508  * <script type="text/javascript">
11509  */
11510 /**
11511  * @class Roo.data.MemoryProxy
11512  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11513  * to the Reader when its load method is called.
11514  * @constructor
11515  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11516  */
11517 Roo.data.MemoryProxy = function(data){
11518     if (data.data) {
11519         data = data.data;
11520     }
11521     Roo.data.MemoryProxy.superclass.constructor.call(this);
11522     this.data = data;
11523 };
11524
11525 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11526     
11527     /**
11528      * Load data from the requested source (in this case an in-memory
11529      * data object passed to the constructor), read the data object into
11530      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11531      * process that block using the passed callback.
11532      * @param {Object} params This parameter is not used by the MemoryProxy class.
11533      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11534      * object into a block of Roo.data.Records.
11535      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11536      * The function must be passed <ul>
11537      * <li>The Record block object</li>
11538      * <li>The "arg" argument from the load function</li>
11539      * <li>A boolean success indicator</li>
11540      * </ul>
11541      * @param {Object} scope The scope in which to call the callback
11542      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11543      */
11544     load : function(params, reader, callback, scope, arg){
11545         params = params || {};
11546         var result;
11547         try {
11548             result = reader.readRecords(this.data);
11549         }catch(e){
11550             this.fireEvent("loadexception", this, arg, null, e);
11551             callback.call(scope, null, arg, false);
11552             return;
11553         }
11554         callback.call(scope, result, arg, true);
11555     },
11556     
11557     // private
11558     update : function(params, records){
11559         
11560     }
11561 });/*
11562  * Based on:
11563  * Ext JS Library 1.1.1
11564  * Copyright(c) 2006-2007, Ext JS, LLC.
11565  *
11566  * Originally Released Under LGPL - original licence link has changed is not relivant.
11567  *
11568  * Fork - LGPL
11569  * <script type="text/javascript">
11570  */
11571 /**
11572  * @class Roo.data.HttpProxy
11573  * @extends Roo.data.DataProxy
11574  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11575  * configured to reference a certain URL.<br><br>
11576  * <p>
11577  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11578  * from which the running page was served.<br><br>
11579  * <p>
11580  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11581  * <p>
11582  * Be aware that to enable the browser to parse an XML document, the server must set
11583  * the Content-Type header in the HTTP response to "text/xml".
11584  * @constructor
11585  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11586  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11587  * will be used to make the request.
11588  */
11589 Roo.data.HttpProxy = function(conn){
11590     Roo.data.HttpProxy.superclass.constructor.call(this);
11591     // is conn a conn config or a real conn?
11592     this.conn = conn;
11593     this.useAjax = !conn || !conn.events;
11594   
11595 };
11596
11597 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11598     // thse are take from connection...
11599     
11600     /**
11601      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11602      */
11603     /**
11604      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11605      * extra parameters to each request made by this object. (defaults to undefined)
11606      */
11607     /**
11608      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11609      *  to each request made by this object. (defaults to undefined)
11610      */
11611     /**
11612      * @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)
11613      */
11614     /**
11615      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11616      */
11617      /**
11618      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11619      * @type Boolean
11620      */
11621   
11622
11623     /**
11624      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11625      * @type Boolean
11626      */
11627     /**
11628      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11629      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11630      * a finer-grained basis than the DataProxy events.
11631      */
11632     getConnection : function(){
11633         return this.useAjax ? Roo.Ajax : this.conn;
11634     },
11635
11636     /**
11637      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11638      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11639      * process that block using the passed callback.
11640      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11641      * for the request to the remote server.
11642      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11643      * object into a block of Roo.data.Records.
11644      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11645      * The function must be passed <ul>
11646      * <li>The Record block object</li>
11647      * <li>The "arg" argument from the load function</li>
11648      * <li>A boolean success indicator</li>
11649      * </ul>
11650      * @param {Object} scope The scope in which to call the callback
11651      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11652      */
11653     load : function(params, reader, callback, scope, arg){
11654         if(this.fireEvent("beforeload", this, params) !== false){
11655             var  o = {
11656                 params : params || {},
11657                 request: {
11658                     callback : callback,
11659                     scope : scope,
11660                     arg : arg
11661                 },
11662                 reader: reader,
11663                 callback : this.loadResponse,
11664                 scope: this
11665             };
11666             if(this.useAjax){
11667                 Roo.applyIf(o, this.conn);
11668                 if(this.activeRequest){
11669                     Roo.Ajax.abort(this.activeRequest);
11670                 }
11671                 this.activeRequest = Roo.Ajax.request(o);
11672             }else{
11673                 this.conn.request(o);
11674             }
11675         }else{
11676             callback.call(scope||this, null, arg, false);
11677         }
11678     },
11679
11680     // private
11681     loadResponse : function(o, success, response){
11682         delete this.activeRequest;
11683         if(!success){
11684             this.fireEvent("loadexception", this, o, response);
11685             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11686             return;
11687         }
11688         var result;
11689         try {
11690             result = o.reader.read(response);
11691         }catch(e){
11692             this.fireEvent("loadexception", this, o, response, e);
11693             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11694             return;
11695         }
11696         
11697         this.fireEvent("load", this, o, o.request.arg);
11698         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11699     },
11700
11701     // private
11702     update : function(dataSet){
11703
11704     },
11705
11706     // private
11707     updateResponse : function(dataSet){
11708
11709     }
11710 });/*
11711  * Based on:
11712  * Ext JS Library 1.1.1
11713  * Copyright(c) 2006-2007, Ext JS, LLC.
11714  *
11715  * Originally Released Under LGPL - original licence link has changed is not relivant.
11716  *
11717  * Fork - LGPL
11718  * <script type="text/javascript">
11719  */
11720
11721 /**
11722  * @class Roo.data.ScriptTagProxy
11723  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11724  * other than the originating domain of the running page.<br><br>
11725  * <p>
11726  * <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
11727  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11728  * <p>
11729  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11730  * source code that is used as the source inside a &lt;script> tag.<br><br>
11731  * <p>
11732  * In order for the browser to process the returned data, the server must wrap the data object
11733  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11734  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11735  * depending on whether the callback name was passed:
11736  * <p>
11737  * <pre><code>
11738 boolean scriptTag = false;
11739 String cb = request.getParameter("callback");
11740 if (cb != null) {
11741     scriptTag = true;
11742     response.setContentType("text/javascript");
11743 } else {
11744     response.setContentType("application/x-json");
11745 }
11746 Writer out = response.getWriter();
11747 if (scriptTag) {
11748     out.write(cb + "(");
11749 }
11750 out.print(dataBlock.toJsonString());
11751 if (scriptTag) {
11752     out.write(");");
11753 }
11754 </pre></code>
11755  *
11756  * @constructor
11757  * @param {Object} config A configuration object.
11758  */
11759 Roo.data.ScriptTagProxy = function(config){
11760     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11761     Roo.apply(this, config);
11762     this.head = document.getElementsByTagName("head")[0];
11763 };
11764
11765 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11766
11767 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11768     /**
11769      * @cfg {String} url The URL from which to request the data object.
11770      */
11771     /**
11772      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11773      */
11774     timeout : 30000,
11775     /**
11776      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11777      * the server the name of the callback function set up by the load call to process the returned data object.
11778      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11779      * javascript output which calls this named function passing the data object as its only parameter.
11780      */
11781     callbackParam : "callback",
11782     /**
11783      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11784      * name to the request.
11785      */
11786     nocache : true,
11787
11788     /**
11789      * Load data from the configured URL, read the data object into
11790      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11791      * process that block using the passed callback.
11792      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11793      * for the request to the remote server.
11794      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11795      * object into a block of Roo.data.Records.
11796      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11797      * The function must be passed <ul>
11798      * <li>The Record block object</li>
11799      * <li>The "arg" argument from the load function</li>
11800      * <li>A boolean success indicator</li>
11801      * </ul>
11802      * @param {Object} scope The scope in which to call the callback
11803      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11804      */
11805     load : function(params, reader, callback, scope, arg){
11806         if(this.fireEvent("beforeload", this, params) !== false){
11807
11808             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11809
11810             var url = this.url;
11811             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11812             if(this.nocache){
11813                 url += "&_dc=" + (new Date().getTime());
11814             }
11815             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11816             var trans = {
11817                 id : transId,
11818                 cb : "stcCallback"+transId,
11819                 scriptId : "stcScript"+transId,
11820                 params : params,
11821                 arg : arg,
11822                 url : url,
11823                 callback : callback,
11824                 scope : scope,
11825                 reader : reader
11826             };
11827             var conn = this;
11828
11829             window[trans.cb] = function(o){
11830                 conn.handleResponse(o, trans);
11831             };
11832
11833             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11834
11835             if(this.autoAbort !== false){
11836                 this.abort();
11837             }
11838
11839             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11840
11841             var script = document.createElement("script");
11842             script.setAttribute("src", url);
11843             script.setAttribute("type", "text/javascript");
11844             script.setAttribute("id", trans.scriptId);
11845             this.head.appendChild(script);
11846
11847             this.trans = trans;
11848         }else{
11849             callback.call(scope||this, null, arg, false);
11850         }
11851     },
11852
11853     // private
11854     isLoading : function(){
11855         return this.trans ? true : false;
11856     },
11857
11858     /**
11859      * Abort the current server request.
11860      */
11861     abort : function(){
11862         if(this.isLoading()){
11863             this.destroyTrans(this.trans);
11864         }
11865     },
11866
11867     // private
11868     destroyTrans : function(trans, isLoaded){
11869         this.head.removeChild(document.getElementById(trans.scriptId));
11870         clearTimeout(trans.timeoutId);
11871         if(isLoaded){
11872             window[trans.cb] = undefined;
11873             try{
11874                 delete window[trans.cb];
11875             }catch(e){}
11876         }else{
11877             // if hasn't been loaded, wait for load to remove it to prevent script error
11878             window[trans.cb] = function(){
11879                 window[trans.cb] = undefined;
11880                 try{
11881                     delete window[trans.cb];
11882                 }catch(e){}
11883             };
11884         }
11885     },
11886
11887     // private
11888     handleResponse : function(o, trans){
11889         this.trans = false;
11890         this.destroyTrans(trans, true);
11891         var result;
11892         try {
11893             result = trans.reader.readRecords(o);
11894         }catch(e){
11895             this.fireEvent("loadexception", this, o, trans.arg, e);
11896             trans.callback.call(trans.scope||window, null, trans.arg, false);
11897             return;
11898         }
11899         this.fireEvent("load", this, o, trans.arg);
11900         trans.callback.call(trans.scope||window, result, trans.arg, true);
11901     },
11902
11903     // private
11904     handleFailure : function(trans){
11905         this.trans = false;
11906         this.destroyTrans(trans, false);
11907         this.fireEvent("loadexception", this, null, trans.arg);
11908         trans.callback.call(trans.scope||window, null, trans.arg, false);
11909     }
11910 });/*
11911  * Based on:
11912  * Ext JS Library 1.1.1
11913  * Copyright(c) 2006-2007, Ext JS, LLC.
11914  *
11915  * Originally Released Under LGPL - original licence link has changed is not relivant.
11916  *
11917  * Fork - LGPL
11918  * <script type="text/javascript">
11919  */
11920
11921 /**
11922  * @class Roo.data.JsonReader
11923  * @extends Roo.data.DataReader
11924  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11925  * based on mappings in a provided Roo.data.Record constructor.
11926  * 
11927  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11928  * in the reply previously. 
11929  * 
11930  * <p>
11931  * Example code:
11932  * <pre><code>
11933 var RecordDef = Roo.data.Record.create([
11934     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11935     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11936 ]);
11937 var myReader = new Roo.data.JsonReader({
11938     totalProperty: "results",    // The property which contains the total dataset size (optional)
11939     root: "rows",                // The property which contains an Array of row objects
11940     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11941 }, RecordDef);
11942 </code></pre>
11943  * <p>
11944  * This would consume a JSON file like this:
11945  * <pre><code>
11946 { 'results': 2, 'rows': [
11947     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11948     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11949 }
11950 </code></pre>
11951  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11952  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11953  * paged from the remote server.
11954  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11955  * @cfg {String} root name of the property which contains the Array of row objects.
11956  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11957  * @cfg {Array} fields Array of field definition objects
11958  * @constructor
11959  * Create a new JsonReader
11960  * @param {Object} meta Metadata configuration options
11961  * @param {Object} recordType Either an Array of field definition objects,
11962  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11963  */
11964 Roo.data.JsonReader = function(meta, recordType){
11965     
11966     meta = meta || {};
11967     // set some defaults:
11968     Roo.applyIf(meta, {
11969         totalProperty: 'total',
11970         successProperty : 'success',
11971         root : 'data',
11972         id : 'id'
11973     });
11974     
11975     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11976 };
11977 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11978     
11979     /**
11980      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11981      * Used by Store query builder to append _requestMeta to params.
11982      * 
11983      */
11984     metaFromRemote : false,
11985     /**
11986      * This method is only used by a DataProxy which has retrieved data from a remote server.
11987      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11988      * @return {Object} data A data block which is used by an Roo.data.Store object as
11989      * a cache of Roo.data.Records.
11990      */
11991     read : function(response){
11992         var json = response.responseText;
11993        
11994         var o = /* eval:var:o */ eval("("+json+")");
11995         if(!o) {
11996             throw {message: "JsonReader.read: Json object not found"};
11997         }
11998         
11999         if(o.metaData){
12000             
12001             delete this.ef;
12002             this.metaFromRemote = true;
12003             this.meta = o.metaData;
12004             this.recordType = Roo.data.Record.create(o.metaData.fields);
12005             this.onMetaChange(this.meta, this.recordType, o);
12006         }
12007         return this.readRecords(o);
12008     },
12009
12010     // private function a store will implement
12011     onMetaChange : function(meta, recordType, o){
12012
12013     },
12014
12015     /**
12016          * @ignore
12017          */
12018     simpleAccess: function(obj, subsc) {
12019         return obj[subsc];
12020     },
12021
12022         /**
12023          * @ignore
12024          */
12025     getJsonAccessor: function(){
12026         var re = /[\[\.]/;
12027         return function(expr) {
12028             try {
12029                 return(re.test(expr))
12030                     ? new Function("obj", "return obj." + expr)
12031                     : function(obj){
12032                         return obj[expr];
12033                     };
12034             } catch(e){}
12035             return Roo.emptyFn;
12036         };
12037     }(),
12038
12039     /**
12040      * Create a data block containing Roo.data.Records from an XML document.
12041      * @param {Object} o An object which contains an Array of row objects in the property specified
12042      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12043      * which contains the total size of the dataset.
12044      * @return {Object} data A data block which is used by an Roo.data.Store object as
12045      * a cache of Roo.data.Records.
12046      */
12047     readRecords : function(o){
12048         /**
12049          * After any data loads, the raw JSON data is available for further custom processing.
12050          * @type Object
12051          */
12052         this.o = o;
12053         var s = this.meta, Record = this.recordType,
12054             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12055
12056 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12057         if (!this.ef) {
12058             if(s.totalProperty) {
12059                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12060                 }
12061                 if(s.successProperty) {
12062                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12063                 }
12064                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12065                 if (s.id) {
12066                         var g = this.getJsonAccessor(s.id);
12067                         this.getId = function(rec) {
12068                                 var r = g(rec);  
12069                                 return (r === undefined || r === "") ? null : r;
12070                         };
12071                 } else {
12072                         this.getId = function(){return null;};
12073                 }
12074             this.ef = [];
12075             for(var jj = 0; jj < fl; jj++){
12076                 f = fi[jj];
12077                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12078                 this.ef[jj] = this.getJsonAccessor(map);
12079             }
12080         }
12081
12082         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12083         if(s.totalProperty){
12084             var vt = parseInt(this.getTotal(o), 10);
12085             if(!isNaN(vt)){
12086                 totalRecords = vt;
12087             }
12088         }
12089         if(s.successProperty){
12090             var vs = this.getSuccess(o);
12091             if(vs === false || vs === 'false'){
12092                 success = false;
12093             }
12094         }
12095         var records = [];
12096         for(var i = 0; i < c; i++){
12097                 var n = root[i];
12098             var values = {};
12099             var id = this.getId(n);
12100             for(var j = 0; j < fl; j++){
12101                 f = fi[j];
12102             var v = this.ef[j](n);
12103             if (!f.convert) {
12104                 Roo.log('missing convert for ' + f.name);
12105                 Roo.log(f);
12106                 continue;
12107             }
12108             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12109             }
12110             var record = new Record(values, id);
12111             record.json = n;
12112             records[i] = record;
12113         }
12114         return {
12115             raw : o,
12116             success : success,
12117             records : records,
12118             totalRecords : totalRecords
12119         };
12120     }
12121 });/*
12122  * Based on:
12123  * Ext JS Library 1.1.1
12124  * Copyright(c) 2006-2007, Ext JS, LLC.
12125  *
12126  * Originally Released Under LGPL - original licence link has changed is not relivant.
12127  *
12128  * Fork - LGPL
12129  * <script type="text/javascript">
12130  */
12131
12132 /**
12133  * @class Roo.data.ArrayReader
12134  * @extends Roo.data.DataReader
12135  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12136  * Each element of that Array represents a row of data fields. The
12137  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12138  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12139  * <p>
12140  * Example code:.
12141  * <pre><code>
12142 var RecordDef = Roo.data.Record.create([
12143     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12144     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12145 ]);
12146 var myReader = new Roo.data.ArrayReader({
12147     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12148 }, RecordDef);
12149 </code></pre>
12150  * <p>
12151  * This would consume an Array like this:
12152  * <pre><code>
12153 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12154   </code></pre>
12155  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12156  * @constructor
12157  * Create a new JsonReader
12158  * @param {Object} meta Metadata configuration options.
12159  * @param {Object} recordType Either an Array of field definition objects
12160  * as specified to {@link Roo.data.Record#create},
12161  * or an {@link Roo.data.Record} object
12162  * created using {@link Roo.data.Record#create}.
12163  */
12164 Roo.data.ArrayReader = function(meta, recordType){
12165     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12166 };
12167
12168 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12169     /**
12170      * Create a data block containing Roo.data.Records from an XML document.
12171      * @param {Object} o An Array of row objects which represents the dataset.
12172      * @return {Object} data A data block which is used by an Roo.data.Store object as
12173      * a cache of Roo.data.Records.
12174      */
12175     readRecords : function(o){
12176         var sid = this.meta ? this.meta.id : null;
12177         var recordType = this.recordType, fields = recordType.prototype.fields;
12178         var records = [];
12179         var root = o;
12180             for(var i = 0; i < root.length; i++){
12181                     var n = root[i];
12182                 var values = {};
12183                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12184                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12185                 var f = fields.items[j];
12186                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12187                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12188                 v = f.convert(v);
12189                 values[f.name] = v;
12190             }
12191                 var record = new recordType(values, id);
12192                 record.json = n;
12193                 records[records.length] = record;
12194             }
12195             return {
12196                 records : records,
12197                 totalRecords : records.length
12198             };
12199     }
12200 });/*
12201  * - LGPL
12202  * * 
12203  */
12204
12205 /**
12206  * @class Roo.bootstrap.ComboBox
12207  * @extends Roo.bootstrap.TriggerField
12208  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12209  * @cfg {Boolean} append (true|false) default false
12210  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12211  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12212  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12213  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12214  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12215  * @cfg {Boolean} animate default true
12216  * @cfg {Boolean} emptyResultText only for touch device
12217  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12218  * @constructor
12219  * Create a new ComboBox.
12220  * @param {Object} config Configuration options
12221  */
12222 Roo.bootstrap.ComboBox = function(config){
12223     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12224     this.addEvents({
12225         /**
12226          * @event expand
12227          * Fires when the dropdown list is expanded
12228              * @param {Roo.bootstrap.ComboBox} combo This combo box
12229              */
12230         'expand' : true,
12231         /**
12232          * @event collapse
12233          * Fires when the dropdown list is collapsed
12234              * @param {Roo.bootstrap.ComboBox} combo This combo box
12235              */
12236         'collapse' : true,
12237         /**
12238          * @event beforeselect
12239          * Fires before a list item is selected. Return false to cancel the selection.
12240              * @param {Roo.bootstrap.ComboBox} combo This combo box
12241              * @param {Roo.data.Record} record The data record returned from the underlying store
12242              * @param {Number} index The index of the selected item in the dropdown list
12243              */
12244         'beforeselect' : true,
12245         /**
12246          * @event select
12247          * Fires when a list item is selected
12248              * @param {Roo.bootstrap.ComboBox} combo This combo box
12249              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12250              * @param {Number} index The index of the selected item in the dropdown list
12251              */
12252         'select' : true,
12253         /**
12254          * @event beforequery
12255          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12256          * The event object passed has these properties:
12257              * @param {Roo.bootstrap.ComboBox} combo This combo box
12258              * @param {String} query The query
12259              * @param {Boolean} forceAll true to force "all" query
12260              * @param {Boolean} cancel true to cancel the query
12261              * @param {Object} e The query event object
12262              */
12263         'beforequery': true,
12264          /**
12265          * @event add
12266          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12267              * @param {Roo.bootstrap.ComboBox} combo This combo box
12268              */
12269         'add' : true,
12270         /**
12271          * @event edit
12272          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12273              * @param {Roo.bootstrap.ComboBox} combo This combo box
12274              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12275              */
12276         'edit' : true,
12277         /**
12278          * @event remove
12279          * Fires when the remove value from the combobox array
12280              * @param {Roo.bootstrap.ComboBox} combo This combo box
12281              */
12282         'remove' : true,
12283         /**
12284          * @event afterremove
12285          * Fires when the remove value from the combobox array
12286              * @param {Roo.bootstrap.ComboBox} combo This combo box
12287              */
12288         'afterremove' : true,
12289         /**
12290          * @event specialfilter
12291          * Fires when specialfilter
12292             * @param {Roo.bootstrap.ComboBox} combo This combo box
12293             */
12294         'specialfilter' : true,
12295         /**
12296          * @event tick
12297          * Fires when tick the element
12298             * @param {Roo.bootstrap.ComboBox} combo This combo box
12299             */
12300         'tick' : true,
12301         /**
12302          * @event touchviewdisplay
12303          * Fires when touch view require special display (default is using displayField)
12304             * @param {Roo.bootstrap.ComboBox} combo This combo box
12305             * @param {Object} cfg set html .
12306             */
12307         'touchviewdisplay' : true
12308         
12309     });
12310     
12311     this.item = [];
12312     this.tickItems = [];
12313     
12314     this.selectedIndex = -1;
12315     if(this.mode == 'local'){
12316         if(config.queryDelay === undefined){
12317             this.queryDelay = 10;
12318         }
12319         if(config.minChars === undefined){
12320             this.minChars = 0;
12321         }
12322     }
12323 };
12324
12325 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12326      
12327     /**
12328      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12329      * rendering into an Roo.Editor, defaults to false)
12330      */
12331     /**
12332      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12333      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12334      */
12335     /**
12336      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12337      */
12338     /**
12339      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12340      * the dropdown list (defaults to undefined, with no header element)
12341      */
12342
12343      /**
12344      * @cfg {String/Roo.Template} tpl The template to use to render the output
12345      */
12346      
12347      /**
12348      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12349      */
12350     listWidth: undefined,
12351     /**
12352      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12353      * mode = 'remote' or 'text' if mode = 'local')
12354      */
12355     displayField: undefined,
12356     
12357     /**
12358      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12359      * mode = 'remote' or 'value' if mode = 'local'). 
12360      * Note: use of a valueField requires the user make a selection
12361      * in order for a value to be mapped.
12362      */
12363     valueField: undefined,
12364     /**
12365      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12366      */
12367     modalTitle : '',
12368     
12369     /**
12370      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12371      * field's data value (defaults to the underlying DOM element's name)
12372      */
12373     hiddenName: undefined,
12374     /**
12375      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12376      */
12377     listClass: '',
12378     /**
12379      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12380      */
12381     selectedClass: 'active',
12382     
12383     /**
12384      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12385      */
12386     shadow:'sides',
12387     /**
12388      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12389      * anchor positions (defaults to 'tl-bl')
12390      */
12391     listAlign: 'tl-bl?',
12392     /**
12393      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12394      */
12395     maxHeight: 300,
12396     /**
12397      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12398      * query specified by the allQuery config option (defaults to 'query')
12399      */
12400     triggerAction: 'query',
12401     /**
12402      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12403      * (defaults to 4, does not apply if editable = false)
12404      */
12405     minChars : 4,
12406     /**
12407      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12408      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12409      */
12410     typeAhead: false,
12411     /**
12412      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12413      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12414      */
12415     queryDelay: 500,
12416     /**
12417      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12418      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12419      */
12420     pageSize: 0,
12421     /**
12422      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12423      * when editable = true (defaults to false)
12424      */
12425     selectOnFocus:false,
12426     /**
12427      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12428      */
12429     queryParam: 'query',
12430     /**
12431      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12432      * when mode = 'remote' (defaults to 'Loading...')
12433      */
12434     loadingText: 'Loading...',
12435     /**
12436      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12437      */
12438     resizable: false,
12439     /**
12440      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12441      */
12442     handleHeight : 8,
12443     /**
12444      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12445      * traditional select (defaults to true)
12446      */
12447     editable: true,
12448     /**
12449      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12450      */
12451     allQuery: '',
12452     /**
12453      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12454      */
12455     mode: 'remote',
12456     /**
12457      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12458      * listWidth has a higher value)
12459      */
12460     minListWidth : 70,
12461     /**
12462      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12463      * allow the user to set arbitrary text into the field (defaults to false)
12464      */
12465     forceSelection:false,
12466     /**
12467      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12468      * if typeAhead = true (defaults to 250)
12469      */
12470     typeAheadDelay : 250,
12471     /**
12472      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12473      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12474      */
12475     valueNotFoundText : undefined,
12476     /**
12477      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12478      */
12479     blockFocus : false,
12480     
12481     /**
12482      * @cfg {Boolean} disableClear Disable showing of clear button.
12483      */
12484     disableClear : false,
12485     /**
12486      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12487      */
12488     alwaysQuery : false,
12489     
12490     /**
12491      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12492      */
12493     multiple : false,
12494     
12495     /**
12496      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12497      */
12498     invalidClass : "has-warning",
12499     
12500     /**
12501      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12502      */
12503     validClass : "has-success",
12504     
12505     /**
12506      * @cfg {Boolean} specialFilter (true|false) special filter default false
12507      */
12508     specialFilter : false,
12509     
12510     /**
12511      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12512      */
12513     mobileTouchView : true,
12514     
12515     /**
12516      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12517      */
12518     useNativeIOS : false,
12519     
12520     ios_options : false,
12521     
12522     //private
12523     addicon : false,
12524     editicon: false,
12525     
12526     page: 0,
12527     hasQuery: false,
12528     append: false,
12529     loadNext: false,
12530     autoFocus : true,
12531     tickable : false,
12532     btnPosition : 'right',
12533     triggerList : true,
12534     showToggleBtn : true,
12535     animate : true,
12536     emptyResultText: 'Empty',
12537     triggerText : 'Select',
12538     
12539     // element that contains real text value.. (when hidden is used..)
12540     
12541     getAutoCreate : function()
12542     {
12543         var cfg = false;
12544         
12545         /*
12546          * Render classic select for iso
12547          */
12548         
12549         if(Roo.isIOS && this.useNativeIOS){
12550             cfg = this.getAutoCreateNativeIOS();
12551             return cfg;
12552         }
12553         
12554         /*
12555          * Touch Devices
12556          */
12557         
12558         if(Roo.isTouch && this.mobileTouchView){
12559             cfg = this.getAutoCreateTouchView();
12560             return cfg;;
12561         }
12562         
12563         /*
12564          *  Normal ComboBox
12565          */
12566         if(!this.tickable){
12567             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12568             return cfg;
12569         }
12570         
12571         /*
12572          *  ComboBox with tickable selections
12573          */
12574              
12575         var align = this.labelAlign || this.parentLabelAlign();
12576         
12577         cfg = {
12578             cls : 'form-group roo-combobox-tickable' //input-group
12579         };
12580         
12581         var btn_text_select = '';
12582         var btn_text_done = '';
12583         var btn_text_cancel = '';
12584         
12585         if (this.btn_text_show) {
12586             btn_text_select = 'Select';
12587             btn_text_done = 'Done';
12588             btn_text_cancel = 'Cancel'; 
12589         }
12590         
12591         var buttons = {
12592             tag : 'div',
12593             cls : 'tickable-buttons',
12594             cn : [
12595                 {
12596                     tag : 'button',
12597                     type : 'button',
12598                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12599                     //html : this.triggerText
12600                     html: btn_text_select
12601                 },
12602                 {
12603                     tag : 'button',
12604                     type : 'button',
12605                     name : 'ok',
12606                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12607                     //html : 'Done'
12608                     html: btn_text_done
12609                 },
12610                 {
12611                     tag : 'button',
12612                     type : 'button',
12613                     name : 'cancel',
12614                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12615                     //html : 'Cancel'
12616                     html: btn_text_cancel
12617                 }
12618             ]
12619         };
12620         
12621         if(this.editable){
12622             buttons.cn.unshift({
12623                 tag: 'input',
12624                 cls: 'roo-select2-search-field-input'
12625             });
12626         }
12627         
12628         var _this = this;
12629         
12630         Roo.each(buttons.cn, function(c){
12631             if (_this.size) {
12632                 c.cls += ' btn-' + _this.size;
12633             }
12634
12635             if (_this.disabled) {
12636                 c.disabled = true;
12637             }
12638         });
12639         
12640         var box = {
12641             tag: 'div',
12642             cn: [
12643                 {
12644                     tag: 'input',
12645                     type : 'hidden',
12646                     cls: 'form-hidden-field'
12647                 },
12648                 {
12649                     tag: 'ul',
12650                     cls: 'roo-select2-choices',
12651                     cn:[
12652                         {
12653                             tag: 'li',
12654                             cls: 'roo-select2-search-field',
12655                             cn: [
12656
12657                                 buttons
12658                             ]
12659                         }
12660                     ]
12661                 }
12662             ]
12663         };
12664         
12665         var combobox = {
12666             cls: 'roo-select2-container input-group roo-select2-container-multi',
12667             cn: [
12668                 box
12669 //                {
12670 //                    tag: 'ul',
12671 //                    cls: 'typeahead typeahead-long dropdown-menu',
12672 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12673 //                }
12674             ]
12675         };
12676         
12677         if(this.hasFeedback && !this.allowBlank){
12678             
12679             var feedback = {
12680                 tag: 'span',
12681                 cls: 'glyphicon form-control-feedback'
12682             };
12683
12684             combobox.cn.push(feedback);
12685         }
12686         
12687         if (align ==='left' && this.fieldLabel.length) {
12688             
12689             cfg.cn = [
12690                 {
12691                     tag : 'i',
12692                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12693                     tooltip : 'This field is required'
12694                 },
12695                 {
12696                     tag: 'label',
12697                     'for' :  id,
12698                     cls : 'control-label',
12699                     html : this.fieldLabel
12700
12701                 },
12702                 {
12703                     cls : "", 
12704                     cn: [
12705                         combobox
12706                     ]
12707                 }
12708
12709             ];
12710             
12711             var labelCfg = cfg.cn[1];
12712             var contentCfg = cfg.cn[2];
12713             
12714
12715             if(this.indicatorpos == 'right'){
12716                 
12717                 cfg.cn = [
12718                     {
12719                         tag: 'label',
12720                         'for' :  id,
12721                         cls : 'control-label',
12722                         html : this.fieldLabel
12723
12724                     },
12725                     {
12726                         tag : 'i',
12727                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12728                         tooltip : 'This field is required'
12729                     },
12730                     {
12731                         cls : "",
12732                         cn: [
12733                             combobox
12734                         ]
12735                     }
12736
12737                 ];
12738                 
12739                 labelCfg = cfg.cn[0];
12740             
12741             }
12742             
12743             if(this.labelWidth > 12){
12744                 labelCfg.style = "width: " + this.labelWidth + 'px';
12745             }
12746             
12747             if(this.labelWidth < 13 && this.labelmd == 0){
12748                 this.labelmd = this.labelWidth;
12749             }
12750             
12751             if(this.labellg > 0){
12752                 labelCfg.cls += ' col-lg-' + this.labellg;
12753                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12754             }
12755             
12756             if(this.labelmd > 0){
12757                 labelCfg.cls += ' col-md-' + this.labelmd;
12758                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12759             }
12760             
12761             if(this.labelsm > 0){
12762                 labelCfg.cls += ' col-sm-' + this.labelsm;
12763                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12764             }
12765             
12766             if(this.labelxs > 0){
12767                 labelCfg.cls += ' col-xs-' + this.labelxs;
12768                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12769             }
12770                 
12771                 
12772         } else if ( this.fieldLabel.length) {
12773 //                Roo.log(" label");
12774                  cfg.cn = [
12775                     {
12776                         tag : 'i',
12777                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12778                         tooltip : 'This field is required'
12779                     },
12780                     {
12781                         tag: 'label',
12782                         //cls : 'input-group-addon',
12783                         html : this.fieldLabel
12784                         
12785                     },
12786                     
12787                     combobox
12788                     
12789                 ];
12790                 
12791                 if(this.indicatorpos == 'right'){
12792                     
12793                     cfg.cn = [
12794                         {
12795                             tag: 'label',
12796                             //cls : 'input-group-addon',
12797                             html : this.fieldLabel
12798
12799                         },
12800                         
12801                         {
12802                             tag : 'i',
12803                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12804                             tooltip : 'This field is required'
12805                         },
12806                         
12807                         combobox
12808
12809                     ];
12810                 
12811                 }
12812
12813         } else {
12814             
12815 //                Roo.log(" no label && no align");
12816                 cfg = combobox
12817                      
12818                 
12819         }
12820          
12821         var settings=this;
12822         ['xs','sm','md','lg'].map(function(size){
12823             if (settings[size]) {
12824                 cfg.cls += ' col-' + size + '-' + settings[size];
12825             }
12826         });
12827         
12828         return cfg;
12829         
12830     },
12831     
12832     _initEventsCalled : false,
12833     
12834     // private
12835     initEvents: function()
12836     {   
12837         if (this._initEventsCalled) { // as we call render... prevent looping...
12838             return;
12839         }
12840         this._initEventsCalled = true;
12841         
12842         if (!this.store) {
12843             throw "can not find store for combo";
12844         }
12845         
12846         this.store = Roo.factory(this.store, Roo.data);
12847         
12848         // if we are building from html. then this element is so complex, that we can not really
12849         // use the rendered HTML.
12850         // so we have to trash and replace the previous code.
12851         if (Roo.XComponent.build_from_html) {
12852             
12853             // remove this element....
12854             var e = this.el.dom, k=0;
12855             while (e ) { e = e.previousSibling;  ++k;}
12856
12857             this.el.remove();
12858             
12859             this.el=false;
12860             this.rendered = false;
12861             
12862             this.render(this.parent().getChildContainer(true), k);
12863             
12864             
12865             
12866         }
12867         
12868         if(Roo.isIOS && this.useNativeIOS){
12869             this.initIOSView();
12870             return;
12871         }
12872         
12873         /*
12874          * Touch Devices
12875          */
12876         
12877         if(Roo.isTouch && this.mobileTouchView){
12878             this.initTouchView();
12879             return;
12880         }
12881         
12882         if(this.tickable){
12883             this.initTickableEvents();
12884             return;
12885         }
12886         
12887         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12888         
12889         if(this.hiddenName){
12890             
12891             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12892             
12893             this.hiddenField.dom.value =
12894                 this.hiddenValue !== undefined ? this.hiddenValue :
12895                 this.value !== undefined ? this.value : '';
12896
12897             // prevent input submission
12898             this.el.dom.removeAttribute('name');
12899             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12900              
12901              
12902         }
12903         //if(Roo.isGecko){
12904         //    this.el.dom.setAttribute('autocomplete', 'off');
12905         //}
12906         
12907         var cls = 'x-combo-list';
12908         
12909         //this.list = new Roo.Layer({
12910         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12911         //});
12912         
12913         var _this = this;
12914         
12915         (function(){
12916             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12917             _this.list.setWidth(lw);
12918         }).defer(100);
12919         
12920         this.list.on('mouseover', this.onViewOver, this);
12921         this.list.on('mousemove', this.onViewMove, this);
12922         
12923         this.list.on('scroll', this.onViewScroll, this);
12924         
12925         /*
12926         this.list.swallowEvent('mousewheel');
12927         this.assetHeight = 0;
12928
12929         if(this.title){
12930             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12931             this.assetHeight += this.header.getHeight();
12932         }
12933
12934         this.innerList = this.list.createChild({cls:cls+'-inner'});
12935         this.innerList.on('mouseover', this.onViewOver, this);
12936         this.innerList.on('mousemove', this.onViewMove, this);
12937         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12938         
12939         if(this.allowBlank && !this.pageSize && !this.disableClear){
12940             this.footer = this.list.createChild({cls:cls+'-ft'});
12941             this.pageTb = new Roo.Toolbar(this.footer);
12942            
12943         }
12944         if(this.pageSize){
12945             this.footer = this.list.createChild({cls:cls+'-ft'});
12946             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12947                     {pageSize: this.pageSize});
12948             
12949         }
12950         
12951         if (this.pageTb && this.allowBlank && !this.disableClear) {
12952             var _this = this;
12953             this.pageTb.add(new Roo.Toolbar.Fill(), {
12954                 cls: 'x-btn-icon x-btn-clear',
12955                 text: '&#160;',
12956                 handler: function()
12957                 {
12958                     _this.collapse();
12959                     _this.clearValue();
12960                     _this.onSelect(false, -1);
12961                 }
12962             });
12963         }
12964         if (this.footer) {
12965             this.assetHeight += this.footer.getHeight();
12966         }
12967         */
12968             
12969         if(!this.tpl){
12970             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12971         }
12972
12973         this.view = new Roo.View(this.list, this.tpl, {
12974             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12975         });
12976         //this.view.wrapEl.setDisplayed(false);
12977         this.view.on('click', this.onViewClick, this);
12978         
12979         
12980         
12981         this.store.on('beforeload', this.onBeforeLoad, this);
12982         this.store.on('load', this.onLoad, this);
12983         this.store.on('loadexception', this.onLoadException, this);
12984         /*
12985         if(this.resizable){
12986             this.resizer = new Roo.Resizable(this.list,  {
12987                pinned:true, handles:'se'
12988             });
12989             this.resizer.on('resize', function(r, w, h){
12990                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12991                 this.listWidth = w;
12992                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12993                 this.restrictHeight();
12994             }, this);
12995             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12996         }
12997         */
12998         if(!this.editable){
12999             this.editable = true;
13000             this.setEditable(false);
13001         }
13002         
13003         /*
13004         
13005         if (typeof(this.events.add.listeners) != 'undefined') {
13006             
13007             this.addicon = this.wrap.createChild(
13008                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13009        
13010             this.addicon.on('click', function(e) {
13011                 this.fireEvent('add', this);
13012             }, this);
13013         }
13014         if (typeof(this.events.edit.listeners) != 'undefined') {
13015             
13016             this.editicon = this.wrap.createChild(
13017                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13018             if (this.addicon) {
13019                 this.editicon.setStyle('margin-left', '40px');
13020             }
13021             this.editicon.on('click', function(e) {
13022                 
13023                 // we fire even  if inothing is selected..
13024                 this.fireEvent('edit', this, this.lastData );
13025                 
13026             }, this);
13027         }
13028         */
13029         
13030         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13031             "up" : function(e){
13032                 this.inKeyMode = true;
13033                 this.selectPrev();
13034             },
13035
13036             "down" : function(e){
13037                 if(!this.isExpanded()){
13038                     this.onTriggerClick();
13039                 }else{
13040                     this.inKeyMode = true;
13041                     this.selectNext();
13042                 }
13043             },
13044
13045             "enter" : function(e){
13046 //                this.onViewClick();
13047                 //return true;
13048                 this.collapse();
13049                 
13050                 if(this.fireEvent("specialkey", this, e)){
13051                     this.onViewClick(false);
13052                 }
13053                 
13054                 return true;
13055             },
13056
13057             "esc" : function(e){
13058                 this.collapse();
13059             },
13060
13061             "tab" : function(e){
13062                 this.collapse();
13063                 
13064                 if(this.fireEvent("specialkey", this, e)){
13065                     this.onViewClick(false);
13066                 }
13067                 
13068                 return true;
13069             },
13070
13071             scope : this,
13072
13073             doRelay : function(foo, bar, hname){
13074                 if(hname == 'down' || this.scope.isExpanded()){
13075                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13076                 }
13077                 return true;
13078             },
13079
13080             forceKeyDown: true
13081         });
13082         
13083         
13084         this.queryDelay = Math.max(this.queryDelay || 10,
13085                 this.mode == 'local' ? 10 : 250);
13086         
13087         
13088         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13089         
13090         if(this.typeAhead){
13091             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13092         }
13093         if(this.editable !== false){
13094             this.inputEl().on("keyup", this.onKeyUp, this);
13095         }
13096         if(this.forceSelection){
13097             this.inputEl().on('blur', this.doForce, this);
13098         }
13099         
13100         if(this.multiple){
13101             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13102             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13103         }
13104     },
13105     
13106     initTickableEvents: function()
13107     {   
13108         this.createList();
13109         
13110         if(this.hiddenName){
13111             
13112             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13113             
13114             this.hiddenField.dom.value =
13115                 this.hiddenValue !== undefined ? this.hiddenValue :
13116                 this.value !== undefined ? this.value : '';
13117
13118             // prevent input submission
13119             this.el.dom.removeAttribute('name');
13120             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13121              
13122              
13123         }
13124         
13125 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13126         
13127         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13128         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13129         if(this.triggerList){
13130             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13131         }
13132          
13133         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13134         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13135         
13136         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13137         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13138         
13139         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13140         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13141         
13142         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13143         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13144         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13145         
13146         this.okBtn.hide();
13147         this.cancelBtn.hide();
13148         
13149         var _this = this;
13150         
13151         (function(){
13152             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13153             _this.list.setWidth(lw);
13154         }).defer(100);
13155         
13156         this.list.on('mouseover', this.onViewOver, this);
13157         this.list.on('mousemove', this.onViewMove, this);
13158         
13159         this.list.on('scroll', this.onViewScroll, this);
13160         
13161         if(!this.tpl){
13162             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>';
13163         }
13164
13165         this.view = new Roo.View(this.list, this.tpl, {
13166             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13167         });
13168         
13169         //this.view.wrapEl.setDisplayed(false);
13170         this.view.on('click', this.onViewClick, this);
13171         
13172         
13173         
13174         this.store.on('beforeload', this.onBeforeLoad, this);
13175         this.store.on('load', this.onLoad, this);
13176         this.store.on('loadexception', this.onLoadException, this);
13177         
13178         if(this.editable){
13179             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13180                 "up" : function(e){
13181                     this.inKeyMode = true;
13182                     this.selectPrev();
13183                 },
13184
13185                 "down" : function(e){
13186                     this.inKeyMode = true;
13187                     this.selectNext();
13188                 },
13189
13190                 "enter" : function(e){
13191                     if(this.fireEvent("specialkey", this, e)){
13192                         this.onViewClick(false);
13193                     }
13194                     
13195                     return true;
13196                 },
13197
13198                 "esc" : function(e){
13199                     this.onTickableFooterButtonClick(e, false, false);
13200                 },
13201
13202                 "tab" : function(e){
13203                     this.fireEvent("specialkey", this, e);
13204                     
13205                     this.onTickableFooterButtonClick(e, false, false);
13206                     
13207                     return true;
13208                 },
13209
13210                 scope : this,
13211
13212                 doRelay : function(e, fn, key){
13213                     if(this.scope.isExpanded()){
13214                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13215                     }
13216                     return true;
13217                 },
13218
13219                 forceKeyDown: true
13220             });
13221         }
13222         
13223         this.queryDelay = Math.max(this.queryDelay || 10,
13224                 this.mode == 'local' ? 10 : 250);
13225         
13226         
13227         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13228         
13229         if(this.typeAhead){
13230             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13231         }
13232         
13233         if(this.editable !== false){
13234             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13235         }
13236         
13237     },
13238
13239     onDestroy : function(){
13240         if(this.view){
13241             this.view.setStore(null);
13242             this.view.el.removeAllListeners();
13243             this.view.el.remove();
13244             this.view.purgeListeners();
13245         }
13246         if(this.list){
13247             this.list.dom.innerHTML  = '';
13248         }
13249         
13250         if(this.store){
13251             this.store.un('beforeload', this.onBeforeLoad, this);
13252             this.store.un('load', this.onLoad, this);
13253             this.store.un('loadexception', this.onLoadException, this);
13254         }
13255         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13256     },
13257
13258     // private
13259     fireKey : function(e){
13260         if(e.isNavKeyPress() && !this.list.isVisible()){
13261             this.fireEvent("specialkey", this, e);
13262         }
13263     },
13264
13265     // private
13266     onResize: function(w, h){
13267 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13268 //        
13269 //        if(typeof w != 'number'){
13270 //            // we do not handle it!?!?
13271 //            return;
13272 //        }
13273 //        var tw = this.trigger.getWidth();
13274 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13275 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13276 //        var x = w - tw;
13277 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13278 //            
13279 //        //this.trigger.setStyle('left', x+'px');
13280 //        
13281 //        if(this.list && this.listWidth === undefined){
13282 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13283 //            this.list.setWidth(lw);
13284 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13285 //        }
13286         
13287     
13288         
13289     },
13290
13291     /**
13292      * Allow or prevent the user from directly editing the field text.  If false is passed,
13293      * the user will only be able to select from the items defined in the dropdown list.  This method
13294      * is the runtime equivalent of setting the 'editable' config option at config time.
13295      * @param {Boolean} value True to allow the user to directly edit the field text
13296      */
13297     setEditable : function(value){
13298         if(value == this.editable){
13299             return;
13300         }
13301         this.editable = value;
13302         if(!value){
13303             this.inputEl().dom.setAttribute('readOnly', true);
13304             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13305             this.inputEl().addClass('x-combo-noedit');
13306         }else{
13307             this.inputEl().dom.setAttribute('readOnly', false);
13308             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13309             this.inputEl().removeClass('x-combo-noedit');
13310         }
13311     },
13312
13313     // private
13314     
13315     onBeforeLoad : function(combo,opts){
13316         if(!this.hasFocus){
13317             return;
13318         }
13319          if (!opts.add) {
13320             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13321          }
13322         this.restrictHeight();
13323         this.selectedIndex = -1;
13324     },
13325
13326     // private
13327     onLoad : function(){
13328         
13329         this.hasQuery = false;
13330         
13331         if(!this.hasFocus){
13332             return;
13333         }
13334         
13335         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13336             this.loading.hide();
13337         }
13338              
13339         if(this.store.getCount() > 0){
13340             this.expand();
13341             this.restrictHeight();
13342             if(this.lastQuery == this.allQuery){
13343                 if(this.editable && !this.tickable){
13344                     this.inputEl().dom.select();
13345                 }
13346                 
13347                 if(
13348                     !this.selectByValue(this.value, true) &&
13349                     this.autoFocus && 
13350                     (
13351                         !this.store.lastOptions ||
13352                         typeof(this.store.lastOptions.add) == 'undefined' || 
13353                         this.store.lastOptions.add != true
13354                     )
13355                 ){
13356                     this.select(0, true);
13357                 }
13358             }else{
13359                 if(this.autoFocus){
13360                     this.selectNext();
13361                 }
13362                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13363                     this.taTask.delay(this.typeAheadDelay);
13364                 }
13365             }
13366         }else{
13367             this.onEmptyResults();
13368         }
13369         
13370         //this.el.focus();
13371     },
13372     // private
13373     onLoadException : function()
13374     {
13375         this.hasQuery = false;
13376         
13377         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13378             this.loading.hide();
13379         }
13380         
13381         if(this.tickable && this.editable){
13382             return;
13383         }
13384         
13385         this.collapse();
13386         // only causes errors at present
13387         //Roo.log(this.store.reader.jsonData);
13388         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13389             // fixme
13390             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13391         //}
13392         
13393         
13394     },
13395     // private
13396     onTypeAhead : function(){
13397         if(this.store.getCount() > 0){
13398             var r = this.store.getAt(0);
13399             var newValue = r.data[this.displayField];
13400             var len = newValue.length;
13401             var selStart = this.getRawValue().length;
13402             
13403             if(selStart != len){
13404                 this.setRawValue(newValue);
13405                 this.selectText(selStart, newValue.length);
13406             }
13407         }
13408     },
13409
13410     // private
13411     onSelect : function(record, index){
13412         
13413         if(this.fireEvent('beforeselect', this, record, index) !== false){
13414         
13415             this.setFromData(index > -1 ? record.data : false);
13416             
13417             this.collapse();
13418             this.fireEvent('select', this, record, index);
13419         }
13420     },
13421
13422     /**
13423      * Returns the currently selected field value or empty string if no value is set.
13424      * @return {String} value The selected value
13425      */
13426     getValue : function()
13427     {
13428         if(Roo.isIOS && this.useNativeIOS){
13429             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13430         }
13431         
13432         if(this.multiple){
13433             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13434         }
13435         
13436         if(this.valueField){
13437             return typeof this.value != 'undefined' ? this.value : '';
13438         }else{
13439             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13440         }
13441     },
13442     
13443     getRawValue : function()
13444     {
13445         if(Roo.isIOS && this.useNativeIOS){
13446             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13447         }
13448         
13449         var v = this.inputEl().getValue();
13450         
13451         return v;
13452     },
13453
13454     /**
13455      * Clears any text/value currently set in the field
13456      */
13457     clearValue : function(){
13458         
13459         if(this.hiddenField){
13460             this.hiddenField.dom.value = '';
13461         }
13462         this.value = '';
13463         this.setRawValue('');
13464         this.lastSelectionText = '';
13465         this.lastData = false;
13466         
13467         var close = this.closeTriggerEl();
13468         
13469         if(close){
13470             close.hide();
13471         }
13472         
13473         this.validate();
13474         
13475     },
13476
13477     /**
13478      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13479      * will be displayed in the field.  If the value does not match the data value of an existing item,
13480      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13481      * Otherwise the field will be blank (although the value will still be set).
13482      * @param {String} value The value to match
13483      */
13484     setValue : function(v)
13485     {
13486         if(Roo.isIOS && this.useNativeIOS){
13487             this.setIOSValue(v);
13488             return;
13489         }
13490         
13491         if(this.multiple){
13492             this.syncValue();
13493             return;
13494         }
13495         
13496         var text = v;
13497         if(this.valueField){
13498             var r = this.findRecord(this.valueField, v);
13499             if(r){
13500                 text = r.data[this.displayField];
13501             }else if(this.valueNotFoundText !== undefined){
13502                 text = this.valueNotFoundText;
13503             }
13504         }
13505         this.lastSelectionText = text;
13506         if(this.hiddenField){
13507             this.hiddenField.dom.value = v;
13508         }
13509         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13510         this.value = v;
13511         
13512         var close = this.closeTriggerEl();
13513         
13514         if(close){
13515             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13516         }
13517         
13518         this.validate();
13519     },
13520     /**
13521      * @property {Object} the last set data for the element
13522      */
13523     
13524     lastData : false,
13525     /**
13526      * Sets the value of the field based on a object which is related to the record format for the store.
13527      * @param {Object} value the value to set as. or false on reset?
13528      */
13529     setFromData : function(o){
13530         
13531         if(this.multiple){
13532             this.addItem(o);
13533             return;
13534         }
13535             
13536         var dv = ''; // display value
13537         var vv = ''; // value value..
13538         this.lastData = o;
13539         if (this.displayField) {
13540             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13541         } else {
13542             // this is an error condition!!!
13543             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13544         }
13545         
13546         if(this.valueField){
13547             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13548         }
13549         
13550         var close = this.closeTriggerEl();
13551         
13552         if(close){
13553             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13554         }
13555         
13556         if(this.hiddenField){
13557             this.hiddenField.dom.value = vv;
13558             
13559             this.lastSelectionText = dv;
13560             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13561             this.value = vv;
13562             return;
13563         }
13564         // no hidden field.. - we store the value in 'value', but still display
13565         // display field!!!!
13566         this.lastSelectionText = dv;
13567         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13568         this.value = vv;
13569         
13570         
13571         
13572     },
13573     // private
13574     reset : function(){
13575         // overridden so that last data is reset..
13576         
13577         if(this.multiple){
13578             this.clearItem();
13579             return;
13580         }
13581         
13582         this.setValue(this.originalValue);
13583         //this.clearInvalid();
13584         this.lastData = false;
13585         if (this.view) {
13586             this.view.clearSelections();
13587         }
13588         
13589         this.validate();
13590     },
13591     // private
13592     findRecord : function(prop, value){
13593         var record;
13594         if(this.store.getCount() > 0){
13595             this.store.each(function(r){
13596                 if(r.data[prop] == value){
13597                     record = r;
13598                     return false;
13599                 }
13600                 return true;
13601             });
13602         }
13603         return record;
13604     },
13605     
13606     getName: function()
13607     {
13608         // returns hidden if it's set..
13609         if (!this.rendered) {return ''};
13610         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13611         
13612     },
13613     // private
13614     onViewMove : function(e, t){
13615         this.inKeyMode = false;
13616     },
13617
13618     // private
13619     onViewOver : function(e, t){
13620         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13621             return;
13622         }
13623         var item = this.view.findItemFromChild(t);
13624         
13625         if(item){
13626             var index = this.view.indexOf(item);
13627             this.select(index, false);
13628         }
13629     },
13630
13631     // private
13632     onViewClick : function(view, doFocus, el, e)
13633     {
13634         var index = this.view.getSelectedIndexes()[0];
13635         
13636         var r = this.store.getAt(index);
13637         
13638         if(this.tickable){
13639             
13640             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13641                 return;
13642             }
13643             
13644             var rm = false;
13645             var _this = this;
13646             
13647             Roo.each(this.tickItems, function(v,k){
13648                 
13649                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13650                     Roo.log(v);
13651                     _this.tickItems.splice(k, 1);
13652                     
13653                     if(typeof(e) == 'undefined' && view == false){
13654                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13655                     }
13656                     
13657                     rm = true;
13658                     return;
13659                 }
13660             });
13661             
13662             if(rm){
13663                 return;
13664             }
13665             
13666             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13667                 this.tickItems.push(r.data);
13668             }
13669             
13670             if(typeof(e) == 'undefined' && view == false){
13671                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13672             }
13673                     
13674             return;
13675         }
13676         
13677         if(r){
13678             this.onSelect(r, index);
13679         }
13680         if(doFocus !== false && !this.blockFocus){
13681             this.inputEl().focus();
13682         }
13683     },
13684
13685     // private
13686     restrictHeight : function(){
13687         //this.innerList.dom.style.height = '';
13688         //var inner = this.innerList.dom;
13689         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13690         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13691         //this.list.beginUpdate();
13692         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13693         this.list.alignTo(this.inputEl(), this.listAlign);
13694         this.list.alignTo(this.inputEl(), this.listAlign);
13695         //this.list.endUpdate();
13696     },
13697
13698     // private
13699     onEmptyResults : function(){
13700         
13701         if(this.tickable && this.editable){
13702             this.restrictHeight();
13703             return;
13704         }
13705         
13706         this.collapse();
13707     },
13708
13709     /**
13710      * Returns true if the dropdown list is expanded, else false.
13711      */
13712     isExpanded : function(){
13713         return this.list.isVisible();
13714     },
13715
13716     /**
13717      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13718      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13719      * @param {String} value The data value of the item to select
13720      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13721      * selected item if it is not currently in view (defaults to true)
13722      * @return {Boolean} True if the value matched an item in the list, else false
13723      */
13724     selectByValue : function(v, scrollIntoView){
13725         if(v !== undefined && v !== null){
13726             var r = this.findRecord(this.valueField || this.displayField, v);
13727             if(r){
13728                 this.select(this.store.indexOf(r), scrollIntoView);
13729                 return true;
13730             }
13731         }
13732         return false;
13733     },
13734
13735     /**
13736      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13737      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13738      * @param {Number} index The zero-based index of the list item to select
13739      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13740      * selected item if it is not currently in view (defaults to true)
13741      */
13742     select : function(index, scrollIntoView){
13743         this.selectedIndex = index;
13744         this.view.select(index);
13745         if(scrollIntoView !== false){
13746             var el = this.view.getNode(index);
13747             /*
13748              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13749              */
13750             if(el){
13751                 this.list.scrollChildIntoView(el, false);
13752             }
13753         }
13754     },
13755
13756     // private
13757     selectNext : function(){
13758         var ct = this.store.getCount();
13759         if(ct > 0){
13760             if(this.selectedIndex == -1){
13761                 this.select(0);
13762             }else if(this.selectedIndex < ct-1){
13763                 this.select(this.selectedIndex+1);
13764             }
13765         }
13766     },
13767
13768     // private
13769     selectPrev : function(){
13770         var ct = this.store.getCount();
13771         if(ct > 0){
13772             if(this.selectedIndex == -1){
13773                 this.select(0);
13774             }else if(this.selectedIndex != 0){
13775                 this.select(this.selectedIndex-1);
13776             }
13777         }
13778     },
13779
13780     // private
13781     onKeyUp : function(e){
13782         if(this.editable !== false && !e.isSpecialKey()){
13783             this.lastKey = e.getKey();
13784             this.dqTask.delay(this.queryDelay);
13785         }
13786     },
13787
13788     // private
13789     validateBlur : function(){
13790         return !this.list || !this.list.isVisible();   
13791     },
13792
13793     // private
13794     initQuery : function(){
13795         
13796         var v = this.getRawValue();
13797         
13798         if(this.tickable && this.editable){
13799             v = this.tickableInputEl().getValue();
13800         }
13801         
13802         this.doQuery(v);
13803     },
13804
13805     // private
13806     doForce : function(){
13807         if(this.inputEl().dom.value.length > 0){
13808             this.inputEl().dom.value =
13809                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13810              
13811         }
13812     },
13813
13814     /**
13815      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13816      * query allowing the query action to be canceled if needed.
13817      * @param {String} query The SQL query to execute
13818      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13819      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13820      * saved in the current store (defaults to false)
13821      */
13822     doQuery : function(q, forceAll){
13823         
13824         if(q === undefined || q === null){
13825             q = '';
13826         }
13827         var qe = {
13828             query: q,
13829             forceAll: forceAll,
13830             combo: this,
13831             cancel:false
13832         };
13833         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13834             return false;
13835         }
13836         q = qe.query;
13837         
13838         forceAll = qe.forceAll;
13839         if(forceAll === true || (q.length >= this.minChars)){
13840             
13841             this.hasQuery = true;
13842             
13843             if(this.lastQuery != q || this.alwaysQuery){
13844                 this.lastQuery = q;
13845                 if(this.mode == 'local'){
13846                     this.selectedIndex = -1;
13847                     if(forceAll){
13848                         this.store.clearFilter();
13849                     }else{
13850                         
13851                         if(this.specialFilter){
13852                             this.fireEvent('specialfilter', this);
13853                             this.onLoad();
13854                             return;
13855                         }
13856                         
13857                         this.store.filter(this.displayField, q);
13858                     }
13859                     
13860                     this.store.fireEvent("datachanged", this.store);
13861                     
13862                     this.onLoad();
13863                     
13864                     
13865                 }else{
13866                     
13867                     this.store.baseParams[this.queryParam] = q;
13868                     
13869                     var options = {params : this.getParams(q)};
13870                     
13871                     if(this.loadNext){
13872                         options.add = true;
13873                         options.params.start = this.page * this.pageSize;
13874                     }
13875                     
13876                     this.store.load(options);
13877                     
13878                     /*
13879                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13880                      *  we should expand the list on onLoad
13881                      *  so command out it
13882                      */
13883 //                    this.expand();
13884                 }
13885             }else{
13886                 this.selectedIndex = -1;
13887                 this.onLoad();   
13888             }
13889         }
13890         
13891         this.loadNext = false;
13892     },
13893     
13894     // private
13895     getParams : function(q){
13896         var p = {};
13897         //p[this.queryParam] = q;
13898         
13899         if(this.pageSize){
13900             p.start = 0;
13901             p.limit = this.pageSize;
13902         }
13903         return p;
13904     },
13905
13906     /**
13907      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13908      */
13909     collapse : function(){
13910         if(!this.isExpanded()){
13911             return;
13912         }
13913         
13914         this.list.hide();
13915         
13916         this.hasFocus = false;
13917         
13918         if(this.tickable){
13919             this.okBtn.hide();
13920             this.cancelBtn.hide();
13921             this.trigger.show();
13922             
13923             if(this.editable){
13924                 this.tickableInputEl().dom.value = '';
13925                 this.tickableInputEl().blur();
13926             }
13927             
13928         }
13929         
13930         Roo.get(document).un('mousedown', this.collapseIf, this);
13931         Roo.get(document).un('mousewheel', this.collapseIf, this);
13932         if (!this.editable) {
13933             Roo.get(document).un('keydown', this.listKeyPress, this);
13934         }
13935         this.fireEvent('collapse', this);
13936         
13937         this.validate();
13938     },
13939
13940     // private
13941     collapseIf : function(e){
13942         var in_combo  = e.within(this.el);
13943         var in_list =  e.within(this.list);
13944         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13945         
13946         if (in_combo || in_list || is_list) {
13947             //e.stopPropagation();
13948             return;
13949         }
13950         
13951         if(this.tickable){
13952             this.onTickableFooterButtonClick(e, false, false);
13953         }
13954
13955         this.collapse();
13956         
13957     },
13958
13959     /**
13960      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13961      */
13962     expand : function(){
13963        
13964         if(this.isExpanded() || !this.hasFocus){
13965             return;
13966         }
13967         
13968         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13969         this.list.setWidth(lw);
13970         
13971         Roo.log('expand');
13972         
13973         this.list.show();
13974         
13975         this.restrictHeight();
13976         
13977         if(this.tickable){
13978             
13979             this.tickItems = Roo.apply([], this.item);
13980             
13981             this.okBtn.show();
13982             this.cancelBtn.show();
13983             this.trigger.hide();
13984             
13985             if(this.editable){
13986                 this.tickableInputEl().focus();
13987             }
13988             
13989         }
13990         
13991         Roo.get(document).on('mousedown', this.collapseIf, this);
13992         Roo.get(document).on('mousewheel', this.collapseIf, this);
13993         if (!this.editable) {
13994             Roo.get(document).on('keydown', this.listKeyPress, this);
13995         }
13996         
13997         this.fireEvent('expand', this);
13998     },
13999
14000     // private
14001     // Implements the default empty TriggerField.onTriggerClick function
14002     onTriggerClick : function(e)
14003     {
14004         Roo.log('trigger click');
14005         
14006         if(this.disabled || !this.triggerList){
14007             return;
14008         }
14009         
14010         this.page = 0;
14011         this.loadNext = false;
14012         
14013         if(this.isExpanded()){
14014             this.collapse();
14015             if (!this.blockFocus) {
14016                 this.inputEl().focus();
14017             }
14018             
14019         }else {
14020             this.hasFocus = true;
14021             if(this.triggerAction == 'all') {
14022                 this.doQuery(this.allQuery, true);
14023             } else {
14024                 this.doQuery(this.getRawValue());
14025             }
14026             if (!this.blockFocus) {
14027                 this.inputEl().focus();
14028             }
14029         }
14030     },
14031     
14032     onTickableTriggerClick : function(e)
14033     {
14034         if(this.disabled){
14035             return;
14036         }
14037         
14038         this.page = 0;
14039         this.loadNext = false;
14040         this.hasFocus = true;
14041         
14042         if(this.triggerAction == 'all') {
14043             this.doQuery(this.allQuery, true);
14044         } else {
14045             this.doQuery(this.getRawValue());
14046         }
14047     },
14048     
14049     onSearchFieldClick : function(e)
14050     {
14051         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14052             this.onTickableFooterButtonClick(e, false, false);
14053             return;
14054         }
14055         
14056         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14057             return;
14058         }
14059         
14060         this.page = 0;
14061         this.loadNext = false;
14062         this.hasFocus = true;
14063         
14064         if(this.triggerAction == 'all') {
14065             this.doQuery(this.allQuery, true);
14066         } else {
14067             this.doQuery(this.getRawValue());
14068         }
14069     },
14070     
14071     listKeyPress : function(e)
14072     {
14073         //Roo.log('listkeypress');
14074         // scroll to first matching element based on key pres..
14075         if (e.isSpecialKey()) {
14076             return false;
14077         }
14078         var k = String.fromCharCode(e.getKey()).toUpperCase();
14079         //Roo.log(k);
14080         var match  = false;
14081         var csel = this.view.getSelectedNodes();
14082         var cselitem = false;
14083         if (csel.length) {
14084             var ix = this.view.indexOf(csel[0]);
14085             cselitem  = this.store.getAt(ix);
14086             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14087                 cselitem = false;
14088             }
14089             
14090         }
14091         
14092         this.store.each(function(v) { 
14093             if (cselitem) {
14094                 // start at existing selection.
14095                 if (cselitem.id == v.id) {
14096                     cselitem = false;
14097                 }
14098                 return true;
14099             }
14100                 
14101             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14102                 match = this.store.indexOf(v);
14103                 return false;
14104             }
14105             return true;
14106         }, this);
14107         
14108         if (match === false) {
14109             return true; // no more action?
14110         }
14111         // scroll to?
14112         this.view.select(match);
14113         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14114         sn.scrollIntoView(sn.dom.parentNode, false);
14115     },
14116     
14117     onViewScroll : function(e, t){
14118         
14119         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){
14120             return;
14121         }
14122         
14123         this.hasQuery = true;
14124         
14125         this.loading = this.list.select('.loading', true).first();
14126         
14127         if(this.loading === null){
14128             this.list.createChild({
14129                 tag: 'div',
14130                 cls: 'loading roo-select2-more-results roo-select2-active',
14131                 html: 'Loading more results...'
14132             });
14133             
14134             this.loading = this.list.select('.loading', true).first();
14135             
14136             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14137             
14138             this.loading.hide();
14139         }
14140         
14141         this.loading.show();
14142         
14143         var _combo = this;
14144         
14145         this.page++;
14146         this.loadNext = true;
14147         
14148         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14149         
14150         return;
14151     },
14152     
14153     addItem : function(o)
14154     {   
14155         var dv = ''; // display value
14156         
14157         if (this.displayField) {
14158             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14159         } else {
14160             // this is an error condition!!!
14161             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14162         }
14163         
14164         if(!dv.length){
14165             return;
14166         }
14167         
14168         var choice = this.choices.createChild({
14169             tag: 'li',
14170             cls: 'roo-select2-search-choice',
14171             cn: [
14172                 {
14173                     tag: 'div',
14174                     html: dv
14175                 },
14176                 {
14177                     tag: 'a',
14178                     href: '#',
14179                     cls: 'roo-select2-search-choice-close',
14180                     tabindex: '-1'
14181                 }
14182             ]
14183             
14184         }, this.searchField);
14185         
14186         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14187         
14188         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14189         
14190         this.item.push(o);
14191         
14192         this.lastData = o;
14193         
14194         this.syncValue();
14195         
14196         this.inputEl().dom.value = '';
14197         
14198         this.validate();
14199     },
14200     
14201     onRemoveItem : function(e, _self, o)
14202     {
14203         e.preventDefault();
14204         
14205         this.lastItem = Roo.apply([], this.item);
14206         
14207         var index = this.item.indexOf(o.data) * 1;
14208         
14209         if( index < 0){
14210             Roo.log('not this item?!');
14211             return;
14212         }
14213         
14214         this.item.splice(index, 1);
14215         o.item.remove();
14216         
14217         this.syncValue();
14218         
14219         this.fireEvent('remove', this, e);
14220         
14221         this.validate();
14222         
14223     },
14224     
14225     syncValue : function()
14226     {
14227         if(!this.item.length){
14228             this.clearValue();
14229             return;
14230         }
14231             
14232         var value = [];
14233         var _this = this;
14234         Roo.each(this.item, function(i){
14235             if(_this.valueField){
14236                 value.push(i[_this.valueField]);
14237                 return;
14238             }
14239
14240             value.push(i);
14241         });
14242
14243         this.value = value.join(',');
14244
14245         if(this.hiddenField){
14246             this.hiddenField.dom.value = this.value;
14247         }
14248         
14249         this.store.fireEvent("datachanged", this.store);
14250         
14251         this.validate();
14252     },
14253     
14254     clearItem : function()
14255     {
14256         if(!this.multiple){
14257             return;
14258         }
14259         
14260         this.item = [];
14261         
14262         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14263            c.remove();
14264         });
14265         
14266         this.syncValue();
14267         
14268         this.validate();
14269         
14270         if(this.tickable && !Roo.isTouch){
14271             this.view.refresh();
14272         }
14273     },
14274     
14275     inputEl: function ()
14276     {
14277         if(Roo.isIOS && this.useNativeIOS){
14278             return this.el.select('select.roo-ios-select', true).first();
14279         }
14280         
14281         if(Roo.isTouch && this.mobileTouchView){
14282             return this.el.select('input.form-control',true).first();
14283         }
14284         
14285         if(this.tickable){
14286             return this.searchField;
14287         }
14288         
14289         return this.el.select('input.form-control',true).first();
14290     },
14291     
14292     onTickableFooterButtonClick : function(e, btn, el)
14293     {
14294         e.preventDefault();
14295         
14296         this.lastItem = Roo.apply([], this.item);
14297         
14298         if(btn && btn.name == 'cancel'){
14299             this.tickItems = Roo.apply([], this.item);
14300             this.collapse();
14301             return;
14302         }
14303         
14304         this.clearItem();
14305         
14306         var _this = this;
14307         
14308         Roo.each(this.tickItems, function(o){
14309             _this.addItem(o);
14310         });
14311         
14312         this.collapse();
14313         
14314     },
14315     
14316     validate : function()
14317     {
14318         var v = this.getRawValue();
14319         
14320         if(this.multiple){
14321             v = this.getValue();
14322         }
14323         
14324         if(this.disabled || this.allowBlank || v.length){
14325             this.markValid();
14326             return true;
14327         }
14328         
14329         this.markInvalid();
14330         return false;
14331     },
14332     
14333     tickableInputEl : function()
14334     {
14335         if(!this.tickable || !this.editable){
14336             return this.inputEl();
14337         }
14338         
14339         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14340     },
14341     
14342     
14343     getAutoCreateTouchView : function()
14344     {
14345         var id = Roo.id();
14346         
14347         var cfg = {
14348             cls: 'form-group' //input-group
14349         };
14350         
14351         var input =  {
14352             tag: 'input',
14353             id : id,
14354             type : this.inputType,
14355             cls : 'form-control x-combo-noedit',
14356             autocomplete: 'new-password',
14357             placeholder : this.placeholder || '',
14358             readonly : true
14359         };
14360         
14361         if (this.name) {
14362             input.name = this.name;
14363         }
14364         
14365         if (this.size) {
14366             input.cls += ' input-' + this.size;
14367         }
14368         
14369         if (this.disabled) {
14370             input.disabled = true;
14371         }
14372         
14373         var inputblock = {
14374             cls : '',
14375             cn : [
14376                 input
14377             ]
14378         };
14379         
14380         if(this.before){
14381             inputblock.cls += ' input-group';
14382             
14383             inputblock.cn.unshift({
14384                 tag :'span',
14385                 cls : 'input-group-addon',
14386                 html : this.before
14387             });
14388         }
14389         
14390         if(this.removable && !this.multiple){
14391             inputblock.cls += ' roo-removable';
14392             
14393             inputblock.cn.push({
14394                 tag: 'button',
14395                 html : 'x',
14396                 cls : 'roo-combo-removable-btn close'
14397             });
14398         }
14399
14400         if(this.hasFeedback && !this.allowBlank){
14401             
14402             inputblock.cls += ' has-feedback';
14403             
14404             inputblock.cn.push({
14405                 tag: 'span',
14406                 cls: 'glyphicon form-control-feedback'
14407             });
14408             
14409         }
14410         
14411         if (this.after) {
14412             
14413             inputblock.cls += (this.before) ? '' : ' input-group';
14414             
14415             inputblock.cn.push({
14416                 tag :'span',
14417                 cls : 'input-group-addon',
14418                 html : this.after
14419             });
14420         }
14421
14422         var box = {
14423             tag: 'div',
14424             cn: [
14425                 {
14426                     tag: 'input',
14427                     type : 'hidden',
14428                     cls: 'form-hidden-field'
14429                 },
14430                 inputblock
14431             ]
14432             
14433         };
14434         
14435         if(this.multiple){
14436             box = {
14437                 tag: 'div',
14438                 cn: [
14439                     {
14440                         tag: 'input',
14441                         type : 'hidden',
14442                         cls: 'form-hidden-field'
14443                     },
14444                     {
14445                         tag: 'ul',
14446                         cls: 'roo-select2-choices',
14447                         cn:[
14448                             {
14449                                 tag: 'li',
14450                                 cls: 'roo-select2-search-field',
14451                                 cn: [
14452
14453                                     inputblock
14454                                 ]
14455                             }
14456                         ]
14457                     }
14458                 ]
14459             }
14460         };
14461         
14462         var combobox = {
14463             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14464             cn: [
14465                 box
14466             ]
14467         };
14468         
14469         if(!this.multiple && this.showToggleBtn){
14470             
14471             var caret = {
14472                         tag: 'span',
14473                         cls: 'caret'
14474             };
14475             
14476             if (this.caret != false) {
14477                 caret = {
14478                      tag: 'i',
14479                      cls: 'fa fa-' + this.caret
14480                 };
14481                 
14482             }
14483             
14484             combobox.cn.push({
14485                 tag :'span',
14486                 cls : 'input-group-addon btn dropdown-toggle',
14487                 cn : [
14488                     caret,
14489                     {
14490                         tag: 'span',
14491                         cls: 'combobox-clear',
14492                         cn  : [
14493                             {
14494                                 tag : 'i',
14495                                 cls: 'icon-remove'
14496                             }
14497                         ]
14498                     }
14499                 ]
14500
14501             })
14502         }
14503         
14504         if(this.multiple){
14505             combobox.cls += ' roo-select2-container-multi';
14506         }
14507         
14508         var align = this.labelAlign || this.parentLabelAlign();
14509         
14510         if (align ==='left' && this.fieldLabel.length) {
14511             
14512             cfg.cn = [
14513                 {
14514                    tag : 'i',
14515                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14516                    tooltip : 'This field is required'
14517                 },
14518                 {
14519                     tag: 'label',
14520                     cls : 'control-label',
14521                     html : this.fieldLabel
14522
14523                 },
14524                 {
14525                     cls : '', 
14526                     cn: [
14527                         combobox
14528                     ]
14529                 }
14530             ];
14531             
14532             var labelCfg = cfg.cn[1];
14533             var contentCfg = cfg.cn[2];
14534             
14535
14536             if(this.indicatorpos == 'right'){
14537                 cfg.cn = [
14538                     {
14539                         tag: 'label',
14540                         cls : 'control-label',
14541                         html : this.fieldLabel
14542
14543                     },
14544                     {
14545                        tag : 'i',
14546                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14547                        tooltip : 'This field is required'
14548                     },
14549                     {
14550                         cls : '', 
14551                         cn: [
14552                             combobox
14553                         ]
14554                     }
14555                 ];
14556             }
14557             
14558             labelCfg = cfg.cn[0];
14559             
14560             if(this.labelWidth > 12){
14561                 labelCfg.style = "width: " + this.labelWidth + 'px';
14562             }
14563             
14564             if(this.labelWidth < 13 && this.labelmd == 0){
14565                 this.labelmd = this.labelWidth;
14566             }
14567             
14568             if(this.labellg > 0){
14569                 labelCfg.cls += ' col-lg-' + this.labellg;
14570                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14571             }
14572             
14573             if(this.labelmd > 0){
14574                 labelCfg.cls += ' col-md-' + this.labelmd;
14575                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14576             }
14577             
14578             if(this.labelsm > 0){
14579                 labelCfg.cls += ' col-sm-' + this.labelsm;
14580                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14581             }
14582             
14583             if(this.labelxs > 0){
14584                 labelCfg.cls += ' col-xs-' + this.labelxs;
14585                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14586             }
14587                 
14588                 
14589         } else if ( this.fieldLabel.length) {
14590             cfg.cn = [
14591                 {
14592                    tag : 'i',
14593                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14594                    tooltip : 'This field is required'
14595                 },
14596                 {
14597                     tag: 'label',
14598                     cls : 'control-label',
14599                     html : this.fieldLabel
14600
14601                 },
14602                 {
14603                     cls : '', 
14604                     cn: [
14605                         combobox
14606                     ]
14607                 }
14608             ];
14609             
14610             if(this.indicatorpos == 'right'){
14611                 cfg.cn = [
14612                     {
14613                         tag: 'label',
14614                         cls : 'control-label',
14615                         html : this.fieldLabel
14616
14617                     },
14618                     {
14619                        tag : 'i',
14620                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14621                        tooltip : 'This field is required'
14622                     },
14623                     {
14624                         cls : '', 
14625                         cn: [
14626                             combobox
14627                         ]
14628                     }
14629                 ];
14630             }
14631         } else {
14632             cfg.cn = combobox;    
14633         }
14634         
14635         
14636         var settings = this;
14637         
14638         ['xs','sm','md','lg'].map(function(size){
14639             if (settings[size]) {
14640                 cfg.cls += ' col-' + size + '-' + settings[size];
14641             }
14642         });
14643         
14644         return cfg;
14645     },
14646     
14647     initTouchView : function()
14648     {
14649         this.renderTouchView();
14650         
14651         this.touchViewEl.on('scroll', function(){
14652             this.el.dom.scrollTop = 0;
14653         }, this);
14654         
14655         this.originalValue = this.getValue();
14656         
14657         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14658         
14659         this.inputEl().on("click", this.showTouchView, this);
14660         if (this.triggerEl) {
14661             this.triggerEl.on("click", this.showTouchView, this);
14662         }
14663         
14664         
14665         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14666         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14667         
14668         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14669         
14670         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14671         this.store.on('load', this.onTouchViewLoad, this);
14672         this.store.on('loadexception', this.onTouchViewLoadException, this);
14673         
14674         if(this.hiddenName){
14675             
14676             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14677             
14678             this.hiddenField.dom.value =
14679                 this.hiddenValue !== undefined ? this.hiddenValue :
14680                 this.value !== undefined ? this.value : '';
14681         
14682             this.el.dom.removeAttribute('name');
14683             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14684         }
14685         
14686         if(this.multiple){
14687             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14688             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14689         }
14690         
14691         if(this.removable && !this.multiple){
14692             var close = this.closeTriggerEl();
14693             if(close){
14694                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14695                 close.on('click', this.removeBtnClick, this, close);
14696             }
14697         }
14698         /*
14699          * fix the bug in Safari iOS8
14700          */
14701         this.inputEl().on("focus", function(e){
14702             document.activeElement.blur();
14703         }, this);
14704         
14705         return;
14706         
14707         
14708     },
14709     
14710     renderTouchView : function()
14711     {
14712         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14713         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14714         
14715         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14716         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14717         
14718         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14719         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14720         this.touchViewBodyEl.setStyle('overflow', 'auto');
14721         
14722         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14723         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14724         
14725         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14726         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14727         
14728     },
14729     
14730     showTouchView : function()
14731     {
14732         if(this.disabled){
14733             return;
14734         }
14735         
14736         this.touchViewHeaderEl.hide();
14737
14738         if(this.modalTitle.length){
14739             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14740             this.touchViewHeaderEl.show();
14741         }
14742
14743         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14744         this.touchViewEl.show();
14745
14746         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14747         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14748                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14749
14750         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14751
14752         if(this.modalTitle.length){
14753             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14754         }
14755         
14756         this.touchViewBodyEl.setHeight(bodyHeight);
14757
14758         if(this.animate){
14759             var _this = this;
14760             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14761         }else{
14762             this.touchViewEl.addClass('in');
14763         }
14764
14765         this.doTouchViewQuery();
14766         
14767     },
14768     
14769     hideTouchView : function()
14770     {
14771         this.touchViewEl.removeClass('in');
14772
14773         if(this.animate){
14774             var _this = this;
14775             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14776         }else{
14777             this.touchViewEl.setStyle('display', 'none');
14778         }
14779         
14780     },
14781     
14782     setTouchViewValue : function()
14783     {
14784         if(this.multiple){
14785             this.clearItem();
14786         
14787             var _this = this;
14788
14789             Roo.each(this.tickItems, function(o){
14790                 this.addItem(o);
14791             }, this);
14792         }
14793         
14794         this.hideTouchView();
14795     },
14796     
14797     doTouchViewQuery : function()
14798     {
14799         var qe = {
14800             query: '',
14801             forceAll: true,
14802             combo: this,
14803             cancel:false
14804         };
14805         
14806         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14807             return false;
14808         }
14809         
14810         if(!this.alwaysQuery || this.mode == 'local'){
14811             this.onTouchViewLoad();
14812             return;
14813         }
14814         
14815         this.store.load();
14816     },
14817     
14818     onTouchViewBeforeLoad : function(combo,opts)
14819     {
14820         return;
14821     },
14822
14823     // private
14824     onTouchViewLoad : function()
14825     {
14826         if(this.store.getCount() < 1){
14827             this.onTouchViewEmptyResults();
14828             return;
14829         }
14830         
14831         this.clearTouchView();
14832         
14833         var rawValue = this.getRawValue();
14834         
14835         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14836         
14837         this.tickItems = [];
14838         
14839         this.store.data.each(function(d, rowIndex){
14840             var row = this.touchViewListGroup.createChild(template);
14841             
14842             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14843                 row.addClass(d.data.cls);
14844             }
14845             
14846             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14847                 var cfg = {
14848                     data : d.data,
14849                     html : d.data[this.displayField]
14850                 };
14851                 
14852                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14853                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14854                 }
14855             }
14856             row.removeClass('selected');
14857             if(!this.multiple && this.valueField &&
14858                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14859             {
14860                 // radio buttons..
14861                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14862                 row.addClass('selected');
14863             }
14864             
14865             if(this.multiple && this.valueField &&
14866                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14867             {
14868                 
14869                 // checkboxes...
14870                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14871                 this.tickItems.push(d.data);
14872             }
14873             
14874             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14875             
14876         }, this);
14877         
14878         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14879         
14880         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14881
14882         if(this.modalTitle.length){
14883             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14884         }
14885
14886         var listHeight = this.touchViewListGroup.getHeight();
14887         
14888         var _this = this;
14889         
14890         if(firstChecked && listHeight > bodyHeight){
14891             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14892         }
14893         
14894     },
14895     
14896     onTouchViewLoadException : function()
14897     {
14898         this.hideTouchView();
14899     },
14900     
14901     onTouchViewEmptyResults : function()
14902     {
14903         this.clearTouchView();
14904         
14905         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14906         
14907         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14908         
14909     },
14910     
14911     clearTouchView : function()
14912     {
14913         this.touchViewListGroup.dom.innerHTML = '';
14914     },
14915     
14916     onTouchViewClick : function(e, el, o)
14917     {
14918         e.preventDefault();
14919         
14920         var row = o.row;
14921         var rowIndex = o.rowIndex;
14922         
14923         var r = this.store.getAt(rowIndex);
14924         
14925         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14926             
14927             if(!this.multiple){
14928                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14929                     c.dom.removeAttribute('checked');
14930                 }, this);
14931
14932                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14933
14934                 this.setFromData(r.data);
14935
14936                 var close = this.closeTriggerEl();
14937
14938                 if(close){
14939                     close.show();
14940                 }
14941
14942                 this.hideTouchView();
14943
14944                 this.fireEvent('select', this, r, rowIndex);
14945
14946                 return;
14947             }
14948
14949             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14950                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14951                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14952                 return;
14953             }
14954
14955             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14956             this.addItem(r.data);
14957             this.tickItems.push(r.data);
14958         }
14959     },
14960     
14961     getAutoCreateNativeIOS : function()
14962     {
14963         var cfg = {
14964             cls: 'form-group' //input-group,
14965         };
14966         
14967         var combobox =  {
14968             tag: 'select',
14969             cls : 'roo-ios-select'
14970         };
14971         
14972         if (this.name) {
14973             combobox.name = this.name;
14974         }
14975         
14976         if (this.disabled) {
14977             combobox.disabled = true;
14978         }
14979         
14980         var settings = this;
14981         
14982         ['xs','sm','md','lg'].map(function(size){
14983             if (settings[size]) {
14984                 cfg.cls += ' col-' + size + '-' + settings[size];
14985             }
14986         });
14987         
14988         cfg.cn = combobox;
14989         
14990         return cfg;
14991         
14992     },
14993     
14994     initIOSView : function()
14995     {
14996         this.store.on('load', this.onIOSViewLoad, this);
14997         
14998         return;
14999     },
15000     
15001     onIOSViewLoad : function()
15002     {
15003         if(this.store.getCount() < 1){
15004             return;
15005         }
15006         
15007         this.clearIOSView();
15008         
15009         if(this.allowBlank) {
15010             
15011             var default_text = '-- SELECT --';
15012             
15013             var opt = this.inputEl().createChild({
15014                 tag: 'option',
15015                 value : 0,
15016                 html : default_text
15017             });
15018             
15019             var o = {};
15020             o[this.valueField] = 0;
15021             o[this.displayField] = default_text;
15022             
15023             this.ios_options.push({
15024                 data : o,
15025                 el : opt
15026             });
15027             
15028         }
15029         
15030         this.store.data.each(function(d, rowIndex){
15031             
15032             var html = '';
15033             
15034             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15035                 html = d.data[this.displayField];
15036             }
15037             
15038             var value = '';
15039             
15040             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15041                 value = d.data[this.valueField];
15042             }
15043             
15044             var option = {
15045                 tag: 'option',
15046                 value : value,
15047                 html : html
15048             };
15049             
15050             if(this.value == d.data[this.valueField]){
15051                 option['selected'] = true;
15052             }
15053             
15054             var opt = this.inputEl().createChild(option);
15055             
15056             this.ios_options.push({
15057                 data : d.data,
15058                 el : opt
15059             });
15060             
15061         }, this);
15062         
15063         this.inputEl().on('change', function(){
15064            this.fireEvent('select', this);
15065         }, this);
15066         
15067     },
15068     
15069     clearIOSView: function()
15070     {
15071         this.inputEl().dom.innerHTML = '';
15072         
15073         this.ios_options = [];
15074     },
15075     
15076     setIOSValue: function(v)
15077     {
15078         this.value = v;
15079         
15080         if(!this.ios_options){
15081             return;
15082         }
15083         
15084         Roo.each(this.ios_options, function(opts){
15085            
15086            opts.el.dom.removeAttribute('selected');
15087            
15088            if(opts.data[this.valueField] != v){
15089                return;
15090            }
15091            
15092            opts.el.dom.setAttribute('selected', true);
15093            
15094         }, this);
15095     }
15096
15097     /** 
15098     * @cfg {Boolean} grow 
15099     * @hide 
15100     */
15101     /** 
15102     * @cfg {Number} growMin 
15103     * @hide 
15104     */
15105     /** 
15106     * @cfg {Number} growMax 
15107     * @hide 
15108     */
15109     /**
15110      * @hide
15111      * @method autoSize
15112      */
15113 });
15114
15115 Roo.apply(Roo.bootstrap.ComboBox,  {
15116     
15117     header : {
15118         tag: 'div',
15119         cls: 'modal-header',
15120         cn: [
15121             {
15122                 tag: 'h4',
15123                 cls: 'modal-title'
15124             }
15125         ]
15126     },
15127     
15128     body : {
15129         tag: 'div',
15130         cls: 'modal-body',
15131         cn: [
15132             {
15133                 tag: 'ul',
15134                 cls: 'list-group'
15135             }
15136         ]
15137     },
15138     
15139     listItemRadio : {
15140         tag: 'li',
15141         cls: 'list-group-item',
15142         cn: [
15143             {
15144                 tag: 'span',
15145                 cls: 'roo-combobox-list-group-item-value'
15146             },
15147             {
15148                 tag: 'div',
15149                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15150                 cn: [
15151                     {
15152                         tag: 'input',
15153                         type: 'radio'
15154                     },
15155                     {
15156                         tag: 'label'
15157                     }
15158                 ]
15159             }
15160         ]
15161     },
15162     
15163     listItemCheckbox : {
15164         tag: 'li',
15165         cls: 'list-group-item',
15166         cn: [
15167             {
15168                 tag: 'span',
15169                 cls: 'roo-combobox-list-group-item-value'
15170             },
15171             {
15172                 tag: 'div',
15173                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15174                 cn: [
15175                     {
15176                         tag: 'input',
15177                         type: 'checkbox'
15178                     },
15179                     {
15180                         tag: 'label'
15181                     }
15182                 ]
15183             }
15184         ]
15185     },
15186     
15187     emptyResult : {
15188         tag: 'div',
15189         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15190     },
15191     
15192     footer : {
15193         tag: 'div',
15194         cls: 'modal-footer',
15195         cn: [
15196             {
15197                 tag: 'div',
15198                 cls: 'row',
15199                 cn: [
15200                     {
15201                         tag: 'div',
15202                         cls: 'col-xs-6 text-left',
15203                         cn: {
15204                             tag: 'button',
15205                             cls: 'btn btn-danger roo-touch-view-cancel',
15206                             html: 'Cancel'
15207                         }
15208                     },
15209                     {
15210                         tag: 'div',
15211                         cls: 'col-xs-6 text-right',
15212                         cn: {
15213                             tag: 'button',
15214                             cls: 'btn btn-success roo-touch-view-ok',
15215                             html: 'OK'
15216                         }
15217                     }
15218                 ]
15219             }
15220         ]
15221         
15222     }
15223 });
15224
15225 Roo.apply(Roo.bootstrap.ComboBox,  {
15226     
15227     touchViewTemplate : {
15228         tag: 'div',
15229         cls: 'modal fade roo-combobox-touch-view',
15230         cn: [
15231             {
15232                 tag: 'div',
15233                 cls: 'modal-dialog',
15234                 style : 'position:fixed', // we have to fix position....
15235                 cn: [
15236                     {
15237                         tag: 'div',
15238                         cls: 'modal-content',
15239                         cn: [
15240                             Roo.bootstrap.ComboBox.header,
15241                             Roo.bootstrap.ComboBox.body,
15242                             Roo.bootstrap.ComboBox.footer
15243                         ]
15244                     }
15245                 ]
15246             }
15247         ]
15248     }
15249 });/*
15250  * Based on:
15251  * Ext JS Library 1.1.1
15252  * Copyright(c) 2006-2007, Ext JS, LLC.
15253  *
15254  * Originally Released Under LGPL - original licence link has changed is not relivant.
15255  *
15256  * Fork - LGPL
15257  * <script type="text/javascript">
15258  */
15259
15260 /**
15261  * @class Roo.View
15262  * @extends Roo.util.Observable
15263  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15264  * This class also supports single and multi selection modes. <br>
15265  * Create a data model bound view:
15266  <pre><code>
15267  var store = new Roo.data.Store(...);
15268
15269  var view = new Roo.View({
15270     el : "my-element",
15271     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15272  
15273     singleSelect: true,
15274     selectedClass: "ydataview-selected",
15275     store: store
15276  });
15277
15278  // listen for node click?
15279  view.on("click", function(vw, index, node, e){
15280  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15281  });
15282
15283  // load XML data
15284  dataModel.load("foobar.xml");
15285  </code></pre>
15286  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15287  * <br><br>
15288  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15289  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15290  * 
15291  * Note: old style constructor is still suported (container, template, config)
15292  * 
15293  * @constructor
15294  * Create a new View
15295  * @param {Object} config The config object
15296  * 
15297  */
15298 Roo.View = function(config, depreciated_tpl, depreciated_config){
15299     
15300     this.parent = false;
15301     
15302     if (typeof(depreciated_tpl) == 'undefined') {
15303         // new way.. - universal constructor.
15304         Roo.apply(this, config);
15305         this.el  = Roo.get(this.el);
15306     } else {
15307         // old format..
15308         this.el  = Roo.get(config);
15309         this.tpl = depreciated_tpl;
15310         Roo.apply(this, depreciated_config);
15311     }
15312     this.wrapEl  = this.el.wrap().wrap();
15313     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15314     
15315     
15316     if(typeof(this.tpl) == "string"){
15317         this.tpl = new Roo.Template(this.tpl);
15318     } else {
15319         // support xtype ctors..
15320         this.tpl = new Roo.factory(this.tpl, Roo);
15321     }
15322     
15323     
15324     this.tpl.compile();
15325     
15326     /** @private */
15327     this.addEvents({
15328         /**
15329          * @event beforeclick
15330          * Fires before a click is processed. Returns false to cancel the default action.
15331          * @param {Roo.View} this
15332          * @param {Number} index The index of the target node
15333          * @param {HTMLElement} node The target node
15334          * @param {Roo.EventObject} e The raw event object
15335          */
15336             "beforeclick" : true,
15337         /**
15338          * @event click
15339          * Fires when a template node is clicked.
15340          * @param {Roo.View} this
15341          * @param {Number} index The index of the target node
15342          * @param {HTMLElement} node The target node
15343          * @param {Roo.EventObject} e The raw event object
15344          */
15345             "click" : true,
15346         /**
15347          * @event dblclick
15348          * Fires when a template node is double clicked.
15349          * @param {Roo.View} this
15350          * @param {Number} index The index of the target node
15351          * @param {HTMLElement} node The target node
15352          * @param {Roo.EventObject} e The raw event object
15353          */
15354             "dblclick" : true,
15355         /**
15356          * @event contextmenu
15357          * Fires when a template node is right clicked.
15358          * @param {Roo.View} this
15359          * @param {Number} index The index of the target node
15360          * @param {HTMLElement} node The target node
15361          * @param {Roo.EventObject} e The raw event object
15362          */
15363             "contextmenu" : true,
15364         /**
15365          * @event selectionchange
15366          * Fires when the selected nodes change.
15367          * @param {Roo.View} this
15368          * @param {Array} selections Array of the selected nodes
15369          */
15370             "selectionchange" : true,
15371     
15372         /**
15373          * @event beforeselect
15374          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15375          * @param {Roo.View} this
15376          * @param {HTMLElement} node The node to be selected
15377          * @param {Array} selections Array of currently selected nodes
15378          */
15379             "beforeselect" : true,
15380         /**
15381          * @event preparedata
15382          * Fires on every row to render, to allow you to change the data.
15383          * @param {Roo.View} this
15384          * @param {Object} data to be rendered (change this)
15385          */
15386           "preparedata" : true
15387           
15388           
15389         });
15390
15391
15392
15393     this.el.on({
15394         "click": this.onClick,
15395         "dblclick": this.onDblClick,
15396         "contextmenu": this.onContextMenu,
15397         scope:this
15398     });
15399
15400     this.selections = [];
15401     this.nodes = [];
15402     this.cmp = new Roo.CompositeElementLite([]);
15403     if(this.store){
15404         this.store = Roo.factory(this.store, Roo.data);
15405         this.setStore(this.store, true);
15406     }
15407     
15408     if ( this.footer && this.footer.xtype) {
15409            
15410          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15411         
15412         this.footer.dataSource = this.store;
15413         this.footer.container = fctr;
15414         this.footer = Roo.factory(this.footer, Roo);
15415         fctr.insertFirst(this.el);
15416         
15417         // this is a bit insane - as the paging toolbar seems to detach the el..
15418 //        dom.parentNode.parentNode.parentNode
15419          // they get detached?
15420     }
15421     
15422     
15423     Roo.View.superclass.constructor.call(this);
15424     
15425     
15426 };
15427
15428 Roo.extend(Roo.View, Roo.util.Observable, {
15429     
15430      /**
15431      * @cfg {Roo.data.Store} store Data store to load data from.
15432      */
15433     store : false,
15434     
15435     /**
15436      * @cfg {String|Roo.Element} el The container element.
15437      */
15438     el : '',
15439     
15440     /**
15441      * @cfg {String|Roo.Template} tpl The template used by this View 
15442      */
15443     tpl : false,
15444     /**
15445      * @cfg {String} dataName the named area of the template to use as the data area
15446      *                          Works with domtemplates roo-name="name"
15447      */
15448     dataName: false,
15449     /**
15450      * @cfg {String} selectedClass The css class to add to selected nodes
15451      */
15452     selectedClass : "x-view-selected",
15453      /**
15454      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15455      */
15456     emptyText : "",
15457     
15458     /**
15459      * @cfg {String} text to display on mask (default Loading)
15460      */
15461     mask : false,
15462     /**
15463      * @cfg {Boolean} multiSelect Allow multiple selection
15464      */
15465     multiSelect : false,
15466     /**
15467      * @cfg {Boolean} singleSelect Allow single selection
15468      */
15469     singleSelect:  false,
15470     
15471     /**
15472      * @cfg {Boolean} toggleSelect - selecting 
15473      */
15474     toggleSelect : false,
15475     
15476     /**
15477      * @cfg {Boolean} tickable - selecting 
15478      */
15479     tickable : false,
15480     
15481     /**
15482      * Returns the element this view is bound to.
15483      * @return {Roo.Element}
15484      */
15485     getEl : function(){
15486         return this.wrapEl;
15487     },
15488     
15489     
15490
15491     /**
15492      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15493      */
15494     refresh : function(){
15495         //Roo.log('refresh');
15496         var t = this.tpl;
15497         
15498         // if we are using something like 'domtemplate', then
15499         // the what gets used is:
15500         // t.applySubtemplate(NAME, data, wrapping data..)
15501         // the outer template then get' applied with
15502         //     the store 'extra data'
15503         // and the body get's added to the
15504         //      roo-name="data" node?
15505         //      <span class='roo-tpl-{name}'></span> ?????
15506         
15507         
15508         
15509         this.clearSelections();
15510         this.el.update("");
15511         var html = [];
15512         var records = this.store.getRange();
15513         if(records.length < 1) {
15514             
15515             // is this valid??  = should it render a template??
15516             
15517             this.el.update(this.emptyText);
15518             return;
15519         }
15520         var el = this.el;
15521         if (this.dataName) {
15522             this.el.update(t.apply(this.store.meta)); //????
15523             el = this.el.child('.roo-tpl-' + this.dataName);
15524         }
15525         
15526         for(var i = 0, len = records.length; i < len; i++){
15527             var data = this.prepareData(records[i].data, i, records[i]);
15528             this.fireEvent("preparedata", this, data, i, records[i]);
15529             
15530             var d = Roo.apply({}, data);
15531             
15532             if(this.tickable){
15533                 Roo.apply(d, {'roo-id' : Roo.id()});
15534                 
15535                 var _this = this;
15536             
15537                 Roo.each(this.parent.item, function(item){
15538                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15539                         return;
15540                     }
15541                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15542                 });
15543             }
15544             
15545             html[html.length] = Roo.util.Format.trim(
15546                 this.dataName ?
15547                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15548                     t.apply(d)
15549             );
15550         }
15551         
15552         
15553         
15554         el.update(html.join(""));
15555         this.nodes = el.dom.childNodes;
15556         this.updateIndexes(0);
15557     },
15558     
15559
15560     /**
15561      * Function to override to reformat the data that is sent to
15562      * the template for each node.
15563      * DEPRICATED - use the preparedata event handler.
15564      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15565      * a JSON object for an UpdateManager bound view).
15566      */
15567     prepareData : function(data, index, record)
15568     {
15569         this.fireEvent("preparedata", this, data, index, record);
15570         return data;
15571     },
15572
15573     onUpdate : function(ds, record){
15574         // Roo.log('on update');   
15575         this.clearSelections();
15576         var index = this.store.indexOf(record);
15577         var n = this.nodes[index];
15578         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15579         n.parentNode.removeChild(n);
15580         this.updateIndexes(index, index);
15581     },
15582
15583     
15584     
15585 // --------- FIXME     
15586     onAdd : function(ds, records, index)
15587     {
15588         //Roo.log(['on Add', ds, records, index] );        
15589         this.clearSelections();
15590         if(this.nodes.length == 0){
15591             this.refresh();
15592             return;
15593         }
15594         var n = this.nodes[index];
15595         for(var i = 0, len = records.length; i < len; i++){
15596             var d = this.prepareData(records[i].data, i, records[i]);
15597             if(n){
15598                 this.tpl.insertBefore(n, d);
15599             }else{
15600                 
15601                 this.tpl.append(this.el, d);
15602             }
15603         }
15604         this.updateIndexes(index);
15605     },
15606
15607     onRemove : function(ds, record, index){
15608        // Roo.log('onRemove');
15609         this.clearSelections();
15610         var el = this.dataName  ?
15611             this.el.child('.roo-tpl-' + this.dataName) :
15612             this.el; 
15613         
15614         el.dom.removeChild(this.nodes[index]);
15615         this.updateIndexes(index);
15616     },
15617
15618     /**
15619      * Refresh an individual node.
15620      * @param {Number} index
15621      */
15622     refreshNode : function(index){
15623         this.onUpdate(this.store, this.store.getAt(index));
15624     },
15625
15626     updateIndexes : function(startIndex, endIndex){
15627         var ns = this.nodes;
15628         startIndex = startIndex || 0;
15629         endIndex = endIndex || ns.length - 1;
15630         for(var i = startIndex; i <= endIndex; i++){
15631             ns[i].nodeIndex = i;
15632         }
15633     },
15634
15635     /**
15636      * Changes the data store this view uses and refresh the view.
15637      * @param {Store} store
15638      */
15639     setStore : function(store, initial){
15640         if(!initial && this.store){
15641             this.store.un("datachanged", this.refresh);
15642             this.store.un("add", this.onAdd);
15643             this.store.un("remove", this.onRemove);
15644             this.store.un("update", this.onUpdate);
15645             this.store.un("clear", this.refresh);
15646             this.store.un("beforeload", this.onBeforeLoad);
15647             this.store.un("load", this.onLoad);
15648             this.store.un("loadexception", this.onLoad);
15649         }
15650         if(store){
15651           
15652             store.on("datachanged", this.refresh, this);
15653             store.on("add", this.onAdd, this);
15654             store.on("remove", this.onRemove, this);
15655             store.on("update", this.onUpdate, this);
15656             store.on("clear", this.refresh, this);
15657             store.on("beforeload", this.onBeforeLoad, this);
15658             store.on("load", this.onLoad, this);
15659             store.on("loadexception", this.onLoad, this);
15660         }
15661         
15662         if(store){
15663             this.refresh();
15664         }
15665     },
15666     /**
15667      * onbeforeLoad - masks the loading area.
15668      *
15669      */
15670     onBeforeLoad : function(store,opts)
15671     {
15672          //Roo.log('onBeforeLoad');   
15673         if (!opts.add) {
15674             this.el.update("");
15675         }
15676         this.el.mask(this.mask ? this.mask : "Loading" ); 
15677     },
15678     onLoad : function ()
15679     {
15680         this.el.unmask();
15681     },
15682     
15683
15684     /**
15685      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15686      * @param {HTMLElement} node
15687      * @return {HTMLElement} The template node
15688      */
15689     findItemFromChild : function(node){
15690         var el = this.dataName  ?
15691             this.el.child('.roo-tpl-' + this.dataName,true) :
15692             this.el.dom; 
15693         
15694         if(!node || node.parentNode == el){
15695                     return node;
15696             }
15697             var p = node.parentNode;
15698             while(p && p != el){
15699             if(p.parentNode == el){
15700                 return p;
15701             }
15702             p = p.parentNode;
15703         }
15704             return null;
15705     },
15706
15707     /** @ignore */
15708     onClick : function(e){
15709         var item = this.findItemFromChild(e.getTarget());
15710         if(item){
15711             var index = this.indexOf(item);
15712             if(this.onItemClick(item, index, e) !== false){
15713                 this.fireEvent("click", this, index, item, e);
15714             }
15715         }else{
15716             this.clearSelections();
15717         }
15718     },
15719
15720     /** @ignore */
15721     onContextMenu : function(e){
15722         var item = this.findItemFromChild(e.getTarget());
15723         if(item){
15724             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15725         }
15726     },
15727
15728     /** @ignore */
15729     onDblClick : function(e){
15730         var item = this.findItemFromChild(e.getTarget());
15731         if(item){
15732             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15733         }
15734     },
15735
15736     onItemClick : function(item, index, e)
15737     {
15738         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15739             return false;
15740         }
15741         if (this.toggleSelect) {
15742             var m = this.isSelected(item) ? 'unselect' : 'select';
15743             //Roo.log(m);
15744             var _t = this;
15745             _t[m](item, true, false);
15746             return true;
15747         }
15748         if(this.multiSelect || this.singleSelect){
15749             if(this.multiSelect && e.shiftKey && this.lastSelection){
15750                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15751             }else{
15752                 this.select(item, this.multiSelect && e.ctrlKey);
15753                 this.lastSelection = item;
15754             }
15755             
15756             if(!this.tickable){
15757                 e.preventDefault();
15758             }
15759             
15760         }
15761         return true;
15762     },
15763
15764     /**
15765      * Get the number of selected nodes.
15766      * @return {Number}
15767      */
15768     getSelectionCount : function(){
15769         return this.selections.length;
15770     },
15771
15772     /**
15773      * Get the currently selected nodes.
15774      * @return {Array} An array of HTMLElements
15775      */
15776     getSelectedNodes : function(){
15777         return this.selections;
15778     },
15779
15780     /**
15781      * Get the indexes of the selected nodes.
15782      * @return {Array}
15783      */
15784     getSelectedIndexes : function(){
15785         var indexes = [], s = this.selections;
15786         for(var i = 0, len = s.length; i < len; i++){
15787             indexes.push(s[i].nodeIndex);
15788         }
15789         return indexes;
15790     },
15791
15792     /**
15793      * Clear all selections
15794      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15795      */
15796     clearSelections : function(suppressEvent){
15797         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15798             this.cmp.elements = this.selections;
15799             this.cmp.removeClass(this.selectedClass);
15800             this.selections = [];
15801             if(!suppressEvent){
15802                 this.fireEvent("selectionchange", this, this.selections);
15803             }
15804         }
15805     },
15806
15807     /**
15808      * Returns true if the passed node is selected
15809      * @param {HTMLElement/Number} node The node or node index
15810      * @return {Boolean}
15811      */
15812     isSelected : function(node){
15813         var s = this.selections;
15814         if(s.length < 1){
15815             return false;
15816         }
15817         node = this.getNode(node);
15818         return s.indexOf(node) !== -1;
15819     },
15820
15821     /**
15822      * Selects nodes.
15823      * @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
15824      * @param {Boolean} keepExisting (optional) true to keep existing selections
15825      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15826      */
15827     select : function(nodeInfo, keepExisting, suppressEvent){
15828         if(nodeInfo instanceof Array){
15829             if(!keepExisting){
15830                 this.clearSelections(true);
15831             }
15832             for(var i = 0, len = nodeInfo.length; i < len; i++){
15833                 this.select(nodeInfo[i], true, true);
15834             }
15835             return;
15836         } 
15837         var node = this.getNode(nodeInfo);
15838         if(!node || this.isSelected(node)){
15839             return; // already selected.
15840         }
15841         if(!keepExisting){
15842             this.clearSelections(true);
15843         }
15844         
15845         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15846             Roo.fly(node).addClass(this.selectedClass);
15847             this.selections.push(node);
15848             if(!suppressEvent){
15849                 this.fireEvent("selectionchange", this, this.selections);
15850             }
15851         }
15852         
15853         
15854     },
15855       /**
15856      * Unselects nodes.
15857      * @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
15858      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15859      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15860      */
15861     unselect : function(nodeInfo, keepExisting, suppressEvent)
15862     {
15863         if(nodeInfo instanceof Array){
15864             Roo.each(this.selections, function(s) {
15865                 this.unselect(s, nodeInfo);
15866             }, this);
15867             return;
15868         }
15869         var node = this.getNode(nodeInfo);
15870         if(!node || !this.isSelected(node)){
15871             //Roo.log("not selected");
15872             return; // not selected.
15873         }
15874         // fireevent???
15875         var ns = [];
15876         Roo.each(this.selections, function(s) {
15877             if (s == node ) {
15878                 Roo.fly(node).removeClass(this.selectedClass);
15879
15880                 return;
15881             }
15882             ns.push(s);
15883         },this);
15884         
15885         this.selections= ns;
15886         this.fireEvent("selectionchange", this, this.selections);
15887     },
15888
15889     /**
15890      * Gets a template node.
15891      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15892      * @return {HTMLElement} The node or null if it wasn't found
15893      */
15894     getNode : function(nodeInfo){
15895         if(typeof nodeInfo == "string"){
15896             return document.getElementById(nodeInfo);
15897         }else if(typeof nodeInfo == "number"){
15898             return this.nodes[nodeInfo];
15899         }
15900         return nodeInfo;
15901     },
15902
15903     /**
15904      * Gets a range template nodes.
15905      * @param {Number} startIndex
15906      * @param {Number} endIndex
15907      * @return {Array} An array of nodes
15908      */
15909     getNodes : function(start, end){
15910         var ns = this.nodes;
15911         start = start || 0;
15912         end = typeof end == "undefined" ? ns.length - 1 : end;
15913         var nodes = [];
15914         if(start <= end){
15915             for(var i = start; i <= end; i++){
15916                 nodes.push(ns[i]);
15917             }
15918         } else{
15919             for(var i = start; i >= end; i--){
15920                 nodes.push(ns[i]);
15921             }
15922         }
15923         return nodes;
15924     },
15925
15926     /**
15927      * Finds the index of the passed node
15928      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15929      * @return {Number} The index of the node or -1
15930      */
15931     indexOf : function(node){
15932         node = this.getNode(node);
15933         if(typeof node.nodeIndex == "number"){
15934             return node.nodeIndex;
15935         }
15936         var ns = this.nodes;
15937         for(var i = 0, len = ns.length; i < len; i++){
15938             if(ns[i] == node){
15939                 return i;
15940             }
15941         }
15942         return -1;
15943     }
15944 });
15945 /*
15946  * - LGPL
15947  *
15948  * based on jquery fullcalendar
15949  * 
15950  */
15951
15952 Roo.bootstrap = Roo.bootstrap || {};
15953 /**
15954  * @class Roo.bootstrap.Calendar
15955  * @extends Roo.bootstrap.Component
15956  * Bootstrap Calendar class
15957  * @cfg {Boolean} loadMask (true|false) default false
15958  * @cfg {Object} header generate the user specific header of the calendar, default false
15959
15960  * @constructor
15961  * Create a new Container
15962  * @param {Object} config The config object
15963  */
15964
15965
15966
15967 Roo.bootstrap.Calendar = function(config){
15968     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15969      this.addEvents({
15970         /**
15971              * @event select
15972              * Fires when a date is selected
15973              * @param {DatePicker} this
15974              * @param {Date} date The selected date
15975              */
15976         'select': true,
15977         /**
15978              * @event monthchange
15979              * Fires when the displayed month changes 
15980              * @param {DatePicker} this
15981              * @param {Date} date The selected month
15982              */
15983         'monthchange': true,
15984         /**
15985              * @event evententer
15986              * Fires when mouse over an event
15987              * @param {Calendar} this
15988              * @param {event} Event
15989              */
15990         'evententer': true,
15991         /**
15992              * @event eventleave
15993              * Fires when the mouse leaves an
15994              * @param {Calendar} this
15995              * @param {event}
15996              */
15997         'eventleave': true,
15998         /**
15999              * @event eventclick
16000              * Fires when the mouse click an
16001              * @param {Calendar} this
16002              * @param {event}
16003              */
16004         'eventclick': true
16005         
16006     });
16007
16008 };
16009
16010 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16011     
16012      /**
16013      * @cfg {Number} startDay
16014      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16015      */
16016     startDay : 0,
16017     
16018     loadMask : false,
16019     
16020     header : false,
16021       
16022     getAutoCreate : function(){
16023         
16024         
16025         var fc_button = function(name, corner, style, content ) {
16026             return Roo.apply({},{
16027                 tag : 'span',
16028                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16029                          (corner.length ?
16030                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16031                             ''
16032                         ),
16033                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16034                 unselectable: 'on'
16035             });
16036         };
16037         
16038         var header = {};
16039         
16040         if(!this.header){
16041             header = {
16042                 tag : 'table',
16043                 cls : 'fc-header',
16044                 style : 'width:100%',
16045                 cn : [
16046                     {
16047                         tag: 'tr',
16048                         cn : [
16049                             {
16050                                 tag : 'td',
16051                                 cls : 'fc-header-left',
16052                                 cn : [
16053                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16054                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16055                                     { tag: 'span', cls: 'fc-header-space' },
16056                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16057
16058
16059                                 ]
16060                             },
16061
16062                             {
16063                                 tag : 'td',
16064                                 cls : 'fc-header-center',
16065                                 cn : [
16066                                     {
16067                                         tag: 'span',
16068                                         cls: 'fc-header-title',
16069                                         cn : {
16070                                             tag: 'H2',
16071                                             html : 'month / year'
16072                                         }
16073                                     }
16074
16075                                 ]
16076                             },
16077                             {
16078                                 tag : 'td',
16079                                 cls : 'fc-header-right',
16080                                 cn : [
16081                               /*      fc_button('month', 'left', '', 'month' ),
16082                                     fc_button('week', '', '', 'week' ),
16083                                     fc_button('day', 'right', '', 'day' )
16084                                 */    
16085
16086                                 ]
16087                             }
16088
16089                         ]
16090                     }
16091                 ]
16092             };
16093         }
16094         
16095         header = this.header;
16096         
16097        
16098         var cal_heads = function() {
16099             var ret = [];
16100             // fixme - handle this.
16101             
16102             for (var i =0; i < Date.dayNames.length; i++) {
16103                 var d = Date.dayNames[i];
16104                 ret.push({
16105                     tag: 'th',
16106                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16107                     html : d.substring(0,3)
16108                 });
16109                 
16110             }
16111             ret[0].cls += ' fc-first';
16112             ret[6].cls += ' fc-last';
16113             return ret;
16114         };
16115         var cal_cell = function(n) {
16116             return  {
16117                 tag: 'td',
16118                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16119                 cn : [
16120                     {
16121                         cn : [
16122                             {
16123                                 cls: 'fc-day-number',
16124                                 html: 'D'
16125                             },
16126                             {
16127                                 cls: 'fc-day-content',
16128                              
16129                                 cn : [
16130                                      {
16131                                         style: 'position: relative;' // height: 17px;
16132                                     }
16133                                 ]
16134                             }
16135                             
16136                             
16137                         ]
16138                     }
16139                 ]
16140                 
16141             }
16142         };
16143         var cal_rows = function() {
16144             
16145             var ret = [];
16146             for (var r = 0; r < 6; r++) {
16147                 var row= {
16148                     tag : 'tr',
16149                     cls : 'fc-week',
16150                     cn : []
16151                 };
16152                 
16153                 for (var i =0; i < Date.dayNames.length; i++) {
16154                     var d = Date.dayNames[i];
16155                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16156
16157                 }
16158                 row.cn[0].cls+=' fc-first';
16159                 row.cn[0].cn[0].style = 'min-height:90px';
16160                 row.cn[6].cls+=' fc-last';
16161                 ret.push(row);
16162                 
16163             }
16164             ret[0].cls += ' fc-first';
16165             ret[4].cls += ' fc-prev-last';
16166             ret[5].cls += ' fc-last';
16167             return ret;
16168             
16169         };
16170         
16171         var cal_table = {
16172             tag: 'table',
16173             cls: 'fc-border-separate',
16174             style : 'width:100%',
16175             cellspacing  : 0,
16176             cn : [
16177                 { 
16178                     tag: 'thead',
16179                     cn : [
16180                         { 
16181                             tag: 'tr',
16182                             cls : 'fc-first fc-last',
16183                             cn : cal_heads()
16184                         }
16185                     ]
16186                 },
16187                 { 
16188                     tag: 'tbody',
16189                     cn : cal_rows()
16190                 }
16191                   
16192             ]
16193         };
16194          
16195          var cfg = {
16196             cls : 'fc fc-ltr',
16197             cn : [
16198                 header,
16199                 {
16200                     cls : 'fc-content',
16201                     style : "position: relative;",
16202                     cn : [
16203                         {
16204                             cls : 'fc-view fc-view-month fc-grid',
16205                             style : 'position: relative',
16206                             unselectable : 'on',
16207                             cn : [
16208                                 {
16209                                     cls : 'fc-event-container',
16210                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16211                                 },
16212                                 cal_table
16213                             ]
16214                         }
16215                     ]
16216     
16217                 }
16218            ] 
16219             
16220         };
16221         
16222          
16223         
16224         return cfg;
16225     },
16226     
16227     
16228     initEvents : function()
16229     {
16230         if(!this.store){
16231             throw "can not find store for calendar";
16232         }
16233         
16234         var mark = {
16235             tag: "div",
16236             cls:"x-dlg-mask",
16237             style: "text-align:center",
16238             cn: [
16239                 {
16240                     tag: "div",
16241                     style: "background-color:white;width:50%;margin:250 auto",
16242                     cn: [
16243                         {
16244                             tag: "img",
16245                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16246                         },
16247                         {
16248                             tag: "span",
16249                             html: "Loading"
16250                         }
16251                         
16252                     ]
16253                 }
16254             ]
16255         };
16256         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16257         
16258         var size = this.el.select('.fc-content', true).first().getSize();
16259         this.maskEl.setSize(size.width, size.height);
16260         this.maskEl.enableDisplayMode("block");
16261         if(!this.loadMask){
16262             this.maskEl.hide();
16263         }
16264         
16265         this.store = Roo.factory(this.store, Roo.data);
16266         this.store.on('load', this.onLoad, this);
16267         this.store.on('beforeload', this.onBeforeLoad, this);
16268         
16269         this.resize();
16270         
16271         this.cells = this.el.select('.fc-day',true);
16272         //Roo.log(this.cells);
16273         this.textNodes = this.el.query('.fc-day-number');
16274         this.cells.addClassOnOver('fc-state-hover');
16275         
16276         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16277         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16278         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16279         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16280         
16281         this.on('monthchange', this.onMonthChange, this);
16282         
16283         this.update(new Date().clearTime());
16284     },
16285     
16286     resize : function() {
16287         var sz  = this.el.getSize();
16288         
16289         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16290         this.el.select('.fc-day-content div',true).setHeight(34);
16291     },
16292     
16293     
16294     // private
16295     showPrevMonth : function(e){
16296         this.update(this.activeDate.add("mo", -1));
16297     },
16298     showToday : function(e){
16299         this.update(new Date().clearTime());
16300     },
16301     // private
16302     showNextMonth : function(e){
16303         this.update(this.activeDate.add("mo", 1));
16304     },
16305
16306     // private
16307     showPrevYear : function(){
16308         this.update(this.activeDate.add("y", -1));
16309     },
16310
16311     // private
16312     showNextYear : function(){
16313         this.update(this.activeDate.add("y", 1));
16314     },
16315
16316     
16317    // private
16318     update : function(date)
16319     {
16320         var vd = this.activeDate;
16321         this.activeDate = date;
16322 //        if(vd && this.el){
16323 //            var t = date.getTime();
16324 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16325 //                Roo.log('using add remove');
16326 //                
16327 //                this.fireEvent('monthchange', this, date);
16328 //                
16329 //                this.cells.removeClass("fc-state-highlight");
16330 //                this.cells.each(function(c){
16331 //                   if(c.dateValue == t){
16332 //                       c.addClass("fc-state-highlight");
16333 //                       setTimeout(function(){
16334 //                            try{c.dom.firstChild.focus();}catch(e){}
16335 //                       }, 50);
16336 //                       return false;
16337 //                   }
16338 //                   return true;
16339 //                });
16340 //                return;
16341 //            }
16342 //        }
16343         
16344         var days = date.getDaysInMonth();
16345         
16346         var firstOfMonth = date.getFirstDateOfMonth();
16347         var startingPos = firstOfMonth.getDay()-this.startDay;
16348         
16349         if(startingPos < this.startDay){
16350             startingPos += 7;
16351         }
16352         
16353         var pm = date.add(Date.MONTH, -1);
16354         var prevStart = pm.getDaysInMonth()-startingPos;
16355 //        
16356         this.cells = this.el.select('.fc-day',true);
16357         this.textNodes = this.el.query('.fc-day-number');
16358         this.cells.addClassOnOver('fc-state-hover');
16359         
16360         var cells = this.cells.elements;
16361         var textEls = this.textNodes;
16362         
16363         Roo.each(cells, function(cell){
16364             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16365         });
16366         
16367         days += startingPos;
16368
16369         // convert everything to numbers so it's fast
16370         var day = 86400000;
16371         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16372         //Roo.log(d);
16373         //Roo.log(pm);
16374         //Roo.log(prevStart);
16375         
16376         var today = new Date().clearTime().getTime();
16377         var sel = date.clearTime().getTime();
16378         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16379         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16380         var ddMatch = this.disabledDatesRE;
16381         var ddText = this.disabledDatesText;
16382         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16383         var ddaysText = this.disabledDaysText;
16384         var format = this.format;
16385         
16386         var setCellClass = function(cal, cell){
16387             cell.row = 0;
16388             cell.events = [];
16389             cell.more = [];
16390             //Roo.log('set Cell Class');
16391             cell.title = "";
16392             var t = d.getTime();
16393             
16394             //Roo.log(d);
16395             
16396             cell.dateValue = t;
16397             if(t == today){
16398                 cell.className += " fc-today";
16399                 cell.className += " fc-state-highlight";
16400                 cell.title = cal.todayText;
16401             }
16402             if(t == sel){
16403                 // disable highlight in other month..
16404                 //cell.className += " fc-state-highlight";
16405                 
16406             }
16407             // disabling
16408             if(t < min) {
16409                 cell.className = " fc-state-disabled";
16410                 cell.title = cal.minText;
16411                 return;
16412             }
16413             if(t > max) {
16414                 cell.className = " fc-state-disabled";
16415                 cell.title = cal.maxText;
16416                 return;
16417             }
16418             if(ddays){
16419                 if(ddays.indexOf(d.getDay()) != -1){
16420                     cell.title = ddaysText;
16421                     cell.className = " fc-state-disabled";
16422                 }
16423             }
16424             if(ddMatch && format){
16425                 var fvalue = d.dateFormat(format);
16426                 if(ddMatch.test(fvalue)){
16427                     cell.title = ddText.replace("%0", fvalue);
16428                     cell.className = " fc-state-disabled";
16429                 }
16430             }
16431             
16432             if (!cell.initialClassName) {
16433                 cell.initialClassName = cell.dom.className;
16434             }
16435             
16436             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16437         };
16438
16439         var i = 0;
16440         
16441         for(; i < startingPos; i++) {
16442             textEls[i].innerHTML = (++prevStart);
16443             d.setDate(d.getDate()+1);
16444             
16445             cells[i].className = "fc-past fc-other-month";
16446             setCellClass(this, cells[i]);
16447         }
16448         
16449         var intDay = 0;
16450         
16451         for(; i < days; i++){
16452             intDay = i - startingPos + 1;
16453             textEls[i].innerHTML = (intDay);
16454             d.setDate(d.getDate()+1);
16455             
16456             cells[i].className = ''; // "x-date-active";
16457             setCellClass(this, cells[i]);
16458         }
16459         var extraDays = 0;
16460         
16461         for(; i < 42; i++) {
16462             textEls[i].innerHTML = (++extraDays);
16463             d.setDate(d.getDate()+1);
16464             
16465             cells[i].className = "fc-future fc-other-month";
16466             setCellClass(this, cells[i]);
16467         }
16468         
16469         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16470         
16471         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16472         
16473         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16474         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16475         
16476         if(totalRows != 6){
16477             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16478             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16479         }
16480         
16481         this.fireEvent('monthchange', this, date);
16482         
16483         
16484         /*
16485         if(!this.internalRender){
16486             var main = this.el.dom.firstChild;
16487             var w = main.offsetWidth;
16488             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16489             Roo.fly(main).setWidth(w);
16490             this.internalRender = true;
16491             // opera does not respect the auto grow header center column
16492             // then, after it gets a width opera refuses to recalculate
16493             // without a second pass
16494             if(Roo.isOpera && !this.secondPass){
16495                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16496                 this.secondPass = true;
16497                 this.update.defer(10, this, [date]);
16498             }
16499         }
16500         */
16501         
16502     },
16503     
16504     findCell : function(dt) {
16505         dt = dt.clearTime().getTime();
16506         var ret = false;
16507         this.cells.each(function(c){
16508             //Roo.log("check " +c.dateValue + '?=' + dt);
16509             if(c.dateValue == dt){
16510                 ret = c;
16511                 return false;
16512             }
16513             return true;
16514         });
16515         
16516         return ret;
16517     },
16518     
16519     findCells : function(ev) {
16520         var s = ev.start.clone().clearTime().getTime();
16521        // Roo.log(s);
16522         var e= ev.end.clone().clearTime().getTime();
16523        // Roo.log(e);
16524         var ret = [];
16525         this.cells.each(function(c){
16526              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16527             
16528             if(c.dateValue > e){
16529                 return ;
16530             }
16531             if(c.dateValue < s){
16532                 return ;
16533             }
16534             ret.push(c);
16535         });
16536         
16537         return ret;    
16538     },
16539     
16540 //    findBestRow: function(cells)
16541 //    {
16542 //        var ret = 0;
16543 //        
16544 //        for (var i =0 ; i < cells.length;i++) {
16545 //            ret  = Math.max(cells[i].rows || 0,ret);
16546 //        }
16547 //        return ret;
16548 //        
16549 //    },
16550     
16551     
16552     addItem : function(ev)
16553     {
16554         // look for vertical location slot in
16555         var cells = this.findCells(ev);
16556         
16557 //        ev.row = this.findBestRow(cells);
16558         
16559         // work out the location.
16560         
16561         var crow = false;
16562         var rows = [];
16563         for(var i =0; i < cells.length; i++) {
16564             
16565             cells[i].row = cells[0].row;
16566             
16567             if(i == 0){
16568                 cells[i].row = cells[i].row + 1;
16569             }
16570             
16571             if (!crow) {
16572                 crow = {
16573                     start : cells[i],
16574                     end :  cells[i]
16575                 };
16576                 continue;
16577             }
16578             if (crow.start.getY() == cells[i].getY()) {
16579                 // on same row.
16580                 crow.end = cells[i];
16581                 continue;
16582             }
16583             // different row.
16584             rows.push(crow);
16585             crow = {
16586                 start: cells[i],
16587                 end : cells[i]
16588             };
16589             
16590         }
16591         
16592         rows.push(crow);
16593         ev.els = [];
16594         ev.rows = rows;
16595         ev.cells = cells;
16596         
16597         cells[0].events.push(ev);
16598         
16599         this.calevents.push(ev);
16600     },
16601     
16602     clearEvents: function() {
16603         
16604         if(!this.calevents){
16605             return;
16606         }
16607         
16608         Roo.each(this.cells.elements, function(c){
16609             c.row = 0;
16610             c.events = [];
16611             c.more = [];
16612         });
16613         
16614         Roo.each(this.calevents, function(e) {
16615             Roo.each(e.els, function(el) {
16616                 el.un('mouseenter' ,this.onEventEnter, this);
16617                 el.un('mouseleave' ,this.onEventLeave, this);
16618                 el.remove();
16619             },this);
16620         },this);
16621         
16622         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16623             e.remove();
16624         });
16625         
16626     },
16627     
16628     renderEvents: function()
16629     {   
16630         var _this = this;
16631         
16632         this.cells.each(function(c) {
16633             
16634             if(c.row < 5){
16635                 return;
16636             }
16637             
16638             var ev = c.events;
16639             
16640             var r = 4;
16641             if(c.row != c.events.length){
16642                 r = 4 - (4 - (c.row - c.events.length));
16643             }
16644             
16645             c.events = ev.slice(0, r);
16646             c.more = ev.slice(r);
16647             
16648             if(c.more.length && c.more.length == 1){
16649                 c.events.push(c.more.pop());
16650             }
16651             
16652             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16653             
16654         });
16655             
16656         this.cells.each(function(c) {
16657             
16658             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16659             
16660             
16661             for (var e = 0; e < c.events.length; e++){
16662                 var ev = c.events[e];
16663                 var rows = ev.rows;
16664                 
16665                 for(var i = 0; i < rows.length; i++) {
16666                 
16667                     // how many rows should it span..
16668
16669                     var  cfg = {
16670                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16671                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16672
16673                         unselectable : "on",
16674                         cn : [
16675                             {
16676                                 cls: 'fc-event-inner',
16677                                 cn : [
16678     //                                {
16679     //                                  tag:'span',
16680     //                                  cls: 'fc-event-time',
16681     //                                  html : cells.length > 1 ? '' : ev.time
16682     //                                },
16683                                     {
16684                                       tag:'span',
16685                                       cls: 'fc-event-title',
16686                                       html : String.format('{0}', ev.title)
16687                                     }
16688
16689
16690                                 ]
16691                             },
16692                             {
16693                                 cls: 'ui-resizable-handle ui-resizable-e',
16694                                 html : '&nbsp;&nbsp;&nbsp'
16695                             }
16696
16697                         ]
16698                     };
16699
16700                     if (i == 0) {
16701                         cfg.cls += ' fc-event-start';
16702                     }
16703                     if ((i+1) == rows.length) {
16704                         cfg.cls += ' fc-event-end';
16705                     }
16706
16707                     var ctr = _this.el.select('.fc-event-container',true).first();
16708                     var cg = ctr.createChild(cfg);
16709
16710                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16711                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16712
16713                     var r = (c.more.length) ? 1 : 0;
16714                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16715                     cg.setWidth(ebox.right - sbox.x -2);
16716
16717                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16718                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16719                     cg.on('click', _this.onEventClick, _this, ev);
16720
16721                     ev.els.push(cg);
16722                     
16723                 }
16724                 
16725             }
16726             
16727             
16728             if(c.more.length){
16729                 var  cfg = {
16730                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16731                     style : 'position: absolute',
16732                     unselectable : "on",
16733                     cn : [
16734                         {
16735                             cls: 'fc-event-inner',
16736                             cn : [
16737                                 {
16738                                   tag:'span',
16739                                   cls: 'fc-event-title',
16740                                   html : 'More'
16741                                 }
16742
16743
16744                             ]
16745                         },
16746                         {
16747                             cls: 'ui-resizable-handle ui-resizable-e',
16748                             html : '&nbsp;&nbsp;&nbsp'
16749                         }
16750
16751                     ]
16752                 };
16753
16754                 var ctr = _this.el.select('.fc-event-container',true).first();
16755                 var cg = ctr.createChild(cfg);
16756
16757                 var sbox = c.select('.fc-day-content',true).first().getBox();
16758                 var ebox = c.select('.fc-day-content',true).first().getBox();
16759                 //Roo.log(cg);
16760                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16761                 cg.setWidth(ebox.right - sbox.x -2);
16762
16763                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16764                 
16765             }
16766             
16767         });
16768         
16769         
16770         
16771     },
16772     
16773     onEventEnter: function (e, el,event,d) {
16774         this.fireEvent('evententer', this, el, event);
16775     },
16776     
16777     onEventLeave: function (e, el,event,d) {
16778         this.fireEvent('eventleave', this, el, event);
16779     },
16780     
16781     onEventClick: function (e, el,event,d) {
16782         this.fireEvent('eventclick', this, el, event);
16783     },
16784     
16785     onMonthChange: function () {
16786         this.store.load();
16787     },
16788     
16789     onMoreEventClick: function(e, el, more)
16790     {
16791         var _this = this;
16792         
16793         this.calpopover.placement = 'right';
16794         this.calpopover.setTitle('More');
16795         
16796         this.calpopover.setContent('');
16797         
16798         var ctr = this.calpopover.el.select('.popover-content', true).first();
16799         
16800         Roo.each(more, function(m){
16801             var cfg = {
16802                 cls : 'fc-event-hori fc-event-draggable',
16803                 html : m.title
16804             };
16805             var cg = ctr.createChild(cfg);
16806             
16807             cg.on('click', _this.onEventClick, _this, m);
16808         });
16809         
16810         this.calpopover.show(el);
16811         
16812         
16813     },
16814     
16815     onLoad: function () 
16816     {   
16817         this.calevents = [];
16818         var cal = this;
16819         
16820         if(this.store.getCount() > 0){
16821             this.store.data.each(function(d){
16822                cal.addItem({
16823                     id : d.data.id,
16824                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16825                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16826                     time : d.data.start_time,
16827                     title : d.data.title,
16828                     description : d.data.description,
16829                     venue : d.data.venue
16830                 });
16831             });
16832         }
16833         
16834         this.renderEvents();
16835         
16836         if(this.calevents.length && this.loadMask){
16837             this.maskEl.hide();
16838         }
16839     },
16840     
16841     onBeforeLoad: function()
16842     {
16843         this.clearEvents();
16844         if(this.loadMask){
16845             this.maskEl.show();
16846         }
16847     }
16848 });
16849
16850  
16851  /*
16852  * - LGPL
16853  *
16854  * element
16855  * 
16856  */
16857
16858 /**
16859  * @class Roo.bootstrap.Popover
16860  * @extends Roo.bootstrap.Component
16861  * Bootstrap Popover class
16862  * @cfg {String} html contents of the popover   (or false to use children..)
16863  * @cfg {String} title of popover (or false to hide)
16864  * @cfg {String} placement how it is placed
16865  * @cfg {String} trigger click || hover (or false to trigger manually)
16866  * @cfg {String} over what (parent or false to trigger manually.)
16867  * @cfg {Number} delay - delay before showing
16868  
16869  * @constructor
16870  * Create a new Popover
16871  * @param {Object} config The config object
16872  */
16873
16874 Roo.bootstrap.Popover = function(config){
16875     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16876     
16877     this.addEvents({
16878         // raw events
16879          /**
16880          * @event show
16881          * After the popover show
16882          * 
16883          * @param {Roo.bootstrap.Popover} this
16884          */
16885         "show" : true,
16886         /**
16887          * @event hide
16888          * After the popover hide
16889          * 
16890          * @param {Roo.bootstrap.Popover} this
16891          */
16892         "hide" : true
16893     });
16894 };
16895
16896 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16897     
16898     title: 'Fill in a title',
16899     html: false,
16900     
16901     placement : 'right',
16902     trigger : 'hover', // hover
16903     
16904     delay : 0,
16905     
16906     over: 'parent',
16907     
16908     can_build_overlaid : false,
16909     
16910     getChildContainer : function()
16911     {
16912         return this.el.select('.popover-content',true).first();
16913     },
16914     
16915     getAutoCreate : function(){
16916          
16917         var cfg = {
16918            cls : 'popover roo-dynamic',
16919            style: 'display:block',
16920            cn : [
16921                 {
16922                     cls : 'arrow'
16923                 },
16924                 {
16925                     cls : 'popover-inner',
16926                     cn : [
16927                         {
16928                             tag: 'h3',
16929                             cls: 'popover-title',
16930                             html : this.title
16931                         },
16932                         {
16933                             cls : 'popover-content',
16934                             html : this.html
16935                         }
16936                     ]
16937                     
16938                 }
16939            ]
16940         };
16941         
16942         return cfg;
16943     },
16944     setTitle: function(str)
16945     {
16946         this.title = str;
16947         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16948     },
16949     setContent: function(str)
16950     {
16951         this.html = str;
16952         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16953     },
16954     // as it get's added to the bottom of the page.
16955     onRender : function(ct, position)
16956     {
16957         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16958         if(!this.el){
16959             var cfg = Roo.apply({},  this.getAutoCreate());
16960             cfg.id = Roo.id();
16961             
16962             if (this.cls) {
16963                 cfg.cls += ' ' + this.cls;
16964             }
16965             if (this.style) {
16966                 cfg.style = this.style;
16967             }
16968             //Roo.log("adding to ");
16969             this.el = Roo.get(document.body).createChild(cfg, position);
16970 //            Roo.log(this.el);
16971         }
16972         this.initEvents();
16973     },
16974     
16975     initEvents : function()
16976     {
16977         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16978         this.el.enableDisplayMode('block');
16979         this.el.hide();
16980         if (this.over === false) {
16981             return; 
16982         }
16983         if (this.triggers === false) {
16984             return;
16985         }
16986         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16987         var triggers = this.trigger ? this.trigger.split(' ') : [];
16988         Roo.each(triggers, function(trigger) {
16989         
16990             if (trigger == 'click') {
16991                 on_el.on('click', this.toggle, this);
16992             } else if (trigger != 'manual') {
16993                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16994                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16995       
16996                 on_el.on(eventIn  ,this.enter, this);
16997                 on_el.on(eventOut, this.leave, this);
16998             }
16999         }, this);
17000         
17001     },
17002     
17003     
17004     // private
17005     timeout : null,
17006     hoverState : null,
17007     
17008     toggle : function () {
17009         this.hoverState == 'in' ? this.leave() : this.enter();
17010     },
17011     
17012     enter : function () {
17013         
17014         clearTimeout(this.timeout);
17015     
17016         this.hoverState = 'in';
17017     
17018         if (!this.delay || !this.delay.show) {
17019             this.show();
17020             return;
17021         }
17022         var _t = this;
17023         this.timeout = setTimeout(function () {
17024             if (_t.hoverState == 'in') {
17025                 _t.show();
17026             }
17027         }, this.delay.show)
17028     },
17029     
17030     leave : function() {
17031         clearTimeout(this.timeout);
17032     
17033         this.hoverState = 'out';
17034     
17035         if (!this.delay || !this.delay.hide) {
17036             this.hide();
17037             return;
17038         }
17039         var _t = this;
17040         this.timeout = setTimeout(function () {
17041             if (_t.hoverState == 'out') {
17042                 _t.hide();
17043             }
17044         }, this.delay.hide)
17045     },
17046     
17047     show : function (on_el)
17048     {
17049         if (!on_el) {
17050             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17051         }
17052         
17053         // set content.
17054         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17055         if (this.html !== false) {
17056             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17057         }
17058         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17059         if (!this.title.length) {
17060             this.el.select('.popover-title',true).hide();
17061         }
17062         
17063         var placement = typeof this.placement == 'function' ?
17064             this.placement.call(this, this.el, on_el) :
17065             this.placement;
17066             
17067         var autoToken = /\s?auto?\s?/i;
17068         var autoPlace = autoToken.test(placement);
17069         if (autoPlace) {
17070             placement = placement.replace(autoToken, '') || 'top';
17071         }
17072         
17073         //this.el.detach()
17074         //this.el.setXY([0,0]);
17075         this.el.show();
17076         this.el.dom.style.display='block';
17077         this.el.addClass(placement);
17078         
17079         //this.el.appendTo(on_el);
17080         
17081         var p = this.getPosition();
17082         var box = this.el.getBox();
17083         
17084         if (autoPlace) {
17085             // fixme..
17086         }
17087         var align = Roo.bootstrap.Popover.alignment[placement];
17088         this.el.alignTo(on_el, align[0],align[1]);
17089         //var arrow = this.el.select('.arrow',true).first();
17090         //arrow.set(align[2], 
17091         
17092         this.el.addClass('in');
17093         
17094         
17095         if (this.el.hasClass('fade')) {
17096             // fade it?
17097         }
17098         
17099         this.hoverState = 'in';
17100         
17101         this.fireEvent('show', this);
17102         
17103     },
17104     hide : function()
17105     {
17106         this.el.setXY([0,0]);
17107         this.el.removeClass('in');
17108         this.el.hide();
17109         this.hoverState = null;
17110         
17111         this.fireEvent('hide', this);
17112     }
17113     
17114 });
17115
17116 Roo.bootstrap.Popover.alignment = {
17117     'left' : ['r-l', [-10,0], 'right'],
17118     'right' : ['l-r', [10,0], 'left'],
17119     'bottom' : ['t-b', [0,10], 'top'],
17120     'top' : [ 'b-t', [0,-10], 'bottom']
17121 };
17122
17123  /*
17124  * - LGPL
17125  *
17126  * Progress
17127  * 
17128  */
17129
17130 /**
17131  * @class Roo.bootstrap.Progress
17132  * @extends Roo.bootstrap.Component
17133  * Bootstrap Progress class
17134  * @cfg {Boolean} striped striped of the progress bar
17135  * @cfg {Boolean} active animated of the progress bar
17136  * 
17137  * 
17138  * @constructor
17139  * Create a new Progress
17140  * @param {Object} config The config object
17141  */
17142
17143 Roo.bootstrap.Progress = function(config){
17144     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17145 };
17146
17147 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17148     
17149     striped : false,
17150     active: false,
17151     
17152     getAutoCreate : function(){
17153         var cfg = {
17154             tag: 'div',
17155             cls: 'progress'
17156         };
17157         
17158         
17159         if(this.striped){
17160             cfg.cls += ' progress-striped';
17161         }
17162       
17163         if(this.active){
17164             cfg.cls += ' active';
17165         }
17166         
17167         
17168         return cfg;
17169     }
17170    
17171 });
17172
17173  
17174
17175  /*
17176  * - LGPL
17177  *
17178  * ProgressBar
17179  * 
17180  */
17181
17182 /**
17183  * @class Roo.bootstrap.ProgressBar
17184  * @extends Roo.bootstrap.Component
17185  * Bootstrap ProgressBar class
17186  * @cfg {Number} aria_valuenow aria-value now
17187  * @cfg {Number} aria_valuemin aria-value min
17188  * @cfg {Number} aria_valuemax aria-value max
17189  * @cfg {String} label label for the progress bar
17190  * @cfg {String} panel (success | info | warning | danger )
17191  * @cfg {String} role role of the progress bar
17192  * @cfg {String} sr_only text
17193  * 
17194  * 
17195  * @constructor
17196  * Create a new ProgressBar
17197  * @param {Object} config The config object
17198  */
17199
17200 Roo.bootstrap.ProgressBar = function(config){
17201     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17202 };
17203
17204 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17205     
17206     aria_valuenow : 0,
17207     aria_valuemin : 0,
17208     aria_valuemax : 100,
17209     label : false,
17210     panel : false,
17211     role : false,
17212     sr_only: false,
17213     
17214     getAutoCreate : function()
17215     {
17216         
17217         var cfg = {
17218             tag: 'div',
17219             cls: 'progress-bar',
17220             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17221         };
17222         
17223         if(this.sr_only){
17224             cfg.cn = {
17225                 tag: 'span',
17226                 cls: 'sr-only',
17227                 html: this.sr_only
17228             }
17229         }
17230         
17231         if(this.role){
17232             cfg.role = this.role;
17233         }
17234         
17235         if(this.aria_valuenow){
17236             cfg['aria-valuenow'] = this.aria_valuenow;
17237         }
17238         
17239         if(this.aria_valuemin){
17240             cfg['aria-valuemin'] = this.aria_valuemin;
17241         }
17242         
17243         if(this.aria_valuemax){
17244             cfg['aria-valuemax'] = this.aria_valuemax;
17245         }
17246         
17247         if(this.label && !this.sr_only){
17248             cfg.html = this.label;
17249         }
17250         
17251         if(this.panel){
17252             cfg.cls += ' progress-bar-' + this.panel;
17253         }
17254         
17255         return cfg;
17256     },
17257     
17258     update : function(aria_valuenow)
17259     {
17260         this.aria_valuenow = aria_valuenow;
17261         
17262         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17263     }
17264    
17265 });
17266
17267  
17268
17269  /*
17270  * - LGPL
17271  *
17272  * column
17273  * 
17274  */
17275
17276 /**
17277  * @class Roo.bootstrap.TabGroup
17278  * @extends Roo.bootstrap.Column
17279  * Bootstrap Column class
17280  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17281  * @cfg {Boolean} carousel true to make the group behave like a carousel
17282  * @cfg {Boolean} bullets show bullets for the panels
17283  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17284  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17285  * @cfg {Boolean} showarrow (true|false) show arrow default true
17286  * 
17287  * @constructor
17288  * Create a new TabGroup
17289  * @param {Object} config The config object
17290  */
17291
17292 Roo.bootstrap.TabGroup = function(config){
17293     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17294     if (!this.navId) {
17295         this.navId = Roo.id();
17296     }
17297     this.tabs = [];
17298     Roo.bootstrap.TabGroup.register(this);
17299     
17300 };
17301
17302 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17303     
17304     carousel : false,
17305     transition : false,
17306     bullets : 0,
17307     timer : 0,
17308     autoslide : false,
17309     slideFn : false,
17310     slideOnTouch : false,
17311     showarrow : true,
17312     
17313     getAutoCreate : function()
17314     {
17315         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17316         
17317         cfg.cls += ' tab-content';
17318         
17319         if (this.carousel) {
17320             cfg.cls += ' carousel slide';
17321             
17322             cfg.cn = [{
17323                cls : 'carousel-inner',
17324                cn : []
17325             }];
17326         
17327             if(this.bullets  && !Roo.isTouch){
17328                 
17329                 var bullets = {
17330                     cls : 'carousel-bullets',
17331                     cn : []
17332                 };
17333                
17334                 if(this.bullets_cls){
17335                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17336                 }
17337                 
17338                 bullets.cn.push({
17339                     cls : 'clear'
17340                 });
17341                 
17342                 cfg.cn[0].cn.push(bullets);
17343             }
17344             
17345             if(this.showarrow){
17346                 cfg.cn[0].cn.push({
17347                     tag : 'div',
17348                     class : 'carousel-arrow',
17349                     cn : [
17350                         {
17351                             tag : 'div',
17352                             class : 'carousel-prev',
17353                             cn : [
17354                                 {
17355                                     tag : 'i',
17356                                     class : 'fa fa-chevron-left'
17357                                 }
17358                             ]
17359                         },
17360                         {
17361                             tag : 'div',
17362                             class : 'carousel-next',
17363                             cn : [
17364                                 {
17365                                     tag : 'i',
17366                                     class : 'fa fa-chevron-right'
17367                                 }
17368                             ]
17369                         }
17370                     ]
17371                 });
17372             }
17373             
17374         }
17375         
17376         return cfg;
17377     },
17378     
17379     initEvents:  function()
17380     {
17381 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17382 //            this.el.on("touchstart", this.onTouchStart, this);
17383 //        }
17384         
17385         if(this.autoslide){
17386             var _this = this;
17387             
17388             this.slideFn = window.setInterval(function() {
17389                 _this.showPanelNext();
17390             }, this.timer);
17391         }
17392         
17393         if(this.showarrow){
17394             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17395             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17396         }
17397         
17398         
17399     },
17400     
17401 //    onTouchStart : function(e, el, o)
17402 //    {
17403 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17404 //            return;
17405 //        }
17406 //        
17407 //        this.showPanelNext();
17408 //    },
17409     
17410     
17411     getChildContainer : function()
17412     {
17413         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17414     },
17415     
17416     /**
17417     * register a Navigation item
17418     * @param {Roo.bootstrap.NavItem} the navitem to add
17419     */
17420     register : function(item)
17421     {
17422         this.tabs.push( item);
17423         item.navId = this.navId; // not really needed..
17424         this.addBullet();
17425     
17426     },
17427     
17428     getActivePanel : function()
17429     {
17430         var r = false;
17431         Roo.each(this.tabs, function(t) {
17432             if (t.active) {
17433                 r = t;
17434                 return false;
17435             }
17436             return null;
17437         });
17438         return r;
17439         
17440     },
17441     getPanelByName : function(n)
17442     {
17443         var r = false;
17444         Roo.each(this.tabs, function(t) {
17445             if (t.tabId == n) {
17446                 r = t;
17447                 return false;
17448             }
17449             return null;
17450         });
17451         return r;
17452     },
17453     indexOfPanel : function(p)
17454     {
17455         var r = false;
17456         Roo.each(this.tabs, function(t,i) {
17457             if (t.tabId == p.tabId) {
17458                 r = i;
17459                 return false;
17460             }
17461             return null;
17462         });
17463         return r;
17464     },
17465     /**
17466      * show a specific panel
17467      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17468      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17469      */
17470     showPanel : function (pan)
17471     {
17472         if(this.transition || typeof(pan) == 'undefined'){
17473             Roo.log("waiting for the transitionend");
17474             return;
17475         }
17476         
17477         if (typeof(pan) == 'number') {
17478             pan = this.tabs[pan];
17479         }
17480         
17481         if (typeof(pan) == 'string') {
17482             pan = this.getPanelByName(pan);
17483         }
17484         
17485         var cur = this.getActivePanel();
17486         
17487         if(!pan || !cur){
17488             Roo.log('pan or acitve pan is undefined');
17489             return false;
17490         }
17491         
17492         if (pan.tabId == this.getActivePanel().tabId) {
17493             return true;
17494         }
17495         
17496         if (false === cur.fireEvent('beforedeactivate')) {
17497             return false;
17498         }
17499         
17500         if(this.bullets > 0 && !Roo.isTouch){
17501             this.setActiveBullet(this.indexOfPanel(pan));
17502         }
17503         
17504         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17505             
17506             this.transition = true;
17507             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17508             var lr = dir == 'next' ? 'left' : 'right';
17509             pan.el.addClass(dir); // or prev
17510             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17511             cur.el.addClass(lr); // or right
17512             pan.el.addClass(lr);
17513             
17514             var _this = this;
17515             cur.el.on('transitionend', function() {
17516                 Roo.log("trans end?");
17517                 
17518                 pan.el.removeClass([lr,dir]);
17519                 pan.setActive(true);
17520                 
17521                 cur.el.removeClass([lr]);
17522                 cur.setActive(false);
17523                 
17524                 _this.transition = false;
17525                 
17526             }, this, { single:  true } );
17527             
17528             return true;
17529         }
17530         
17531         cur.setActive(false);
17532         pan.setActive(true);
17533         
17534         return true;
17535         
17536     },
17537     showPanelNext : function()
17538     {
17539         var i = this.indexOfPanel(this.getActivePanel());
17540         
17541         if (i >= this.tabs.length - 1 && !this.autoslide) {
17542             return;
17543         }
17544         
17545         if (i >= this.tabs.length - 1 && this.autoslide) {
17546             i = -1;
17547         }
17548         
17549         this.showPanel(this.tabs[i+1]);
17550     },
17551     
17552     showPanelPrev : function()
17553     {
17554         var i = this.indexOfPanel(this.getActivePanel());
17555         
17556         if (i  < 1 && !this.autoslide) {
17557             return;
17558         }
17559         
17560         if (i < 1 && this.autoslide) {
17561             i = this.tabs.length;
17562         }
17563         
17564         this.showPanel(this.tabs[i-1]);
17565     },
17566     
17567     
17568     addBullet: function()
17569     {
17570         if(!this.bullets || Roo.isTouch){
17571             return;
17572         }
17573         var ctr = this.el.select('.carousel-bullets',true).first();
17574         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17575         var bullet = ctr.createChild({
17576             cls : 'bullet bullet-' + i
17577         },ctr.dom.lastChild);
17578         
17579         
17580         var _this = this;
17581         
17582         bullet.on('click', (function(e, el, o, ii, t){
17583
17584             e.preventDefault();
17585
17586             this.showPanel(ii);
17587
17588             if(this.autoslide && this.slideFn){
17589                 clearInterval(this.slideFn);
17590                 this.slideFn = window.setInterval(function() {
17591                     _this.showPanelNext();
17592                 }, this.timer);
17593             }
17594
17595         }).createDelegate(this, [i, bullet], true));
17596                 
17597         
17598     },
17599      
17600     setActiveBullet : function(i)
17601     {
17602         if(Roo.isTouch){
17603             return;
17604         }
17605         
17606         Roo.each(this.el.select('.bullet', true).elements, function(el){
17607             el.removeClass('selected');
17608         });
17609
17610         var bullet = this.el.select('.bullet-' + i, true).first();
17611         
17612         if(!bullet){
17613             return;
17614         }
17615         
17616         bullet.addClass('selected');
17617     }
17618     
17619     
17620   
17621 });
17622
17623  
17624
17625  
17626  
17627 Roo.apply(Roo.bootstrap.TabGroup, {
17628     
17629     groups: {},
17630      /**
17631     * register a Navigation Group
17632     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17633     */
17634     register : function(navgrp)
17635     {
17636         this.groups[navgrp.navId] = navgrp;
17637         
17638     },
17639     /**
17640     * fetch a Navigation Group based on the navigation ID
17641     * if one does not exist , it will get created.
17642     * @param {string} the navgroup to add
17643     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17644     */
17645     get: function(navId) {
17646         if (typeof(this.groups[navId]) == 'undefined') {
17647             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17648         }
17649         return this.groups[navId] ;
17650     }
17651     
17652     
17653     
17654 });
17655
17656  /*
17657  * - LGPL
17658  *
17659  * TabPanel
17660  * 
17661  */
17662
17663 /**
17664  * @class Roo.bootstrap.TabPanel
17665  * @extends Roo.bootstrap.Component
17666  * Bootstrap TabPanel class
17667  * @cfg {Boolean} active panel active
17668  * @cfg {String} html panel content
17669  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17670  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17671  * @cfg {String} href click to link..
17672  * 
17673  * 
17674  * @constructor
17675  * Create a new TabPanel
17676  * @param {Object} config The config object
17677  */
17678
17679 Roo.bootstrap.TabPanel = function(config){
17680     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17681     this.addEvents({
17682         /**
17683              * @event changed
17684              * Fires when the active status changes
17685              * @param {Roo.bootstrap.TabPanel} this
17686              * @param {Boolean} state the new state
17687             
17688          */
17689         'changed': true,
17690         /**
17691              * @event beforedeactivate
17692              * Fires before a tab is de-activated - can be used to do validation on a form.
17693              * @param {Roo.bootstrap.TabPanel} this
17694              * @return {Boolean} false if there is an error
17695             
17696          */
17697         'beforedeactivate': true
17698      });
17699     
17700     this.tabId = this.tabId || Roo.id();
17701   
17702 };
17703
17704 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17705     
17706     active: false,
17707     html: false,
17708     tabId: false,
17709     navId : false,
17710     href : '',
17711     
17712     getAutoCreate : function(){
17713         var cfg = {
17714             tag: 'div',
17715             // item is needed for carousel - not sure if it has any effect otherwise
17716             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17717             html: this.html || ''
17718         };
17719         
17720         if(this.active){
17721             cfg.cls += ' active';
17722         }
17723         
17724         if(this.tabId){
17725             cfg.tabId = this.tabId;
17726         }
17727         
17728         
17729         return cfg;
17730     },
17731     
17732     initEvents:  function()
17733     {
17734         var p = this.parent();
17735         
17736         this.navId = this.navId || p.navId;
17737         
17738         if (typeof(this.navId) != 'undefined') {
17739             // not really needed.. but just in case.. parent should be a NavGroup.
17740             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17741             
17742             tg.register(this);
17743             
17744             var i = tg.tabs.length - 1;
17745             
17746             if(this.active && tg.bullets > 0 && i < tg.bullets){
17747                 tg.setActiveBullet(i);
17748             }
17749         }
17750         
17751         this.el.on('click', this.onClick, this);
17752         
17753         if(Roo.isTouch){
17754             this.el.on("touchstart", this.onTouchStart, this);
17755             this.el.on("touchmove", this.onTouchMove, this);
17756             this.el.on("touchend", this.onTouchEnd, this);
17757         }
17758         
17759     },
17760     
17761     onRender : function(ct, position)
17762     {
17763         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17764     },
17765     
17766     setActive : function(state)
17767     {
17768         Roo.log("panel - set active " + this.tabId + "=" + state);
17769         
17770         this.active = state;
17771         if (!state) {
17772             this.el.removeClass('active');
17773             
17774         } else  if (!this.el.hasClass('active')) {
17775             this.el.addClass('active');
17776         }
17777         
17778         this.fireEvent('changed', this, state);
17779     },
17780     
17781     onClick : function(e)
17782     {
17783         e.preventDefault();
17784         
17785         if(!this.href.length){
17786             return;
17787         }
17788         
17789         window.location.href = this.href;
17790     },
17791     
17792     startX : 0,
17793     startY : 0,
17794     endX : 0,
17795     endY : 0,
17796     swiping : false,
17797     
17798     onTouchStart : function(e)
17799     {
17800         this.swiping = false;
17801         
17802         this.startX = e.browserEvent.touches[0].clientX;
17803         this.startY = e.browserEvent.touches[0].clientY;
17804     },
17805     
17806     onTouchMove : function(e)
17807     {
17808         this.swiping = true;
17809         
17810         this.endX = e.browserEvent.touches[0].clientX;
17811         this.endY = e.browserEvent.touches[0].clientY;
17812     },
17813     
17814     onTouchEnd : function(e)
17815     {
17816         if(!this.swiping){
17817             this.onClick(e);
17818             return;
17819         }
17820         
17821         var tabGroup = this.parent();
17822         
17823         if(this.endX > this.startX){ // swiping right
17824             tabGroup.showPanelPrev();
17825             return;
17826         }
17827         
17828         if(this.startX > this.endX){ // swiping left
17829             tabGroup.showPanelNext();
17830             return;
17831         }
17832     }
17833     
17834     
17835 });
17836  
17837
17838  
17839
17840  /*
17841  * - LGPL
17842  *
17843  * DateField
17844  * 
17845  */
17846
17847 /**
17848  * @class Roo.bootstrap.DateField
17849  * @extends Roo.bootstrap.Input
17850  * Bootstrap DateField class
17851  * @cfg {Number} weekStart default 0
17852  * @cfg {String} viewMode default empty, (months|years)
17853  * @cfg {String} minViewMode default empty, (months|years)
17854  * @cfg {Number} startDate default -Infinity
17855  * @cfg {Number} endDate default Infinity
17856  * @cfg {Boolean} todayHighlight default false
17857  * @cfg {Boolean} todayBtn default false
17858  * @cfg {Boolean} calendarWeeks default false
17859  * @cfg {Object} daysOfWeekDisabled default empty
17860  * @cfg {Boolean} singleMode default false (true | false)
17861  * 
17862  * @cfg {Boolean} keyboardNavigation default true
17863  * @cfg {String} language default en
17864  * 
17865  * @constructor
17866  * Create a new DateField
17867  * @param {Object} config The config object
17868  */
17869
17870 Roo.bootstrap.DateField = function(config){
17871     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17872      this.addEvents({
17873             /**
17874              * @event show
17875              * Fires when this field show.
17876              * @param {Roo.bootstrap.DateField} this
17877              * @param {Mixed} date The date value
17878              */
17879             show : true,
17880             /**
17881              * @event show
17882              * Fires when this field hide.
17883              * @param {Roo.bootstrap.DateField} this
17884              * @param {Mixed} date The date value
17885              */
17886             hide : true,
17887             /**
17888              * @event select
17889              * Fires when select a date.
17890              * @param {Roo.bootstrap.DateField} this
17891              * @param {Mixed} date The date value
17892              */
17893             select : true,
17894             /**
17895              * @event beforeselect
17896              * Fires when before select a date.
17897              * @param {Roo.bootstrap.DateField} this
17898              * @param {Mixed} date The date value
17899              */
17900             beforeselect : true
17901         });
17902 };
17903
17904 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17905     
17906     /**
17907      * @cfg {String} format
17908      * The default date format string which can be overriden for localization support.  The format must be
17909      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17910      */
17911     format : "m/d/y",
17912     /**
17913      * @cfg {String} altFormats
17914      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17915      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17916      */
17917     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17918     
17919     weekStart : 0,
17920     
17921     viewMode : '',
17922     
17923     minViewMode : '',
17924     
17925     todayHighlight : false,
17926     
17927     todayBtn: false,
17928     
17929     language: 'en',
17930     
17931     keyboardNavigation: true,
17932     
17933     calendarWeeks: false,
17934     
17935     startDate: -Infinity,
17936     
17937     endDate: Infinity,
17938     
17939     daysOfWeekDisabled: [],
17940     
17941     _events: [],
17942     
17943     singleMode : false,
17944     
17945     UTCDate: function()
17946     {
17947         return new Date(Date.UTC.apply(Date, arguments));
17948     },
17949     
17950     UTCToday: function()
17951     {
17952         var today = new Date();
17953         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17954     },
17955     
17956     getDate: function() {
17957             var d = this.getUTCDate();
17958             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17959     },
17960     
17961     getUTCDate: function() {
17962             return this.date;
17963     },
17964     
17965     setDate: function(d) {
17966             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17967     },
17968     
17969     setUTCDate: function(d) {
17970             this.date = d;
17971             this.setValue(this.formatDate(this.date));
17972     },
17973         
17974     onRender: function(ct, position)
17975     {
17976         
17977         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17978         
17979         this.language = this.language || 'en';
17980         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17981         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17982         
17983         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17984         this.format = this.format || 'm/d/y';
17985         this.isInline = false;
17986         this.isInput = true;
17987         this.component = this.el.select('.add-on', true).first() || false;
17988         this.component = (this.component && this.component.length === 0) ? false : this.component;
17989         this.hasInput = this.component && this.inputEl().length;
17990         
17991         if (typeof(this.minViewMode === 'string')) {
17992             switch (this.minViewMode) {
17993                 case 'months':
17994                     this.minViewMode = 1;
17995                     break;
17996                 case 'years':
17997                     this.minViewMode = 2;
17998                     break;
17999                 default:
18000                     this.minViewMode = 0;
18001                     break;
18002             }
18003         }
18004         
18005         if (typeof(this.viewMode === 'string')) {
18006             switch (this.viewMode) {
18007                 case 'months':
18008                     this.viewMode = 1;
18009                     break;
18010                 case 'years':
18011                     this.viewMode = 2;
18012                     break;
18013                 default:
18014                     this.viewMode = 0;
18015                     break;
18016             }
18017         }
18018                 
18019         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18020         
18021 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18022         
18023         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18024         
18025         this.picker().on('mousedown', this.onMousedown, this);
18026         this.picker().on('click', this.onClick, this);
18027         
18028         this.picker().addClass('datepicker-dropdown');
18029         
18030         this.startViewMode = this.viewMode;
18031         
18032         if(this.singleMode){
18033             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18034                 v.setVisibilityMode(Roo.Element.DISPLAY);
18035                 v.hide();
18036             });
18037             
18038             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18039                 v.setStyle('width', '189px');
18040             });
18041         }
18042         
18043         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18044             if(!this.calendarWeeks){
18045                 v.remove();
18046                 return;
18047             }
18048             
18049             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18050             v.attr('colspan', function(i, val){
18051                 return parseInt(val) + 1;
18052             });
18053         });
18054                         
18055         
18056         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18057         
18058         this.setStartDate(this.startDate);
18059         this.setEndDate(this.endDate);
18060         
18061         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18062         
18063         this.fillDow();
18064         this.fillMonths();
18065         this.update();
18066         this.showMode();
18067         
18068         if(this.isInline) {
18069             this.show();
18070         }
18071     },
18072     
18073     picker : function()
18074     {
18075         return this.pickerEl;
18076 //        return this.el.select('.datepicker', true).first();
18077     },
18078     
18079     fillDow: function()
18080     {
18081         var dowCnt = this.weekStart;
18082         
18083         var dow = {
18084             tag: 'tr',
18085             cn: [
18086                 
18087             ]
18088         };
18089         
18090         if(this.calendarWeeks){
18091             dow.cn.push({
18092                 tag: 'th',
18093                 cls: 'cw',
18094                 html: '&nbsp;'
18095             })
18096         }
18097         
18098         while (dowCnt < this.weekStart + 7) {
18099             dow.cn.push({
18100                 tag: 'th',
18101                 cls: 'dow',
18102                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18103             });
18104         }
18105         
18106         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18107     },
18108     
18109     fillMonths: function()
18110     {    
18111         var i = 0;
18112         var months = this.picker().select('>.datepicker-months td', true).first();
18113         
18114         months.dom.innerHTML = '';
18115         
18116         while (i < 12) {
18117             var month = {
18118                 tag: 'span',
18119                 cls: 'month',
18120                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18121             };
18122             
18123             months.createChild(month);
18124         }
18125         
18126     },
18127     
18128     update: function()
18129     {
18130         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;
18131         
18132         if (this.date < this.startDate) {
18133             this.viewDate = new Date(this.startDate);
18134         } else if (this.date > this.endDate) {
18135             this.viewDate = new Date(this.endDate);
18136         } else {
18137             this.viewDate = new Date(this.date);
18138         }
18139         
18140         this.fill();
18141     },
18142     
18143     fill: function() 
18144     {
18145         var d = new Date(this.viewDate),
18146                 year = d.getUTCFullYear(),
18147                 month = d.getUTCMonth(),
18148                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18149                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18150                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18151                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18152                 currentDate = this.date && this.date.valueOf(),
18153                 today = this.UTCToday();
18154         
18155         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18156         
18157 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18158         
18159 //        this.picker.select('>tfoot th.today').
18160 //                                              .text(dates[this.language].today)
18161 //                                              .toggle(this.todayBtn !== false);
18162     
18163         this.updateNavArrows();
18164         this.fillMonths();
18165                                                 
18166         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18167         
18168         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18169          
18170         prevMonth.setUTCDate(day);
18171         
18172         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18173         
18174         var nextMonth = new Date(prevMonth);
18175         
18176         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18177         
18178         nextMonth = nextMonth.valueOf();
18179         
18180         var fillMonths = false;
18181         
18182         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18183         
18184         while(prevMonth.valueOf() < nextMonth) {
18185             var clsName = '';
18186             
18187             if (prevMonth.getUTCDay() === this.weekStart) {
18188                 if(fillMonths){
18189                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18190                 }
18191                     
18192                 fillMonths = {
18193                     tag: 'tr',
18194                     cn: []
18195                 };
18196                 
18197                 if(this.calendarWeeks){
18198                     // ISO 8601: First week contains first thursday.
18199                     // ISO also states week starts on Monday, but we can be more abstract here.
18200                     var
18201                     // Start of current week: based on weekstart/current date
18202                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18203                     // Thursday of this week
18204                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18205                     // First Thursday of year, year from thursday
18206                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18207                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18208                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18209                     
18210                     fillMonths.cn.push({
18211                         tag: 'td',
18212                         cls: 'cw',
18213                         html: calWeek
18214                     });
18215                 }
18216             }
18217             
18218             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18219                 clsName += ' old';
18220             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18221                 clsName += ' new';
18222             }
18223             if (this.todayHighlight &&
18224                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18225                 prevMonth.getUTCMonth() == today.getMonth() &&
18226                 prevMonth.getUTCDate() == today.getDate()) {
18227                 clsName += ' today';
18228             }
18229             
18230             if (currentDate && prevMonth.valueOf() === currentDate) {
18231                 clsName += ' active';
18232             }
18233             
18234             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18235                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18236                     clsName += ' disabled';
18237             }
18238             
18239             fillMonths.cn.push({
18240                 tag: 'td',
18241                 cls: 'day ' + clsName,
18242                 html: prevMonth.getDate()
18243             });
18244             
18245             prevMonth.setDate(prevMonth.getDate()+1);
18246         }
18247           
18248         var currentYear = this.date && this.date.getUTCFullYear();
18249         var currentMonth = this.date && this.date.getUTCMonth();
18250         
18251         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18252         
18253         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18254             v.removeClass('active');
18255             
18256             if(currentYear === year && k === currentMonth){
18257                 v.addClass('active');
18258             }
18259             
18260             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18261                 v.addClass('disabled');
18262             }
18263             
18264         });
18265         
18266         
18267         year = parseInt(year/10, 10) * 10;
18268         
18269         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18270         
18271         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18272         
18273         year -= 1;
18274         for (var i = -1; i < 11; i++) {
18275             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18276                 tag: 'span',
18277                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18278                 html: year
18279             });
18280             
18281             year += 1;
18282         }
18283     },
18284     
18285     showMode: function(dir) 
18286     {
18287         if (dir) {
18288             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18289         }
18290         
18291         Roo.each(this.picker().select('>div',true).elements, function(v){
18292             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18293             v.hide();
18294         });
18295         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18296     },
18297     
18298     place: function()
18299     {
18300         if(this.isInline) {
18301             return;
18302         }
18303         
18304         this.picker().removeClass(['bottom', 'top']);
18305         
18306         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18307             /*
18308              * place to the top of element!
18309              *
18310              */
18311             
18312             this.picker().addClass('top');
18313             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18314             
18315             return;
18316         }
18317         
18318         this.picker().addClass('bottom');
18319         
18320         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18321     },
18322     
18323     parseDate : function(value)
18324     {
18325         if(!value || value instanceof Date){
18326             return value;
18327         }
18328         var v = Date.parseDate(value, this.format);
18329         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18330             v = Date.parseDate(value, 'Y-m-d');
18331         }
18332         if(!v && this.altFormats){
18333             if(!this.altFormatsArray){
18334                 this.altFormatsArray = this.altFormats.split("|");
18335             }
18336             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18337                 v = Date.parseDate(value, this.altFormatsArray[i]);
18338             }
18339         }
18340         return v;
18341     },
18342     
18343     formatDate : function(date, fmt)
18344     {   
18345         return (!date || !(date instanceof Date)) ?
18346         date : date.dateFormat(fmt || this.format);
18347     },
18348     
18349     onFocus : function()
18350     {
18351         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18352         this.show();
18353     },
18354     
18355     onBlur : function()
18356     {
18357         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18358         
18359         var d = this.inputEl().getValue();
18360         
18361         this.setValue(d);
18362                 
18363         this.hide();
18364     },
18365     
18366     show : function()
18367     {
18368         this.picker().show();
18369         this.update();
18370         this.place();
18371         
18372         this.fireEvent('show', this, this.date);
18373     },
18374     
18375     hide : function()
18376     {
18377         if(this.isInline) {
18378             return;
18379         }
18380         this.picker().hide();
18381         this.viewMode = this.startViewMode;
18382         this.showMode();
18383         
18384         this.fireEvent('hide', this, this.date);
18385         
18386     },
18387     
18388     onMousedown: function(e)
18389     {
18390         e.stopPropagation();
18391         e.preventDefault();
18392     },
18393     
18394     keyup: function(e)
18395     {
18396         Roo.bootstrap.DateField.superclass.keyup.call(this);
18397         this.update();
18398     },
18399
18400     setValue: function(v)
18401     {
18402         if(this.fireEvent('beforeselect', this, v) !== false){
18403             var d = new Date(this.parseDate(v) ).clearTime();
18404         
18405             if(isNaN(d.getTime())){
18406                 this.date = this.viewDate = '';
18407                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18408                 return;
18409             }
18410
18411             v = this.formatDate(d);
18412
18413             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18414
18415             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18416
18417             this.update();
18418
18419             this.fireEvent('select', this, this.date);
18420         }
18421     },
18422     
18423     getValue: function()
18424     {
18425         return this.formatDate(this.date);
18426     },
18427     
18428     fireKey: function(e)
18429     {
18430         if (!this.picker().isVisible()){
18431             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18432                 this.show();
18433             }
18434             return;
18435         }
18436         
18437         var dateChanged = false,
18438         dir, day, month,
18439         newDate, newViewDate;
18440         
18441         switch(e.keyCode){
18442             case 27: // escape
18443                 this.hide();
18444                 e.preventDefault();
18445                 break;
18446             case 37: // left
18447             case 39: // right
18448                 if (!this.keyboardNavigation) {
18449                     break;
18450                 }
18451                 dir = e.keyCode == 37 ? -1 : 1;
18452                 
18453                 if (e.ctrlKey){
18454                     newDate = this.moveYear(this.date, dir);
18455                     newViewDate = this.moveYear(this.viewDate, dir);
18456                 } else if (e.shiftKey){
18457                     newDate = this.moveMonth(this.date, dir);
18458                     newViewDate = this.moveMonth(this.viewDate, dir);
18459                 } else {
18460                     newDate = new Date(this.date);
18461                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18462                     newViewDate = new Date(this.viewDate);
18463                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18464                 }
18465                 if (this.dateWithinRange(newDate)){
18466                     this.date = newDate;
18467                     this.viewDate = newViewDate;
18468                     this.setValue(this.formatDate(this.date));
18469 //                    this.update();
18470                     e.preventDefault();
18471                     dateChanged = true;
18472                 }
18473                 break;
18474             case 38: // up
18475             case 40: // down
18476                 if (!this.keyboardNavigation) {
18477                     break;
18478                 }
18479                 dir = e.keyCode == 38 ? -1 : 1;
18480                 if (e.ctrlKey){
18481                     newDate = this.moveYear(this.date, dir);
18482                     newViewDate = this.moveYear(this.viewDate, dir);
18483                 } else if (e.shiftKey){
18484                     newDate = this.moveMonth(this.date, dir);
18485                     newViewDate = this.moveMonth(this.viewDate, dir);
18486                 } else {
18487                     newDate = new Date(this.date);
18488                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18489                     newViewDate = new Date(this.viewDate);
18490                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18491                 }
18492                 if (this.dateWithinRange(newDate)){
18493                     this.date = newDate;
18494                     this.viewDate = newViewDate;
18495                     this.setValue(this.formatDate(this.date));
18496 //                    this.update();
18497                     e.preventDefault();
18498                     dateChanged = true;
18499                 }
18500                 break;
18501             case 13: // enter
18502                 this.setValue(this.formatDate(this.date));
18503                 this.hide();
18504                 e.preventDefault();
18505                 break;
18506             case 9: // tab
18507                 this.setValue(this.formatDate(this.date));
18508                 this.hide();
18509                 break;
18510             case 16: // shift
18511             case 17: // ctrl
18512             case 18: // alt
18513                 break;
18514             default :
18515                 this.hide();
18516                 
18517         }
18518     },
18519     
18520     
18521     onClick: function(e) 
18522     {
18523         e.stopPropagation();
18524         e.preventDefault();
18525         
18526         var target = e.getTarget();
18527         
18528         if(target.nodeName.toLowerCase() === 'i'){
18529             target = Roo.get(target).dom.parentNode;
18530         }
18531         
18532         var nodeName = target.nodeName;
18533         var className = target.className;
18534         var html = target.innerHTML;
18535         //Roo.log(nodeName);
18536         
18537         switch(nodeName.toLowerCase()) {
18538             case 'th':
18539                 switch(className) {
18540                     case 'switch':
18541                         this.showMode(1);
18542                         break;
18543                     case 'prev':
18544                     case 'next':
18545                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18546                         switch(this.viewMode){
18547                                 case 0:
18548                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18549                                         break;
18550                                 case 1:
18551                                 case 2:
18552                                         this.viewDate = this.moveYear(this.viewDate, dir);
18553                                         break;
18554                         }
18555                         this.fill();
18556                         break;
18557                     case 'today':
18558                         var date = new Date();
18559                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18560 //                        this.fill()
18561                         this.setValue(this.formatDate(this.date));
18562                         
18563                         this.hide();
18564                         break;
18565                 }
18566                 break;
18567             case 'span':
18568                 if (className.indexOf('disabled') < 0) {
18569                     this.viewDate.setUTCDate(1);
18570                     if (className.indexOf('month') > -1) {
18571                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18572                     } else {
18573                         var year = parseInt(html, 10) || 0;
18574                         this.viewDate.setUTCFullYear(year);
18575                         
18576                     }
18577                     
18578                     if(this.singleMode){
18579                         this.setValue(this.formatDate(this.viewDate));
18580                         this.hide();
18581                         return;
18582                     }
18583                     
18584                     this.showMode(-1);
18585                     this.fill();
18586                 }
18587                 break;
18588                 
18589             case 'td':
18590                 //Roo.log(className);
18591                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18592                     var day = parseInt(html, 10) || 1;
18593                     var year = this.viewDate.getUTCFullYear(),
18594                         month = this.viewDate.getUTCMonth();
18595
18596                     if (className.indexOf('old') > -1) {
18597                         if(month === 0 ){
18598                             month = 11;
18599                             year -= 1;
18600                         }else{
18601                             month -= 1;
18602                         }
18603                     } else if (className.indexOf('new') > -1) {
18604                         if (month == 11) {
18605                             month = 0;
18606                             year += 1;
18607                         } else {
18608                             month += 1;
18609                         }
18610                     }
18611                     //Roo.log([year,month,day]);
18612                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18613                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18614 //                    this.fill();
18615                     //Roo.log(this.formatDate(this.date));
18616                     this.setValue(this.formatDate(this.date));
18617                     this.hide();
18618                 }
18619                 break;
18620         }
18621     },
18622     
18623     setStartDate: function(startDate)
18624     {
18625         this.startDate = startDate || -Infinity;
18626         if (this.startDate !== -Infinity) {
18627             this.startDate = this.parseDate(this.startDate);
18628         }
18629         this.update();
18630         this.updateNavArrows();
18631     },
18632
18633     setEndDate: function(endDate)
18634     {
18635         this.endDate = endDate || Infinity;
18636         if (this.endDate !== Infinity) {
18637             this.endDate = this.parseDate(this.endDate);
18638         }
18639         this.update();
18640         this.updateNavArrows();
18641     },
18642     
18643     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18644     {
18645         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18646         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18647             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18648         }
18649         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18650             return parseInt(d, 10);
18651         });
18652         this.update();
18653         this.updateNavArrows();
18654     },
18655     
18656     updateNavArrows: function() 
18657     {
18658         if(this.singleMode){
18659             return;
18660         }
18661         
18662         var d = new Date(this.viewDate),
18663         year = d.getUTCFullYear(),
18664         month = d.getUTCMonth();
18665         
18666         Roo.each(this.picker().select('.prev', true).elements, function(v){
18667             v.show();
18668             switch (this.viewMode) {
18669                 case 0:
18670
18671                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18672                         v.hide();
18673                     }
18674                     break;
18675                 case 1:
18676                 case 2:
18677                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18678                         v.hide();
18679                     }
18680                     break;
18681             }
18682         });
18683         
18684         Roo.each(this.picker().select('.next', true).elements, function(v){
18685             v.show();
18686             switch (this.viewMode) {
18687                 case 0:
18688
18689                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18690                         v.hide();
18691                     }
18692                     break;
18693                 case 1:
18694                 case 2:
18695                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18696                         v.hide();
18697                     }
18698                     break;
18699             }
18700         })
18701     },
18702     
18703     moveMonth: function(date, dir)
18704     {
18705         if (!dir) {
18706             return date;
18707         }
18708         var new_date = new Date(date.valueOf()),
18709         day = new_date.getUTCDate(),
18710         month = new_date.getUTCMonth(),
18711         mag = Math.abs(dir),
18712         new_month, test;
18713         dir = dir > 0 ? 1 : -1;
18714         if (mag == 1){
18715             test = dir == -1
18716             // If going back one month, make sure month is not current month
18717             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18718             ? function(){
18719                 return new_date.getUTCMonth() == month;
18720             }
18721             // If going forward one month, make sure month is as expected
18722             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18723             : function(){
18724                 return new_date.getUTCMonth() != new_month;
18725             };
18726             new_month = month + dir;
18727             new_date.setUTCMonth(new_month);
18728             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18729             if (new_month < 0 || new_month > 11) {
18730                 new_month = (new_month + 12) % 12;
18731             }
18732         } else {
18733             // For magnitudes >1, move one month at a time...
18734             for (var i=0; i<mag; i++) {
18735                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18736                 new_date = this.moveMonth(new_date, dir);
18737             }
18738             // ...then reset the day, keeping it in the new month
18739             new_month = new_date.getUTCMonth();
18740             new_date.setUTCDate(day);
18741             test = function(){
18742                 return new_month != new_date.getUTCMonth();
18743             };
18744         }
18745         // Common date-resetting loop -- if date is beyond end of month, make it
18746         // end of month
18747         while (test()){
18748             new_date.setUTCDate(--day);
18749             new_date.setUTCMonth(new_month);
18750         }
18751         return new_date;
18752     },
18753
18754     moveYear: function(date, dir)
18755     {
18756         return this.moveMonth(date, dir*12);
18757     },
18758
18759     dateWithinRange: function(date)
18760     {
18761         return date >= this.startDate && date <= this.endDate;
18762     },
18763
18764     
18765     remove: function() 
18766     {
18767         this.picker().remove();
18768     },
18769     
18770     validateValue : function(value)
18771     {
18772         if(value.length < 1)  {
18773             if(this.allowBlank){
18774                 return true;
18775             }
18776             return false;
18777         }
18778         
18779         if(value.length < this.minLength){
18780             return false;
18781         }
18782         if(value.length > this.maxLength){
18783             return false;
18784         }
18785         if(this.vtype){
18786             var vt = Roo.form.VTypes;
18787             if(!vt[this.vtype](value, this)){
18788                 return false;
18789             }
18790         }
18791         if(typeof this.validator == "function"){
18792             var msg = this.validator(value);
18793             if(msg !== true){
18794                 return false;
18795             }
18796         }
18797         
18798         if(this.regex && !this.regex.test(value)){
18799             return false;
18800         }
18801         
18802         if(typeof(this.parseDate(value)) == 'undefined'){
18803             return false;
18804         }
18805         
18806         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18807             return false;
18808         }      
18809         
18810         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18811             return false;
18812         } 
18813         
18814         
18815         return true;
18816     }
18817    
18818 });
18819
18820 Roo.apply(Roo.bootstrap.DateField,  {
18821     
18822     head : {
18823         tag: 'thead',
18824         cn: [
18825         {
18826             tag: 'tr',
18827             cn: [
18828             {
18829                 tag: 'th',
18830                 cls: 'prev',
18831                 html: '<i class="fa fa-arrow-left"/>'
18832             },
18833             {
18834                 tag: 'th',
18835                 cls: 'switch',
18836                 colspan: '5'
18837             },
18838             {
18839                 tag: 'th',
18840                 cls: 'next',
18841                 html: '<i class="fa fa-arrow-right"/>'
18842             }
18843
18844             ]
18845         }
18846         ]
18847     },
18848     
18849     content : {
18850         tag: 'tbody',
18851         cn: [
18852         {
18853             tag: 'tr',
18854             cn: [
18855             {
18856                 tag: 'td',
18857                 colspan: '7'
18858             }
18859             ]
18860         }
18861         ]
18862     },
18863     
18864     footer : {
18865         tag: 'tfoot',
18866         cn: [
18867         {
18868             tag: 'tr',
18869             cn: [
18870             {
18871                 tag: 'th',
18872                 colspan: '7',
18873                 cls: 'today'
18874             }
18875                     
18876             ]
18877         }
18878         ]
18879     },
18880     
18881     dates:{
18882         en: {
18883             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18884             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18885             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18886             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18887             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18888             today: "Today"
18889         }
18890     },
18891     
18892     modes: [
18893     {
18894         clsName: 'days',
18895         navFnc: 'Month',
18896         navStep: 1
18897     },
18898     {
18899         clsName: 'months',
18900         navFnc: 'FullYear',
18901         navStep: 1
18902     },
18903     {
18904         clsName: 'years',
18905         navFnc: 'FullYear',
18906         navStep: 10
18907     }]
18908 });
18909
18910 Roo.apply(Roo.bootstrap.DateField,  {
18911   
18912     template : {
18913         tag: 'div',
18914         cls: 'datepicker dropdown-menu roo-dynamic',
18915         cn: [
18916         {
18917             tag: 'div',
18918             cls: 'datepicker-days',
18919             cn: [
18920             {
18921                 tag: 'table',
18922                 cls: 'table-condensed',
18923                 cn:[
18924                 Roo.bootstrap.DateField.head,
18925                 {
18926                     tag: 'tbody'
18927                 },
18928                 Roo.bootstrap.DateField.footer
18929                 ]
18930             }
18931             ]
18932         },
18933         {
18934             tag: 'div',
18935             cls: 'datepicker-months',
18936             cn: [
18937             {
18938                 tag: 'table',
18939                 cls: 'table-condensed',
18940                 cn:[
18941                 Roo.bootstrap.DateField.head,
18942                 Roo.bootstrap.DateField.content,
18943                 Roo.bootstrap.DateField.footer
18944                 ]
18945             }
18946             ]
18947         },
18948         {
18949             tag: 'div',
18950             cls: 'datepicker-years',
18951             cn: [
18952             {
18953                 tag: 'table',
18954                 cls: 'table-condensed',
18955                 cn:[
18956                 Roo.bootstrap.DateField.head,
18957                 Roo.bootstrap.DateField.content,
18958                 Roo.bootstrap.DateField.footer
18959                 ]
18960             }
18961             ]
18962         }
18963         ]
18964     }
18965 });
18966
18967  
18968
18969  /*
18970  * - LGPL
18971  *
18972  * TimeField
18973  * 
18974  */
18975
18976 /**
18977  * @class Roo.bootstrap.TimeField
18978  * @extends Roo.bootstrap.Input
18979  * Bootstrap DateField class
18980  * 
18981  * 
18982  * @constructor
18983  * Create a new TimeField
18984  * @param {Object} config The config object
18985  */
18986
18987 Roo.bootstrap.TimeField = function(config){
18988     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18989     this.addEvents({
18990             /**
18991              * @event show
18992              * Fires when this field show.
18993              * @param {Roo.bootstrap.DateField} thisthis
18994              * @param {Mixed} date The date value
18995              */
18996             show : true,
18997             /**
18998              * @event show
18999              * Fires when this field hide.
19000              * @param {Roo.bootstrap.DateField} this
19001              * @param {Mixed} date The date value
19002              */
19003             hide : true,
19004             /**
19005              * @event select
19006              * Fires when select a date.
19007              * @param {Roo.bootstrap.DateField} this
19008              * @param {Mixed} date The date value
19009              */
19010             select : true
19011         });
19012 };
19013
19014 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19015     
19016     /**
19017      * @cfg {String} format
19018      * The default time format string which can be overriden for localization support.  The format must be
19019      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19020      */
19021     format : "H:i",
19022        
19023     onRender: function(ct, position)
19024     {
19025         
19026         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19027                 
19028         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19029         
19030         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19031         
19032         this.pop = this.picker().select('>.datepicker-time',true).first();
19033         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19034         
19035         this.picker().on('mousedown', this.onMousedown, this);
19036         this.picker().on('click', this.onClick, this);
19037         
19038         this.picker().addClass('datepicker-dropdown');
19039     
19040         this.fillTime();
19041         this.update();
19042             
19043         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19044         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19045         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19046         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19047         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19048         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19049
19050     },
19051     
19052     fireKey: function(e){
19053         if (!this.picker().isVisible()){
19054             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19055                 this.show();
19056             }
19057             return;
19058         }
19059
19060         e.preventDefault();
19061         
19062         switch(e.keyCode){
19063             case 27: // escape
19064                 this.hide();
19065                 break;
19066             case 37: // left
19067             case 39: // right
19068                 this.onTogglePeriod();
19069                 break;
19070             case 38: // up
19071                 this.onIncrementMinutes();
19072                 break;
19073             case 40: // down
19074                 this.onDecrementMinutes();
19075                 break;
19076             case 13: // enter
19077             case 9: // tab
19078                 this.setTime();
19079                 break;
19080         }
19081     },
19082     
19083     onClick: function(e) {
19084         e.stopPropagation();
19085         e.preventDefault();
19086     },
19087     
19088     picker : function()
19089     {
19090         return this.el.select('.datepicker', true).first();
19091     },
19092     
19093     fillTime: function()
19094     {    
19095         var time = this.pop.select('tbody', true).first();
19096         
19097         time.dom.innerHTML = '';
19098         
19099         time.createChild({
19100             tag: 'tr',
19101             cn: [
19102                 {
19103                     tag: 'td',
19104                     cn: [
19105                         {
19106                             tag: 'a',
19107                             href: '#',
19108                             cls: 'btn',
19109                             cn: [
19110                                 {
19111                                     tag: 'span',
19112                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19113                                 }
19114                             ]
19115                         } 
19116                     ]
19117                 },
19118                 {
19119                     tag: 'td',
19120                     cls: 'separator'
19121                 },
19122                 {
19123                     tag: 'td',
19124                     cn: [
19125                         {
19126                             tag: 'a',
19127                             href: '#',
19128                             cls: 'btn',
19129                             cn: [
19130                                 {
19131                                     tag: 'span',
19132                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19133                                 }
19134                             ]
19135                         }
19136                     ]
19137                 },
19138                 {
19139                     tag: 'td',
19140                     cls: 'separator'
19141                 }
19142             ]
19143         });
19144         
19145         time.createChild({
19146             tag: 'tr',
19147             cn: [
19148                 {
19149                     tag: 'td',
19150                     cn: [
19151                         {
19152                             tag: 'span',
19153                             cls: 'timepicker-hour',
19154                             html: '00'
19155                         }  
19156                     ]
19157                 },
19158                 {
19159                     tag: 'td',
19160                     cls: 'separator',
19161                     html: ':'
19162                 },
19163                 {
19164                     tag: 'td',
19165                     cn: [
19166                         {
19167                             tag: 'span',
19168                             cls: 'timepicker-minute',
19169                             html: '00'
19170                         }  
19171                     ]
19172                 },
19173                 {
19174                     tag: 'td',
19175                     cls: 'separator'
19176                 },
19177                 {
19178                     tag: 'td',
19179                     cn: [
19180                         {
19181                             tag: 'button',
19182                             type: 'button',
19183                             cls: 'btn btn-primary period',
19184                             html: 'AM'
19185                             
19186                         }
19187                     ]
19188                 }
19189             ]
19190         });
19191         
19192         time.createChild({
19193             tag: 'tr',
19194             cn: [
19195                 {
19196                     tag: 'td',
19197                     cn: [
19198                         {
19199                             tag: 'a',
19200                             href: '#',
19201                             cls: 'btn',
19202                             cn: [
19203                                 {
19204                                     tag: 'span',
19205                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19206                                 }
19207                             ]
19208                         }
19209                     ]
19210                 },
19211                 {
19212                     tag: 'td',
19213                     cls: 'separator'
19214                 },
19215                 {
19216                     tag: 'td',
19217                     cn: [
19218                         {
19219                             tag: 'a',
19220                             href: '#',
19221                             cls: 'btn',
19222                             cn: [
19223                                 {
19224                                     tag: 'span',
19225                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19226                                 }
19227                             ]
19228                         }
19229                     ]
19230                 },
19231                 {
19232                     tag: 'td',
19233                     cls: 'separator'
19234                 }
19235             ]
19236         });
19237         
19238     },
19239     
19240     update: function()
19241     {
19242         
19243         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19244         
19245         this.fill();
19246     },
19247     
19248     fill: function() 
19249     {
19250         var hours = this.time.getHours();
19251         var minutes = this.time.getMinutes();
19252         var period = 'AM';
19253         
19254         if(hours > 11){
19255             period = 'PM';
19256         }
19257         
19258         if(hours == 0){
19259             hours = 12;
19260         }
19261         
19262         
19263         if(hours > 12){
19264             hours = hours - 12;
19265         }
19266         
19267         if(hours < 10){
19268             hours = '0' + hours;
19269         }
19270         
19271         if(minutes < 10){
19272             minutes = '0' + minutes;
19273         }
19274         
19275         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19276         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19277         this.pop.select('button', true).first().dom.innerHTML = period;
19278         
19279     },
19280     
19281     place: function()
19282     {   
19283         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19284         
19285         var cls = ['bottom'];
19286         
19287         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19288             cls.pop();
19289             cls.push('top');
19290         }
19291         
19292         cls.push('right');
19293         
19294         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19295             cls.pop();
19296             cls.push('left');
19297         }
19298         
19299         this.picker().addClass(cls.join('-'));
19300         
19301         var _this = this;
19302         
19303         Roo.each(cls, function(c){
19304             if(c == 'bottom'){
19305                 _this.picker().setTop(_this.inputEl().getHeight());
19306                 return;
19307             }
19308             if(c == 'top'){
19309                 _this.picker().setTop(0 - _this.picker().getHeight());
19310                 return;
19311             }
19312             
19313             if(c == 'left'){
19314                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19315                 return;
19316             }
19317             if(c == 'right'){
19318                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19319                 return;
19320             }
19321         });
19322         
19323     },
19324   
19325     onFocus : function()
19326     {
19327         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19328         this.show();
19329     },
19330     
19331     onBlur : function()
19332     {
19333         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19334         this.hide();
19335     },
19336     
19337     show : function()
19338     {
19339         this.picker().show();
19340         this.pop.show();
19341         this.update();
19342         this.place();
19343         
19344         this.fireEvent('show', this, this.date);
19345     },
19346     
19347     hide : function()
19348     {
19349         this.picker().hide();
19350         this.pop.hide();
19351         
19352         this.fireEvent('hide', this, this.date);
19353     },
19354     
19355     setTime : function()
19356     {
19357         this.hide();
19358         this.setValue(this.time.format(this.format));
19359         
19360         this.fireEvent('select', this, this.date);
19361         
19362         
19363     },
19364     
19365     onMousedown: function(e){
19366         e.stopPropagation();
19367         e.preventDefault();
19368     },
19369     
19370     onIncrementHours: function()
19371     {
19372         Roo.log('onIncrementHours');
19373         this.time = this.time.add(Date.HOUR, 1);
19374         this.update();
19375         
19376     },
19377     
19378     onDecrementHours: function()
19379     {
19380         Roo.log('onDecrementHours');
19381         this.time = this.time.add(Date.HOUR, -1);
19382         this.update();
19383     },
19384     
19385     onIncrementMinutes: function()
19386     {
19387         Roo.log('onIncrementMinutes');
19388         this.time = this.time.add(Date.MINUTE, 1);
19389         this.update();
19390     },
19391     
19392     onDecrementMinutes: function()
19393     {
19394         Roo.log('onDecrementMinutes');
19395         this.time = this.time.add(Date.MINUTE, -1);
19396         this.update();
19397     },
19398     
19399     onTogglePeriod: function()
19400     {
19401         Roo.log('onTogglePeriod');
19402         this.time = this.time.add(Date.HOUR, 12);
19403         this.update();
19404     }
19405     
19406    
19407 });
19408
19409 Roo.apply(Roo.bootstrap.TimeField,  {
19410     
19411     content : {
19412         tag: 'tbody',
19413         cn: [
19414             {
19415                 tag: 'tr',
19416                 cn: [
19417                 {
19418                     tag: 'td',
19419                     colspan: '7'
19420                 }
19421                 ]
19422             }
19423         ]
19424     },
19425     
19426     footer : {
19427         tag: 'tfoot',
19428         cn: [
19429             {
19430                 tag: 'tr',
19431                 cn: [
19432                 {
19433                     tag: 'th',
19434                     colspan: '7',
19435                     cls: '',
19436                     cn: [
19437                         {
19438                             tag: 'button',
19439                             cls: 'btn btn-info ok',
19440                             html: 'OK'
19441                         }
19442                     ]
19443                 }
19444
19445                 ]
19446             }
19447         ]
19448     }
19449 });
19450
19451 Roo.apply(Roo.bootstrap.TimeField,  {
19452   
19453     template : {
19454         tag: 'div',
19455         cls: 'datepicker dropdown-menu',
19456         cn: [
19457             {
19458                 tag: 'div',
19459                 cls: 'datepicker-time',
19460                 cn: [
19461                 {
19462                     tag: 'table',
19463                     cls: 'table-condensed',
19464                     cn:[
19465                     Roo.bootstrap.TimeField.content,
19466                     Roo.bootstrap.TimeField.footer
19467                     ]
19468                 }
19469                 ]
19470             }
19471         ]
19472     }
19473 });
19474
19475  
19476
19477  /*
19478  * - LGPL
19479  *
19480  * MonthField
19481  * 
19482  */
19483
19484 /**
19485  * @class Roo.bootstrap.MonthField
19486  * @extends Roo.bootstrap.Input
19487  * Bootstrap MonthField class
19488  * 
19489  * @cfg {String} language default en
19490  * 
19491  * @constructor
19492  * Create a new MonthField
19493  * @param {Object} config The config object
19494  */
19495
19496 Roo.bootstrap.MonthField = function(config){
19497     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19498     
19499     this.addEvents({
19500         /**
19501          * @event show
19502          * Fires when this field show.
19503          * @param {Roo.bootstrap.MonthField} this
19504          * @param {Mixed} date The date value
19505          */
19506         show : true,
19507         /**
19508          * @event show
19509          * Fires when this field hide.
19510          * @param {Roo.bootstrap.MonthField} this
19511          * @param {Mixed} date The date value
19512          */
19513         hide : true,
19514         /**
19515          * @event select
19516          * Fires when select a date.
19517          * @param {Roo.bootstrap.MonthField} this
19518          * @param {String} oldvalue The old value
19519          * @param {String} newvalue The new value
19520          */
19521         select : true
19522     });
19523 };
19524
19525 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19526     
19527     onRender: function(ct, position)
19528     {
19529         
19530         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19531         
19532         this.language = this.language || 'en';
19533         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19534         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19535         
19536         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19537         this.isInline = false;
19538         this.isInput = true;
19539         this.component = this.el.select('.add-on', true).first() || false;
19540         this.component = (this.component && this.component.length === 0) ? false : this.component;
19541         this.hasInput = this.component && this.inputEL().length;
19542         
19543         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19544         
19545         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19546         
19547         this.picker().on('mousedown', this.onMousedown, this);
19548         this.picker().on('click', this.onClick, this);
19549         
19550         this.picker().addClass('datepicker-dropdown');
19551         
19552         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19553             v.setStyle('width', '189px');
19554         });
19555         
19556         this.fillMonths();
19557         
19558         this.update();
19559         
19560         if(this.isInline) {
19561             this.show();
19562         }
19563         
19564     },
19565     
19566     setValue: function(v, suppressEvent)
19567     {   
19568         var o = this.getValue();
19569         
19570         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19571         
19572         this.update();
19573
19574         if(suppressEvent !== true){
19575             this.fireEvent('select', this, o, v);
19576         }
19577         
19578     },
19579     
19580     getValue: function()
19581     {
19582         return this.value;
19583     },
19584     
19585     onClick: function(e) 
19586     {
19587         e.stopPropagation();
19588         e.preventDefault();
19589         
19590         var target = e.getTarget();
19591         
19592         if(target.nodeName.toLowerCase() === 'i'){
19593             target = Roo.get(target).dom.parentNode;
19594         }
19595         
19596         var nodeName = target.nodeName;
19597         var className = target.className;
19598         var html = target.innerHTML;
19599         
19600         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19601             return;
19602         }
19603         
19604         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19605         
19606         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19607         
19608         this.hide();
19609                         
19610     },
19611     
19612     picker : function()
19613     {
19614         return this.pickerEl;
19615     },
19616     
19617     fillMonths: function()
19618     {    
19619         var i = 0;
19620         var months = this.picker().select('>.datepicker-months td', true).first();
19621         
19622         months.dom.innerHTML = '';
19623         
19624         while (i < 12) {
19625             var month = {
19626                 tag: 'span',
19627                 cls: 'month',
19628                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19629             };
19630             
19631             months.createChild(month);
19632         }
19633         
19634     },
19635     
19636     update: function()
19637     {
19638         var _this = this;
19639         
19640         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19641             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19642         }
19643         
19644         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19645             e.removeClass('active');
19646             
19647             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19648                 e.addClass('active');
19649             }
19650         })
19651     },
19652     
19653     place: function()
19654     {
19655         if(this.isInline) {
19656             return;
19657         }
19658         
19659         this.picker().removeClass(['bottom', 'top']);
19660         
19661         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19662             /*
19663              * place to the top of element!
19664              *
19665              */
19666             
19667             this.picker().addClass('top');
19668             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19669             
19670             return;
19671         }
19672         
19673         this.picker().addClass('bottom');
19674         
19675         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19676     },
19677     
19678     onFocus : function()
19679     {
19680         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19681         this.show();
19682     },
19683     
19684     onBlur : function()
19685     {
19686         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19687         
19688         var d = this.inputEl().getValue();
19689         
19690         this.setValue(d);
19691                 
19692         this.hide();
19693     },
19694     
19695     show : function()
19696     {
19697         this.picker().show();
19698         this.picker().select('>.datepicker-months', true).first().show();
19699         this.update();
19700         this.place();
19701         
19702         this.fireEvent('show', this, this.date);
19703     },
19704     
19705     hide : function()
19706     {
19707         if(this.isInline) {
19708             return;
19709         }
19710         this.picker().hide();
19711         this.fireEvent('hide', this, this.date);
19712         
19713     },
19714     
19715     onMousedown: function(e)
19716     {
19717         e.stopPropagation();
19718         e.preventDefault();
19719     },
19720     
19721     keyup: function(e)
19722     {
19723         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19724         this.update();
19725     },
19726
19727     fireKey: function(e)
19728     {
19729         if (!this.picker().isVisible()){
19730             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19731                 this.show();
19732             }
19733             return;
19734         }
19735         
19736         var dir;
19737         
19738         switch(e.keyCode){
19739             case 27: // escape
19740                 this.hide();
19741                 e.preventDefault();
19742                 break;
19743             case 37: // left
19744             case 39: // right
19745                 dir = e.keyCode == 37 ? -1 : 1;
19746                 
19747                 this.vIndex = this.vIndex + dir;
19748                 
19749                 if(this.vIndex < 0){
19750                     this.vIndex = 0;
19751                 }
19752                 
19753                 if(this.vIndex > 11){
19754                     this.vIndex = 11;
19755                 }
19756                 
19757                 if(isNaN(this.vIndex)){
19758                     this.vIndex = 0;
19759                 }
19760                 
19761                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19762                 
19763                 break;
19764             case 38: // up
19765             case 40: // down
19766                 
19767                 dir = e.keyCode == 38 ? -1 : 1;
19768                 
19769                 this.vIndex = this.vIndex + dir * 4;
19770                 
19771                 if(this.vIndex < 0){
19772                     this.vIndex = 0;
19773                 }
19774                 
19775                 if(this.vIndex > 11){
19776                     this.vIndex = 11;
19777                 }
19778                 
19779                 if(isNaN(this.vIndex)){
19780                     this.vIndex = 0;
19781                 }
19782                 
19783                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19784                 break;
19785                 
19786             case 13: // enter
19787                 
19788                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19789                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19790                 }
19791                 
19792                 this.hide();
19793                 e.preventDefault();
19794                 break;
19795             case 9: // tab
19796                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19797                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19798                 }
19799                 this.hide();
19800                 break;
19801             case 16: // shift
19802             case 17: // ctrl
19803             case 18: // alt
19804                 break;
19805             default :
19806                 this.hide();
19807                 
19808         }
19809     },
19810     
19811     remove: function() 
19812     {
19813         this.picker().remove();
19814     }
19815    
19816 });
19817
19818 Roo.apply(Roo.bootstrap.MonthField,  {
19819     
19820     content : {
19821         tag: 'tbody',
19822         cn: [
19823         {
19824             tag: 'tr',
19825             cn: [
19826             {
19827                 tag: 'td',
19828                 colspan: '7'
19829             }
19830             ]
19831         }
19832         ]
19833     },
19834     
19835     dates:{
19836         en: {
19837             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19838             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19839         }
19840     }
19841 });
19842
19843 Roo.apply(Roo.bootstrap.MonthField,  {
19844   
19845     template : {
19846         tag: 'div',
19847         cls: 'datepicker dropdown-menu roo-dynamic',
19848         cn: [
19849             {
19850                 tag: 'div',
19851                 cls: 'datepicker-months',
19852                 cn: [
19853                 {
19854                     tag: 'table',
19855                     cls: 'table-condensed',
19856                     cn:[
19857                         Roo.bootstrap.DateField.content
19858                     ]
19859                 }
19860                 ]
19861             }
19862         ]
19863     }
19864 });
19865
19866  
19867
19868  
19869  /*
19870  * - LGPL
19871  *
19872  * CheckBox
19873  * 
19874  */
19875
19876 /**
19877  * @class Roo.bootstrap.CheckBox
19878  * @extends Roo.bootstrap.Input
19879  * Bootstrap CheckBox class
19880  * 
19881  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19882  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19883  * @cfg {String} boxLabel The text that appears beside the checkbox
19884  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19885  * @cfg {Boolean} checked initnal the element
19886  * @cfg {Boolean} inline inline the element (default false)
19887  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19888  * 
19889  * @constructor
19890  * Create a new CheckBox
19891  * @param {Object} config The config object
19892  */
19893
19894 Roo.bootstrap.CheckBox = function(config){
19895     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19896    
19897     this.addEvents({
19898         /**
19899         * @event check
19900         * Fires when the element is checked or unchecked.
19901         * @param {Roo.bootstrap.CheckBox} this This input
19902         * @param {Boolean} checked The new checked value
19903         */
19904        check : true
19905     });
19906     
19907 };
19908
19909 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19910   
19911     inputType: 'checkbox',
19912     inputValue: 1,
19913     valueOff: 0,
19914     boxLabel: false,
19915     checked: false,
19916     weight : false,
19917     inline: false,
19918     
19919     getAutoCreate : function()
19920     {
19921         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19922         
19923         var id = Roo.id();
19924         
19925         var cfg = {};
19926         
19927         cfg.cls = 'form-group ' + this.inputType; //input-group
19928         
19929         if(this.inline){
19930             cfg.cls += ' ' + this.inputType + '-inline';
19931         }
19932         
19933         var input =  {
19934             tag: 'input',
19935             id : id,
19936             type : this.inputType,
19937             value : this.inputValue,
19938             cls : 'roo-' + this.inputType, //'form-box',
19939             placeholder : this.placeholder || ''
19940             
19941         };
19942         
19943         if(this.inputType != 'radio'){
19944             var hidden =  {
19945                 tag: 'input',
19946                 type : 'hidden',
19947                 cls : 'roo-hidden-value',
19948                 value : this.checked ? this.valueOff : this.inputValue
19949             };
19950         }
19951         
19952             
19953         if (this.weight) { // Validity check?
19954             cfg.cls += " " + this.inputType + "-" + this.weight;
19955         }
19956         
19957         if (this.disabled) {
19958             input.disabled=true;
19959         }
19960         
19961         if(this.checked){
19962             input.checked = this.checked;
19963             
19964         }
19965         
19966         
19967         if (this.name) {
19968             
19969             input.name = this.name;
19970             
19971             if(this.inputType != 'radio'){
19972                 hidden.name = this.name;
19973                 input.name = '_hidden_' + this.name;
19974             }
19975         }
19976         
19977         if (this.size) {
19978             input.cls += ' input-' + this.size;
19979         }
19980         
19981         var settings=this;
19982         
19983         ['xs','sm','md','lg'].map(function(size){
19984             if (settings[size]) {
19985                 cfg.cls += ' col-' + size + '-' + settings[size];
19986             }
19987         });
19988         
19989         var inputblock = input;
19990          
19991         if (this.before || this.after) {
19992             
19993             inputblock = {
19994                 cls : 'input-group',
19995                 cn :  [] 
19996             };
19997             
19998             if (this.before) {
19999                 inputblock.cn.push({
20000                     tag :'span',
20001                     cls : 'input-group-addon',
20002                     html : this.before
20003                 });
20004             }
20005             
20006             inputblock.cn.push(input);
20007             
20008             if(this.inputType != 'radio'){
20009                 inputblock.cn.push(hidden);
20010             }
20011             
20012             if (this.after) {
20013                 inputblock.cn.push({
20014                     tag :'span',
20015                     cls : 'input-group-addon',
20016                     html : this.after
20017                 });
20018             }
20019             
20020         }
20021         
20022         if (align ==='left' && this.fieldLabel.length) {
20023 //                Roo.log("left and has label");
20024             cfg.cn = [
20025                 {
20026                     tag: 'label',
20027                     'for' :  id,
20028                     cls : 'control-label',
20029                     html : this.fieldLabel
20030
20031                 },
20032                 {
20033                     cls : "", 
20034                     cn: [
20035                         inputblock
20036                     ]
20037                 }
20038             ];
20039             
20040             if(this.labelWidth > 12){
20041                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20042             }
20043             
20044             if(this.labelWidth < 13 && this.labelmd == 0){
20045                 this.labelmd = this.labelWidth;
20046             }
20047             
20048             if(this.labellg > 0){
20049                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20050                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20051             }
20052             
20053             if(this.labelmd > 0){
20054                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20055                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20056             }
20057             
20058             if(this.labelsm > 0){
20059                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20060                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20061             }
20062             
20063             if(this.labelxs > 0){
20064                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20065                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20066             }
20067             
20068         } else if ( this.fieldLabel.length) {
20069 //                Roo.log(" label");
20070                 cfg.cn = [
20071                    
20072                     {
20073                         tag: this.boxLabel ? 'span' : 'label',
20074                         'for': id,
20075                         cls: 'control-label box-input-label',
20076                         //cls : 'input-group-addon',
20077                         html : this.fieldLabel
20078                         
20079                     },
20080                     
20081                     inputblock
20082                     
20083                 ];
20084
20085         } else {
20086             
20087 //                Roo.log(" no label && no align");
20088                 cfg.cn = [  inputblock ] ;
20089                 
20090                 
20091         }
20092         
20093         if(this.boxLabel){
20094              var boxLabelCfg = {
20095                 tag: 'label',
20096                 //'for': id, // box label is handled by onclick - so no for...
20097                 cls: 'box-label',
20098                 html: this.boxLabel
20099             };
20100             
20101             if(this.tooltip){
20102                 boxLabelCfg.tooltip = this.tooltip;
20103             }
20104              
20105             cfg.cn.push(boxLabelCfg);
20106         }
20107         
20108         if(this.inputType != 'radio'){
20109             cfg.cn.push(hidden);
20110         }
20111         
20112         return cfg;
20113         
20114     },
20115     
20116     /**
20117      * return the real input element.
20118      */
20119     inputEl: function ()
20120     {
20121         return this.el.select('input.roo-' + this.inputType,true).first();
20122     },
20123     hiddenEl: function ()
20124     {
20125         return this.el.select('input.roo-hidden-value',true).first();
20126     },
20127     
20128     labelEl: function()
20129     {
20130         return this.el.select('label.control-label',true).first();
20131     },
20132     /* depricated... */
20133     
20134     label: function()
20135     {
20136         return this.labelEl();
20137     },
20138     
20139     boxLabelEl: function()
20140     {
20141         return this.el.select('label.box-label',true).first();
20142     },
20143     
20144     initEvents : function()
20145     {
20146 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20147         
20148         this.inputEl().on('click', this.onClick,  this);
20149         
20150         if (this.boxLabel) { 
20151             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20152         }
20153         
20154         this.startValue = this.getValue();
20155         
20156         if(this.groupId){
20157             Roo.bootstrap.CheckBox.register(this);
20158         }
20159     },
20160     
20161     onClick : function()
20162     {   
20163         this.setChecked(!this.checked);
20164     },
20165     
20166     setChecked : function(state,suppressEvent)
20167     {
20168         this.startValue = this.getValue();
20169
20170         if(this.inputType == 'radio'){
20171             
20172             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20173                 e.dom.checked = false;
20174             });
20175             
20176             this.inputEl().dom.checked = true;
20177             
20178             this.inputEl().dom.value = this.inputValue;
20179             
20180             if(suppressEvent !== true){
20181                 this.fireEvent('check', this, true);
20182             }
20183             
20184             this.validate();
20185             
20186             return;
20187         }
20188         
20189         this.checked = state;
20190         
20191         this.inputEl().dom.checked = state;
20192         
20193         
20194         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20195         
20196         if(suppressEvent !== true){
20197             this.fireEvent('check', this, state);
20198         }
20199         
20200         this.validate();
20201     },
20202     
20203     getValue : function()
20204     {
20205         if(this.inputType == 'radio'){
20206             return this.getGroupValue();
20207         }
20208         
20209         return this.hiddenEl().dom.value;
20210         
20211     },
20212     
20213     getGroupValue : function()
20214     {
20215         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20216             return '';
20217         }
20218         
20219         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20220     },
20221     
20222     setValue : function(v,suppressEvent)
20223     {
20224         if(this.inputType == 'radio'){
20225             this.setGroupValue(v, suppressEvent);
20226             return;
20227         }
20228         
20229         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20230         
20231         this.validate();
20232     },
20233     
20234     setGroupValue : function(v, suppressEvent)
20235     {
20236         this.startValue = this.getValue();
20237         
20238         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20239             e.dom.checked = false;
20240             
20241             if(e.dom.value == v){
20242                 e.dom.checked = true;
20243             }
20244         });
20245         
20246         if(suppressEvent !== true){
20247             this.fireEvent('check', this, true);
20248         }
20249
20250         this.validate();
20251         
20252         return;
20253     },
20254     
20255     validate : function()
20256     {
20257         if(
20258                 this.disabled || 
20259                 (this.inputType == 'radio' && this.validateRadio()) ||
20260                 (this.inputType == 'checkbox' && this.validateCheckbox())
20261         ){
20262             this.markValid();
20263             return true;
20264         }
20265         
20266         this.markInvalid();
20267         return false;
20268     },
20269     
20270     validateRadio : function()
20271     {
20272         if(this.allowBlank){
20273             return true;
20274         }
20275         
20276         var valid = false;
20277         
20278         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20279             if(!e.dom.checked){
20280                 return;
20281             }
20282             
20283             valid = true;
20284             
20285             return false;
20286         });
20287         
20288         return valid;
20289     },
20290     
20291     validateCheckbox : function()
20292     {
20293         if(!this.groupId){
20294             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20295         }
20296         
20297         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20298         
20299         if(!group){
20300             return false;
20301         }
20302         
20303         var r = false;
20304         
20305         for(var i in group){
20306             if(r){
20307                 break;
20308             }
20309             
20310             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20311         }
20312         
20313         return r;
20314     },
20315     
20316     /**
20317      * Mark this field as valid
20318      */
20319     markValid : function()
20320     {
20321         var _this = this;
20322         
20323         this.fireEvent('valid', this);
20324         
20325         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20326         
20327         if(this.groupId){
20328             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20329         }
20330         
20331         if(label){
20332             label.markValid();
20333         }
20334
20335         if(this.inputType == 'radio'){
20336             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20337                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20338                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20339             });
20340             
20341             return;
20342         }
20343         
20344         if(!this.groupId){
20345             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20346             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20347             return;
20348         }
20349         
20350         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20351             
20352         if(!group){
20353             return;
20354         }
20355         
20356         for(var i in group){
20357             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20358             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20359         }
20360     },
20361     
20362      /**
20363      * Mark this field as invalid
20364      * @param {String} msg The validation message
20365      */
20366     markInvalid : function(msg)
20367     {
20368         if(this.allowBlank){
20369             return;
20370         }
20371         
20372         var _this = this;
20373         
20374         this.fireEvent('invalid', this, msg);
20375         
20376         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20377         
20378         if(this.groupId){
20379             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20380         }
20381         
20382         if(label){
20383             label.markInvalid();
20384         }
20385             
20386         if(this.inputType == 'radio'){
20387             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20388                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20389                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20390             });
20391             
20392             return;
20393         }
20394         
20395         if(!this.groupId){
20396             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20397             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20398             return;
20399         }
20400         
20401         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20402         
20403         if(!group){
20404             return;
20405         }
20406         
20407         for(var i in group){
20408             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20409             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20410         }
20411         
20412     },
20413     
20414     clearInvalid : function()
20415     {
20416         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20417         
20418         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20419         
20420         if (label) {
20421             label.iconEl.removeClass(label.validClass);
20422             label.iconEl.removeClass(label.invalidClass);
20423         }
20424     },
20425     
20426     disable : function()
20427     {
20428         if(this.inputType != 'radio'){
20429             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20430             return;
20431         }
20432         
20433         var _this = this;
20434         
20435         if(this.rendered){
20436             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20437                 _this.getActionEl().addClass(this.disabledClass);
20438                 e.dom.disabled = true;
20439             });
20440         }
20441         
20442         this.disabled = true;
20443         this.fireEvent("disable", this);
20444         return this;
20445     },
20446
20447     enable : function()
20448     {
20449         if(this.inputType != 'radio'){
20450             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20451             return;
20452         }
20453         
20454         var _this = this;
20455         
20456         if(this.rendered){
20457             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20458                 _this.getActionEl().removeClass(this.disabledClass);
20459                 e.dom.disabled = false;
20460             });
20461         }
20462         
20463         this.disabled = false;
20464         this.fireEvent("enable", this);
20465         return this;
20466     }
20467
20468 });
20469
20470 Roo.apply(Roo.bootstrap.CheckBox, {
20471     
20472     groups: {},
20473     
20474      /**
20475     * register a CheckBox Group
20476     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20477     */
20478     register : function(checkbox)
20479     {
20480         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20481             this.groups[checkbox.groupId] = {};
20482         }
20483         
20484         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20485             return;
20486         }
20487         
20488         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20489         
20490     },
20491     /**
20492     * fetch a CheckBox Group based on the group ID
20493     * @param {string} the group ID
20494     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20495     */
20496     get: function(groupId) {
20497         if (typeof(this.groups[groupId]) == 'undefined') {
20498             return false;
20499         }
20500         
20501         return this.groups[groupId] ;
20502     }
20503     
20504     
20505 });
20506 /*
20507  * - LGPL
20508  *
20509  * RadioItem
20510  * 
20511  */
20512
20513 /**
20514  * @class Roo.bootstrap.Radio
20515  * @extends Roo.bootstrap.Component
20516  * Bootstrap Radio class
20517  * @cfg {String} boxLabel - the label associated
20518  * @cfg {String} value - the value of radio
20519  * 
20520  * @constructor
20521  * Create a new Radio
20522  * @param {Object} config The config object
20523  */
20524 Roo.bootstrap.Radio = function(config){
20525     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20526     
20527 };
20528
20529 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20530     
20531     boxLabel : '',
20532     
20533     value : '',
20534     
20535     getAutoCreate : function()
20536     {
20537         var cfg = {
20538             tag : 'div',
20539             cls : 'form-group radio',
20540             cn : [
20541                 {
20542                     tag : 'label',
20543                     cls : 'box-label',
20544                     html : this.boxLabel
20545                 }
20546             ]
20547         };
20548         
20549         return cfg;
20550     },
20551     
20552     initEvents : function() 
20553     {
20554         this.parent().register(this);
20555         
20556         this.el.on('click', this.onClick, this);
20557         
20558     },
20559     
20560     onClick : function()
20561     {
20562         this.setChecked(true);
20563     },
20564     
20565     setChecked : function(state, suppressEvent)
20566     {
20567         this.parent().setValue(this.value, suppressEvent);
20568         
20569     }
20570     
20571 });
20572  
20573
20574  //<script type="text/javascript">
20575
20576 /*
20577  * Based  Ext JS Library 1.1.1
20578  * Copyright(c) 2006-2007, Ext JS, LLC.
20579  * LGPL
20580  *
20581  */
20582  
20583 /**
20584  * @class Roo.HtmlEditorCore
20585  * @extends Roo.Component
20586  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20587  *
20588  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20589  */
20590
20591 Roo.HtmlEditorCore = function(config){
20592     
20593     
20594     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20595     
20596     
20597     this.addEvents({
20598         /**
20599          * @event initialize
20600          * Fires when the editor is fully initialized (including the iframe)
20601          * @param {Roo.HtmlEditorCore} this
20602          */
20603         initialize: true,
20604         /**
20605          * @event activate
20606          * Fires when the editor is first receives the focus. Any insertion must wait
20607          * until after this event.
20608          * @param {Roo.HtmlEditorCore} this
20609          */
20610         activate: true,
20611          /**
20612          * @event beforesync
20613          * Fires before the textarea is updated with content from the editor iframe. Return false
20614          * to cancel the sync.
20615          * @param {Roo.HtmlEditorCore} this
20616          * @param {String} html
20617          */
20618         beforesync: true,
20619          /**
20620          * @event beforepush
20621          * Fires before the iframe editor is updated with content from the textarea. Return false
20622          * to cancel the push.
20623          * @param {Roo.HtmlEditorCore} this
20624          * @param {String} html
20625          */
20626         beforepush: true,
20627          /**
20628          * @event sync
20629          * Fires when the textarea is updated with content from the editor iframe.
20630          * @param {Roo.HtmlEditorCore} this
20631          * @param {String} html
20632          */
20633         sync: true,
20634          /**
20635          * @event push
20636          * Fires when the iframe editor is updated with content from the textarea.
20637          * @param {Roo.HtmlEditorCore} this
20638          * @param {String} html
20639          */
20640         push: true,
20641         
20642         /**
20643          * @event editorevent
20644          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20645          * @param {Roo.HtmlEditorCore} this
20646          */
20647         editorevent: true
20648         
20649     });
20650     
20651     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20652     
20653     // defaults : white / black...
20654     this.applyBlacklists();
20655     
20656     
20657     
20658 };
20659
20660
20661 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20662
20663
20664      /**
20665      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20666      */
20667     
20668     owner : false,
20669     
20670      /**
20671      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20672      *                        Roo.resizable.
20673      */
20674     resizable : false,
20675      /**
20676      * @cfg {Number} height (in pixels)
20677      */   
20678     height: 300,
20679    /**
20680      * @cfg {Number} width (in pixels)
20681      */   
20682     width: 500,
20683     
20684     /**
20685      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20686      * 
20687      */
20688     stylesheets: false,
20689     
20690     // id of frame..
20691     frameId: false,
20692     
20693     // private properties
20694     validationEvent : false,
20695     deferHeight: true,
20696     initialized : false,
20697     activated : false,
20698     sourceEditMode : false,
20699     onFocus : Roo.emptyFn,
20700     iframePad:3,
20701     hideMode:'offsets',
20702     
20703     clearUp: true,
20704     
20705     // blacklist + whitelisted elements..
20706     black: false,
20707     white: false,
20708      
20709     
20710
20711     /**
20712      * Protected method that will not generally be called directly. It
20713      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20714      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20715      */
20716     getDocMarkup : function(){
20717         // body styles..
20718         var st = '';
20719         
20720         // inherit styels from page...?? 
20721         if (this.stylesheets === false) {
20722             
20723             Roo.get(document.head).select('style').each(function(node) {
20724                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20725             });
20726             
20727             Roo.get(document.head).select('link').each(function(node) { 
20728                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20729             });
20730             
20731         } else if (!this.stylesheets.length) {
20732                 // simple..
20733                 st = '<style type="text/css">' +
20734                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20735                    '</style>';
20736         } else { 
20737             
20738         }
20739         
20740         st +=  '<style type="text/css">' +
20741             'IMG { cursor: pointer } ' +
20742         '</style>';
20743
20744         
20745         return '<html><head>' + st  +
20746             //<style type="text/css">' +
20747             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20748             //'</style>' +
20749             ' </head><body class="roo-htmleditor-body"></body></html>';
20750     },
20751
20752     // private
20753     onRender : function(ct, position)
20754     {
20755         var _t = this;
20756         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20757         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20758         
20759         
20760         this.el.dom.style.border = '0 none';
20761         this.el.dom.setAttribute('tabIndex', -1);
20762         this.el.addClass('x-hidden hide');
20763         
20764         
20765         
20766         if(Roo.isIE){ // fix IE 1px bogus margin
20767             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20768         }
20769        
20770         
20771         this.frameId = Roo.id();
20772         
20773          
20774         
20775         var iframe = this.owner.wrap.createChild({
20776             tag: 'iframe',
20777             cls: 'form-control', // bootstrap..
20778             id: this.frameId,
20779             name: this.frameId,
20780             frameBorder : 'no',
20781             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20782         }, this.el
20783         );
20784         
20785         
20786         this.iframe = iframe.dom;
20787
20788          this.assignDocWin();
20789         
20790         this.doc.designMode = 'on';
20791        
20792         this.doc.open();
20793         this.doc.write(this.getDocMarkup());
20794         this.doc.close();
20795
20796         
20797         var task = { // must defer to wait for browser to be ready
20798             run : function(){
20799                 //console.log("run task?" + this.doc.readyState);
20800                 this.assignDocWin();
20801                 if(this.doc.body || this.doc.readyState == 'complete'){
20802                     try {
20803                         this.doc.designMode="on";
20804                     } catch (e) {
20805                         return;
20806                     }
20807                     Roo.TaskMgr.stop(task);
20808                     this.initEditor.defer(10, this);
20809                 }
20810             },
20811             interval : 10,
20812             duration: 10000,
20813             scope: this
20814         };
20815         Roo.TaskMgr.start(task);
20816
20817     },
20818
20819     // private
20820     onResize : function(w, h)
20821     {
20822          Roo.log('resize: ' +w + ',' + h );
20823         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20824         if(!this.iframe){
20825             return;
20826         }
20827         if(typeof w == 'number'){
20828             
20829             this.iframe.style.width = w + 'px';
20830         }
20831         if(typeof h == 'number'){
20832             
20833             this.iframe.style.height = h + 'px';
20834             if(this.doc){
20835                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20836             }
20837         }
20838         
20839     },
20840
20841     /**
20842      * Toggles the editor between standard and source edit mode.
20843      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20844      */
20845     toggleSourceEdit : function(sourceEditMode){
20846         
20847         this.sourceEditMode = sourceEditMode === true;
20848         
20849         if(this.sourceEditMode){
20850  
20851             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20852             
20853         }else{
20854             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20855             //this.iframe.className = '';
20856             this.deferFocus();
20857         }
20858         //this.setSize(this.owner.wrap.getSize());
20859         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20860     },
20861
20862     
20863   
20864
20865     /**
20866      * Protected method that will not generally be called directly. If you need/want
20867      * custom HTML cleanup, this is the method you should override.
20868      * @param {String} html The HTML to be cleaned
20869      * return {String} The cleaned HTML
20870      */
20871     cleanHtml : function(html){
20872         html = String(html);
20873         if(html.length > 5){
20874             if(Roo.isSafari){ // strip safari nonsense
20875                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20876             }
20877         }
20878         if(html == '&nbsp;'){
20879             html = '';
20880         }
20881         return html;
20882     },
20883
20884     /**
20885      * HTML Editor -> Textarea
20886      * Protected method that will not generally be called directly. Syncs the contents
20887      * of the editor iframe with the textarea.
20888      */
20889     syncValue : function(){
20890         if(this.initialized){
20891             var bd = (this.doc.body || this.doc.documentElement);
20892             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20893             var html = bd.innerHTML;
20894             if(Roo.isSafari){
20895                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20896                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20897                 if(m && m[1]){
20898                     html = '<div style="'+m[0]+'">' + html + '</div>';
20899                 }
20900             }
20901             html = this.cleanHtml(html);
20902             // fix up the special chars.. normaly like back quotes in word...
20903             // however we do not want to do this with chinese..
20904             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20905                 var cc = b.charCodeAt();
20906                 if (
20907                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20908                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20909                     (cc >= 0xf900 && cc < 0xfb00 )
20910                 ) {
20911                         return b;
20912                 }
20913                 return "&#"+cc+";" 
20914             });
20915             if(this.owner.fireEvent('beforesync', this, html) !== false){
20916                 this.el.dom.value = html;
20917                 this.owner.fireEvent('sync', this, html);
20918             }
20919         }
20920     },
20921
20922     /**
20923      * Protected method that will not generally be called directly. Pushes the value of the textarea
20924      * into the iframe editor.
20925      */
20926     pushValue : function(){
20927         if(this.initialized){
20928             var v = this.el.dom.value.trim();
20929             
20930 //            if(v.length < 1){
20931 //                v = '&#160;';
20932 //            }
20933             
20934             if(this.owner.fireEvent('beforepush', this, v) !== false){
20935                 var d = (this.doc.body || this.doc.documentElement);
20936                 d.innerHTML = v;
20937                 this.cleanUpPaste();
20938                 this.el.dom.value = d.innerHTML;
20939                 this.owner.fireEvent('push', this, v);
20940             }
20941         }
20942     },
20943
20944     // private
20945     deferFocus : function(){
20946         this.focus.defer(10, this);
20947     },
20948
20949     // doc'ed in Field
20950     focus : function(){
20951         if(this.win && !this.sourceEditMode){
20952             this.win.focus();
20953         }else{
20954             this.el.focus();
20955         }
20956     },
20957     
20958     assignDocWin: function()
20959     {
20960         var iframe = this.iframe;
20961         
20962          if(Roo.isIE){
20963             this.doc = iframe.contentWindow.document;
20964             this.win = iframe.contentWindow;
20965         } else {
20966 //            if (!Roo.get(this.frameId)) {
20967 //                return;
20968 //            }
20969 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20970 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20971             
20972             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20973                 return;
20974             }
20975             
20976             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20977             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20978         }
20979     },
20980     
20981     // private
20982     initEditor : function(){
20983         //console.log("INIT EDITOR");
20984         this.assignDocWin();
20985         
20986         
20987         
20988         this.doc.designMode="on";
20989         this.doc.open();
20990         this.doc.write(this.getDocMarkup());
20991         this.doc.close();
20992         
20993         var dbody = (this.doc.body || this.doc.documentElement);
20994         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20995         // this copies styles from the containing element into thsi one..
20996         // not sure why we need all of this..
20997         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20998         
20999         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21000         //ss['background-attachment'] = 'fixed'; // w3c
21001         dbody.bgProperties = 'fixed'; // ie
21002         //Roo.DomHelper.applyStyles(dbody, ss);
21003         Roo.EventManager.on(this.doc, {
21004             //'mousedown': this.onEditorEvent,
21005             'mouseup': this.onEditorEvent,
21006             'dblclick': this.onEditorEvent,
21007             'click': this.onEditorEvent,
21008             'keyup': this.onEditorEvent,
21009             buffer:100,
21010             scope: this
21011         });
21012         if(Roo.isGecko){
21013             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21014         }
21015         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21016             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21017         }
21018         this.initialized = true;
21019
21020         this.owner.fireEvent('initialize', this);
21021         this.pushValue();
21022     },
21023
21024     // private
21025     onDestroy : function(){
21026         
21027         
21028         
21029         if(this.rendered){
21030             
21031             //for (var i =0; i < this.toolbars.length;i++) {
21032             //    // fixme - ask toolbars for heights?
21033             //    this.toolbars[i].onDestroy();
21034            // }
21035             
21036             //this.wrap.dom.innerHTML = '';
21037             //this.wrap.remove();
21038         }
21039     },
21040
21041     // private
21042     onFirstFocus : function(){
21043         
21044         this.assignDocWin();
21045         
21046         
21047         this.activated = true;
21048          
21049     
21050         if(Roo.isGecko){ // prevent silly gecko errors
21051             this.win.focus();
21052             var s = this.win.getSelection();
21053             if(!s.focusNode || s.focusNode.nodeType != 3){
21054                 var r = s.getRangeAt(0);
21055                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21056                 r.collapse(true);
21057                 this.deferFocus();
21058             }
21059             try{
21060                 this.execCmd('useCSS', true);
21061                 this.execCmd('styleWithCSS', false);
21062             }catch(e){}
21063         }
21064         this.owner.fireEvent('activate', this);
21065     },
21066
21067     // private
21068     adjustFont: function(btn){
21069         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21070         //if(Roo.isSafari){ // safari
21071         //    adjust *= 2;
21072        // }
21073         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21074         if(Roo.isSafari){ // safari
21075             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21076             v =  (v < 10) ? 10 : v;
21077             v =  (v > 48) ? 48 : v;
21078             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21079             
21080         }
21081         
21082         
21083         v = Math.max(1, v+adjust);
21084         
21085         this.execCmd('FontSize', v  );
21086     },
21087
21088     onEditorEvent : function(e)
21089     {
21090         this.owner.fireEvent('editorevent', this, e);
21091       //  this.updateToolbar();
21092         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21093     },
21094
21095     insertTag : function(tg)
21096     {
21097         // could be a bit smarter... -> wrap the current selected tRoo..
21098         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21099             
21100             range = this.createRange(this.getSelection());
21101             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21102             wrappingNode.appendChild(range.extractContents());
21103             range.insertNode(wrappingNode);
21104
21105             return;
21106             
21107             
21108             
21109         }
21110         this.execCmd("formatblock",   tg);
21111         
21112     },
21113     
21114     insertText : function(txt)
21115     {
21116         
21117         
21118         var range = this.createRange();
21119         range.deleteContents();
21120                //alert(Sender.getAttribute('label'));
21121                
21122         range.insertNode(this.doc.createTextNode(txt));
21123     } ,
21124     
21125      
21126
21127     /**
21128      * Executes a Midas editor command on the editor document and performs necessary focus and
21129      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21130      * @param {String} cmd The Midas command
21131      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21132      */
21133     relayCmd : function(cmd, value){
21134         this.win.focus();
21135         this.execCmd(cmd, value);
21136         this.owner.fireEvent('editorevent', this);
21137         //this.updateToolbar();
21138         this.owner.deferFocus();
21139     },
21140
21141     /**
21142      * Executes a Midas editor command directly on the editor document.
21143      * For visual commands, you should use {@link #relayCmd} instead.
21144      * <b>This should only be called after the editor is initialized.</b>
21145      * @param {String} cmd The Midas command
21146      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21147      */
21148     execCmd : function(cmd, value){
21149         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21150         this.syncValue();
21151     },
21152  
21153  
21154    
21155     /**
21156      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21157      * to insert tRoo.
21158      * @param {String} text | dom node.. 
21159      */
21160     insertAtCursor : function(text)
21161     {
21162         
21163         
21164         
21165         if(!this.activated){
21166             return;
21167         }
21168         /*
21169         if(Roo.isIE){
21170             this.win.focus();
21171             var r = this.doc.selection.createRange();
21172             if(r){
21173                 r.collapse(true);
21174                 r.pasteHTML(text);
21175                 this.syncValue();
21176                 this.deferFocus();
21177             
21178             }
21179             return;
21180         }
21181         */
21182         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21183             this.win.focus();
21184             
21185             
21186             // from jquery ui (MIT licenced)
21187             var range, node;
21188             var win = this.win;
21189             
21190             if (win.getSelection && win.getSelection().getRangeAt) {
21191                 range = win.getSelection().getRangeAt(0);
21192                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21193                 range.insertNode(node);
21194             } else if (win.document.selection && win.document.selection.createRange) {
21195                 // no firefox support
21196                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21197                 win.document.selection.createRange().pasteHTML(txt);
21198             } else {
21199                 // no firefox support
21200                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21201                 this.execCmd('InsertHTML', txt);
21202             } 
21203             
21204             this.syncValue();
21205             
21206             this.deferFocus();
21207         }
21208     },
21209  // private
21210     mozKeyPress : function(e){
21211         if(e.ctrlKey){
21212             var c = e.getCharCode(), cmd;
21213           
21214             if(c > 0){
21215                 c = String.fromCharCode(c).toLowerCase();
21216                 switch(c){
21217                     case 'b':
21218                         cmd = 'bold';
21219                         break;
21220                     case 'i':
21221                         cmd = 'italic';
21222                         break;
21223                     
21224                     case 'u':
21225                         cmd = 'underline';
21226                         break;
21227                     
21228                     case 'v':
21229                         this.cleanUpPaste.defer(100, this);
21230                         return;
21231                         
21232                 }
21233                 if(cmd){
21234                     this.win.focus();
21235                     this.execCmd(cmd);
21236                     this.deferFocus();
21237                     e.preventDefault();
21238                 }
21239                 
21240             }
21241         }
21242     },
21243
21244     // private
21245     fixKeys : function(){ // load time branching for fastest keydown performance
21246         if(Roo.isIE){
21247             return function(e){
21248                 var k = e.getKey(), r;
21249                 if(k == e.TAB){
21250                     e.stopEvent();
21251                     r = this.doc.selection.createRange();
21252                     if(r){
21253                         r.collapse(true);
21254                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21255                         this.deferFocus();
21256                     }
21257                     return;
21258                 }
21259                 
21260                 if(k == e.ENTER){
21261                     r = this.doc.selection.createRange();
21262                     if(r){
21263                         var target = r.parentElement();
21264                         if(!target || target.tagName.toLowerCase() != 'li'){
21265                             e.stopEvent();
21266                             r.pasteHTML('<br />');
21267                             r.collapse(false);
21268                             r.select();
21269                         }
21270                     }
21271                 }
21272                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21273                     this.cleanUpPaste.defer(100, this);
21274                     return;
21275                 }
21276                 
21277                 
21278             };
21279         }else if(Roo.isOpera){
21280             return function(e){
21281                 var k = e.getKey();
21282                 if(k == e.TAB){
21283                     e.stopEvent();
21284                     this.win.focus();
21285                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21286                     this.deferFocus();
21287                 }
21288                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21289                     this.cleanUpPaste.defer(100, this);
21290                     return;
21291                 }
21292                 
21293             };
21294         }else if(Roo.isSafari){
21295             return function(e){
21296                 var k = e.getKey();
21297                 
21298                 if(k == e.TAB){
21299                     e.stopEvent();
21300                     this.execCmd('InsertText','\t');
21301                     this.deferFocus();
21302                     return;
21303                 }
21304                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21305                     this.cleanUpPaste.defer(100, this);
21306                     return;
21307                 }
21308                 
21309              };
21310         }
21311     }(),
21312     
21313     getAllAncestors: function()
21314     {
21315         var p = this.getSelectedNode();
21316         var a = [];
21317         if (!p) {
21318             a.push(p); // push blank onto stack..
21319             p = this.getParentElement();
21320         }
21321         
21322         
21323         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21324             a.push(p);
21325             p = p.parentNode;
21326         }
21327         a.push(this.doc.body);
21328         return a;
21329     },
21330     lastSel : false,
21331     lastSelNode : false,
21332     
21333     
21334     getSelection : function() 
21335     {
21336         this.assignDocWin();
21337         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21338     },
21339     
21340     getSelectedNode: function() 
21341     {
21342         // this may only work on Gecko!!!
21343         
21344         // should we cache this!!!!
21345         
21346         
21347         
21348          
21349         var range = this.createRange(this.getSelection()).cloneRange();
21350         
21351         if (Roo.isIE) {
21352             var parent = range.parentElement();
21353             while (true) {
21354                 var testRange = range.duplicate();
21355                 testRange.moveToElementText(parent);
21356                 if (testRange.inRange(range)) {
21357                     break;
21358                 }
21359                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21360                     break;
21361                 }
21362                 parent = parent.parentElement;
21363             }
21364             return parent;
21365         }
21366         
21367         // is ancestor a text element.
21368         var ac =  range.commonAncestorContainer;
21369         if (ac.nodeType == 3) {
21370             ac = ac.parentNode;
21371         }
21372         
21373         var ar = ac.childNodes;
21374          
21375         var nodes = [];
21376         var other_nodes = [];
21377         var has_other_nodes = false;
21378         for (var i=0;i<ar.length;i++) {
21379             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21380                 continue;
21381             }
21382             // fullly contained node.
21383             
21384             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21385                 nodes.push(ar[i]);
21386                 continue;
21387             }
21388             
21389             // probably selected..
21390             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21391                 other_nodes.push(ar[i]);
21392                 continue;
21393             }
21394             // outer..
21395             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21396                 continue;
21397             }
21398             
21399             
21400             has_other_nodes = true;
21401         }
21402         if (!nodes.length && other_nodes.length) {
21403             nodes= other_nodes;
21404         }
21405         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21406             return false;
21407         }
21408         
21409         return nodes[0];
21410     },
21411     createRange: function(sel)
21412     {
21413         // this has strange effects when using with 
21414         // top toolbar - not sure if it's a great idea.
21415         //this.editor.contentWindow.focus();
21416         if (typeof sel != "undefined") {
21417             try {
21418                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21419             } catch(e) {
21420                 return this.doc.createRange();
21421             }
21422         } else {
21423             return this.doc.createRange();
21424         }
21425     },
21426     getParentElement: function()
21427     {
21428         
21429         this.assignDocWin();
21430         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21431         
21432         var range = this.createRange(sel);
21433          
21434         try {
21435             var p = range.commonAncestorContainer;
21436             while (p.nodeType == 3) { // text node
21437                 p = p.parentNode;
21438             }
21439             return p;
21440         } catch (e) {
21441             return null;
21442         }
21443     
21444     },
21445     /***
21446      *
21447      * Range intersection.. the hard stuff...
21448      *  '-1' = before
21449      *  '0' = hits..
21450      *  '1' = after.
21451      *         [ -- selected range --- ]
21452      *   [fail]                        [fail]
21453      *
21454      *    basically..
21455      *      if end is before start or  hits it. fail.
21456      *      if start is after end or hits it fail.
21457      *
21458      *   if either hits (but other is outside. - then it's not 
21459      *   
21460      *    
21461      **/
21462     
21463     
21464     // @see http://www.thismuchiknow.co.uk/?p=64.
21465     rangeIntersectsNode : function(range, node)
21466     {
21467         var nodeRange = node.ownerDocument.createRange();
21468         try {
21469             nodeRange.selectNode(node);
21470         } catch (e) {
21471             nodeRange.selectNodeContents(node);
21472         }
21473     
21474         var rangeStartRange = range.cloneRange();
21475         rangeStartRange.collapse(true);
21476     
21477         var rangeEndRange = range.cloneRange();
21478         rangeEndRange.collapse(false);
21479     
21480         var nodeStartRange = nodeRange.cloneRange();
21481         nodeStartRange.collapse(true);
21482     
21483         var nodeEndRange = nodeRange.cloneRange();
21484         nodeEndRange.collapse(false);
21485     
21486         return rangeStartRange.compareBoundaryPoints(
21487                  Range.START_TO_START, nodeEndRange) == -1 &&
21488                rangeEndRange.compareBoundaryPoints(
21489                  Range.START_TO_START, nodeStartRange) == 1;
21490         
21491          
21492     },
21493     rangeCompareNode : function(range, node)
21494     {
21495         var nodeRange = node.ownerDocument.createRange();
21496         try {
21497             nodeRange.selectNode(node);
21498         } catch (e) {
21499             nodeRange.selectNodeContents(node);
21500         }
21501         
21502         
21503         range.collapse(true);
21504     
21505         nodeRange.collapse(true);
21506      
21507         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21508         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21509          
21510         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21511         
21512         var nodeIsBefore   =  ss == 1;
21513         var nodeIsAfter    = ee == -1;
21514         
21515         if (nodeIsBefore && nodeIsAfter) {
21516             return 0; // outer
21517         }
21518         if (!nodeIsBefore && nodeIsAfter) {
21519             return 1; //right trailed.
21520         }
21521         
21522         if (nodeIsBefore && !nodeIsAfter) {
21523             return 2;  // left trailed.
21524         }
21525         // fully contined.
21526         return 3;
21527     },
21528
21529     // private? - in a new class?
21530     cleanUpPaste :  function()
21531     {
21532         // cleans up the whole document..
21533         Roo.log('cleanuppaste');
21534         
21535         this.cleanUpChildren(this.doc.body);
21536         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21537         if (clean != this.doc.body.innerHTML) {
21538             this.doc.body.innerHTML = clean;
21539         }
21540         
21541     },
21542     
21543     cleanWordChars : function(input) {// change the chars to hex code
21544         var he = Roo.HtmlEditorCore;
21545         
21546         var output = input;
21547         Roo.each(he.swapCodes, function(sw) { 
21548             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21549             
21550             output = output.replace(swapper, sw[1]);
21551         });
21552         
21553         return output;
21554     },
21555     
21556     
21557     cleanUpChildren : function (n)
21558     {
21559         if (!n.childNodes.length) {
21560             return;
21561         }
21562         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21563            this.cleanUpChild(n.childNodes[i]);
21564         }
21565     },
21566     
21567     
21568         
21569     
21570     cleanUpChild : function (node)
21571     {
21572         var ed = this;
21573         //console.log(node);
21574         if (node.nodeName == "#text") {
21575             // clean up silly Windows -- stuff?
21576             return; 
21577         }
21578         if (node.nodeName == "#comment") {
21579             node.parentNode.removeChild(node);
21580             // clean up silly Windows -- stuff?
21581             return; 
21582         }
21583         var lcname = node.tagName.toLowerCase();
21584         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21585         // whitelist of tags..
21586         
21587         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21588             // remove node.
21589             node.parentNode.removeChild(node);
21590             return;
21591             
21592         }
21593         
21594         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21595         
21596         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21597         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21598         
21599         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21600         //    remove_keep_children = true;
21601         //}
21602         
21603         if (remove_keep_children) {
21604             this.cleanUpChildren(node);
21605             // inserts everything just before this node...
21606             while (node.childNodes.length) {
21607                 var cn = node.childNodes[0];
21608                 node.removeChild(cn);
21609                 node.parentNode.insertBefore(cn, node);
21610             }
21611             node.parentNode.removeChild(node);
21612             return;
21613         }
21614         
21615         if (!node.attributes || !node.attributes.length) {
21616             this.cleanUpChildren(node);
21617             return;
21618         }
21619         
21620         function cleanAttr(n,v)
21621         {
21622             
21623             if (v.match(/^\./) || v.match(/^\//)) {
21624                 return;
21625             }
21626             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21627                 return;
21628             }
21629             if (v.match(/^#/)) {
21630                 return;
21631             }
21632 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21633             node.removeAttribute(n);
21634             
21635         }
21636         
21637         var cwhite = this.cwhite;
21638         var cblack = this.cblack;
21639             
21640         function cleanStyle(n,v)
21641         {
21642             if (v.match(/expression/)) { //XSS?? should we even bother..
21643                 node.removeAttribute(n);
21644                 return;
21645             }
21646             
21647             var parts = v.split(/;/);
21648             var clean = [];
21649             
21650             Roo.each(parts, function(p) {
21651                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21652                 if (!p.length) {
21653                     return true;
21654                 }
21655                 var l = p.split(':').shift().replace(/\s+/g,'');
21656                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21657                 
21658                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21659 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21660                     //node.removeAttribute(n);
21661                     return true;
21662                 }
21663                 //Roo.log()
21664                 // only allow 'c whitelisted system attributes'
21665                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21666 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21667                     //node.removeAttribute(n);
21668                     return true;
21669                 }
21670                 
21671                 
21672                  
21673                 
21674                 clean.push(p);
21675                 return true;
21676             });
21677             if (clean.length) { 
21678                 node.setAttribute(n, clean.join(';'));
21679             } else {
21680                 node.removeAttribute(n);
21681             }
21682             
21683         }
21684         
21685         
21686         for (var i = node.attributes.length-1; i > -1 ; i--) {
21687             var a = node.attributes[i];
21688             //console.log(a);
21689             
21690             if (a.name.toLowerCase().substr(0,2)=='on')  {
21691                 node.removeAttribute(a.name);
21692                 continue;
21693             }
21694             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21695                 node.removeAttribute(a.name);
21696                 continue;
21697             }
21698             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21699                 cleanAttr(a.name,a.value); // fixme..
21700                 continue;
21701             }
21702             if (a.name == 'style') {
21703                 cleanStyle(a.name,a.value);
21704                 continue;
21705             }
21706             /// clean up MS crap..
21707             // tecnically this should be a list of valid class'es..
21708             
21709             
21710             if (a.name == 'class') {
21711                 if (a.value.match(/^Mso/)) {
21712                     node.className = '';
21713                 }
21714                 
21715                 if (a.value.match(/body/)) {
21716                     node.className = '';
21717                 }
21718                 continue;
21719             }
21720             
21721             // style cleanup!?
21722             // class cleanup?
21723             
21724         }
21725         
21726         
21727         this.cleanUpChildren(node);
21728         
21729         
21730     },
21731     
21732     /**
21733      * Clean up MS wordisms...
21734      */
21735     cleanWord : function(node)
21736     {
21737         
21738         
21739         if (!node) {
21740             this.cleanWord(this.doc.body);
21741             return;
21742         }
21743         if (node.nodeName == "#text") {
21744             // clean up silly Windows -- stuff?
21745             return; 
21746         }
21747         if (node.nodeName == "#comment") {
21748             node.parentNode.removeChild(node);
21749             // clean up silly Windows -- stuff?
21750             return; 
21751         }
21752         
21753         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21754             node.parentNode.removeChild(node);
21755             return;
21756         }
21757         
21758         // remove - but keep children..
21759         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21760             while (node.childNodes.length) {
21761                 var cn = node.childNodes[0];
21762                 node.removeChild(cn);
21763                 node.parentNode.insertBefore(cn, node);
21764             }
21765             node.parentNode.removeChild(node);
21766             this.iterateChildren(node, this.cleanWord);
21767             return;
21768         }
21769         // clean styles
21770         if (node.className.length) {
21771             
21772             var cn = node.className.split(/\W+/);
21773             var cna = [];
21774             Roo.each(cn, function(cls) {
21775                 if (cls.match(/Mso[a-zA-Z]+/)) {
21776                     return;
21777                 }
21778                 cna.push(cls);
21779             });
21780             node.className = cna.length ? cna.join(' ') : '';
21781             if (!cna.length) {
21782                 node.removeAttribute("class");
21783             }
21784         }
21785         
21786         if (node.hasAttribute("lang")) {
21787             node.removeAttribute("lang");
21788         }
21789         
21790         if (node.hasAttribute("style")) {
21791             
21792             var styles = node.getAttribute("style").split(";");
21793             var nstyle = [];
21794             Roo.each(styles, function(s) {
21795                 if (!s.match(/:/)) {
21796                     return;
21797                 }
21798                 var kv = s.split(":");
21799                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21800                     return;
21801                 }
21802                 // what ever is left... we allow.
21803                 nstyle.push(s);
21804             });
21805             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21806             if (!nstyle.length) {
21807                 node.removeAttribute('style');
21808             }
21809         }
21810         this.iterateChildren(node, this.cleanWord);
21811         
21812         
21813         
21814     },
21815     /**
21816      * iterateChildren of a Node, calling fn each time, using this as the scole..
21817      * @param {DomNode} node node to iterate children of.
21818      * @param {Function} fn method of this class to call on each item.
21819      */
21820     iterateChildren : function(node, fn)
21821     {
21822         if (!node.childNodes.length) {
21823                 return;
21824         }
21825         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21826            fn.call(this, node.childNodes[i])
21827         }
21828     },
21829     
21830     
21831     /**
21832      * cleanTableWidths.
21833      *
21834      * Quite often pasting from word etc.. results in tables with column and widths.
21835      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21836      *
21837      */
21838     cleanTableWidths : function(node)
21839     {
21840          
21841          
21842         if (!node) {
21843             this.cleanTableWidths(this.doc.body);
21844             return;
21845         }
21846         
21847         // ignore list...
21848         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21849             return; 
21850         }
21851         Roo.log(node.tagName);
21852         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21853             this.iterateChildren(node, this.cleanTableWidths);
21854             return;
21855         }
21856         if (node.hasAttribute('width')) {
21857             node.removeAttribute('width');
21858         }
21859         
21860          
21861         if (node.hasAttribute("style")) {
21862             // pretty basic...
21863             
21864             var styles = node.getAttribute("style").split(";");
21865             var nstyle = [];
21866             Roo.each(styles, function(s) {
21867                 if (!s.match(/:/)) {
21868                     return;
21869                 }
21870                 var kv = s.split(":");
21871                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21872                     return;
21873                 }
21874                 // what ever is left... we allow.
21875                 nstyle.push(s);
21876             });
21877             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21878             if (!nstyle.length) {
21879                 node.removeAttribute('style');
21880             }
21881         }
21882         
21883         this.iterateChildren(node, this.cleanTableWidths);
21884         
21885         
21886     },
21887     
21888     
21889     
21890     
21891     domToHTML : function(currentElement, depth, nopadtext) {
21892         
21893         depth = depth || 0;
21894         nopadtext = nopadtext || false;
21895     
21896         if (!currentElement) {
21897             return this.domToHTML(this.doc.body);
21898         }
21899         
21900         //Roo.log(currentElement);
21901         var j;
21902         var allText = false;
21903         var nodeName = currentElement.nodeName;
21904         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21905         
21906         if  (nodeName == '#text') {
21907             
21908             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21909         }
21910         
21911         
21912         var ret = '';
21913         if (nodeName != 'BODY') {
21914              
21915             var i = 0;
21916             // Prints the node tagName, such as <A>, <IMG>, etc
21917             if (tagName) {
21918                 var attr = [];
21919                 for(i = 0; i < currentElement.attributes.length;i++) {
21920                     // quoting?
21921                     var aname = currentElement.attributes.item(i).name;
21922                     if (!currentElement.attributes.item(i).value.length) {
21923                         continue;
21924                     }
21925                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21926                 }
21927                 
21928                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21929             } 
21930             else {
21931                 
21932                 // eack
21933             }
21934         } else {
21935             tagName = false;
21936         }
21937         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21938             return ret;
21939         }
21940         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21941             nopadtext = true;
21942         }
21943         
21944         
21945         // Traverse the tree
21946         i = 0;
21947         var currentElementChild = currentElement.childNodes.item(i);
21948         var allText = true;
21949         var innerHTML  = '';
21950         lastnode = '';
21951         while (currentElementChild) {
21952             // Formatting code (indent the tree so it looks nice on the screen)
21953             var nopad = nopadtext;
21954             if (lastnode == 'SPAN') {
21955                 nopad  = true;
21956             }
21957             // text
21958             if  (currentElementChild.nodeName == '#text') {
21959                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21960                 toadd = nopadtext ? toadd : toadd.trim();
21961                 if (!nopad && toadd.length > 80) {
21962                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21963                 }
21964                 innerHTML  += toadd;
21965                 
21966                 i++;
21967                 currentElementChild = currentElement.childNodes.item(i);
21968                 lastNode = '';
21969                 continue;
21970             }
21971             allText = false;
21972             
21973             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21974                 
21975             // Recursively traverse the tree structure of the child node
21976             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21977             lastnode = currentElementChild.nodeName;
21978             i++;
21979             currentElementChild=currentElement.childNodes.item(i);
21980         }
21981         
21982         ret += innerHTML;
21983         
21984         if (!allText) {
21985                 // The remaining code is mostly for formatting the tree
21986             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21987         }
21988         
21989         
21990         if (tagName) {
21991             ret+= "</"+tagName+">";
21992         }
21993         return ret;
21994         
21995     },
21996         
21997     applyBlacklists : function()
21998     {
21999         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22000         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22001         
22002         this.white = [];
22003         this.black = [];
22004         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22005             if (b.indexOf(tag) > -1) {
22006                 return;
22007             }
22008             this.white.push(tag);
22009             
22010         }, this);
22011         
22012         Roo.each(w, function(tag) {
22013             if (b.indexOf(tag) > -1) {
22014                 return;
22015             }
22016             if (this.white.indexOf(tag) > -1) {
22017                 return;
22018             }
22019             this.white.push(tag);
22020             
22021         }, this);
22022         
22023         
22024         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22025             if (w.indexOf(tag) > -1) {
22026                 return;
22027             }
22028             this.black.push(tag);
22029             
22030         }, this);
22031         
22032         Roo.each(b, function(tag) {
22033             if (w.indexOf(tag) > -1) {
22034                 return;
22035             }
22036             if (this.black.indexOf(tag) > -1) {
22037                 return;
22038             }
22039             this.black.push(tag);
22040             
22041         }, this);
22042         
22043         
22044         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22045         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22046         
22047         this.cwhite = [];
22048         this.cblack = [];
22049         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22050             if (b.indexOf(tag) > -1) {
22051                 return;
22052             }
22053             this.cwhite.push(tag);
22054             
22055         }, this);
22056         
22057         Roo.each(w, function(tag) {
22058             if (b.indexOf(tag) > -1) {
22059                 return;
22060             }
22061             if (this.cwhite.indexOf(tag) > -1) {
22062                 return;
22063             }
22064             this.cwhite.push(tag);
22065             
22066         }, this);
22067         
22068         
22069         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22070             if (w.indexOf(tag) > -1) {
22071                 return;
22072             }
22073             this.cblack.push(tag);
22074             
22075         }, this);
22076         
22077         Roo.each(b, function(tag) {
22078             if (w.indexOf(tag) > -1) {
22079                 return;
22080             }
22081             if (this.cblack.indexOf(tag) > -1) {
22082                 return;
22083             }
22084             this.cblack.push(tag);
22085             
22086         }, this);
22087     },
22088     
22089     setStylesheets : function(stylesheets)
22090     {
22091         if(typeof(stylesheets) == 'string'){
22092             Roo.get(this.iframe.contentDocument.head).createChild({
22093                 tag : 'link',
22094                 rel : 'stylesheet',
22095                 type : 'text/css',
22096                 href : stylesheets
22097             });
22098             
22099             return;
22100         }
22101         var _this = this;
22102      
22103         Roo.each(stylesheets, function(s) {
22104             if(!s.length){
22105                 return;
22106             }
22107             
22108             Roo.get(_this.iframe.contentDocument.head).createChild({
22109                 tag : 'link',
22110                 rel : 'stylesheet',
22111                 type : 'text/css',
22112                 href : s
22113             });
22114         });
22115
22116         
22117     },
22118     
22119     removeStylesheets : function()
22120     {
22121         var _this = this;
22122         
22123         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22124             s.remove();
22125         });
22126     }
22127     
22128     // hide stuff that is not compatible
22129     /**
22130      * @event blur
22131      * @hide
22132      */
22133     /**
22134      * @event change
22135      * @hide
22136      */
22137     /**
22138      * @event focus
22139      * @hide
22140      */
22141     /**
22142      * @event specialkey
22143      * @hide
22144      */
22145     /**
22146      * @cfg {String} fieldClass @hide
22147      */
22148     /**
22149      * @cfg {String} focusClass @hide
22150      */
22151     /**
22152      * @cfg {String} autoCreate @hide
22153      */
22154     /**
22155      * @cfg {String} inputType @hide
22156      */
22157     /**
22158      * @cfg {String} invalidClass @hide
22159      */
22160     /**
22161      * @cfg {String} invalidText @hide
22162      */
22163     /**
22164      * @cfg {String} msgFx @hide
22165      */
22166     /**
22167      * @cfg {String} validateOnBlur @hide
22168      */
22169 });
22170
22171 Roo.HtmlEditorCore.white = [
22172         'area', 'br', 'img', 'input', 'hr', 'wbr',
22173         
22174        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22175        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22176        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22177        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22178        'table',   'ul',         'xmp', 
22179        
22180        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22181       'thead',   'tr', 
22182      
22183       'dir', 'menu', 'ol', 'ul', 'dl',
22184        
22185       'embed',  'object'
22186 ];
22187
22188
22189 Roo.HtmlEditorCore.black = [
22190     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22191         'applet', // 
22192         'base',   'basefont', 'bgsound', 'blink',  'body', 
22193         'frame',  'frameset', 'head',    'html',   'ilayer', 
22194         'iframe', 'layer',  'link',     'meta',    'object',   
22195         'script', 'style' ,'title',  'xml' // clean later..
22196 ];
22197 Roo.HtmlEditorCore.clean = [
22198     'script', 'style', 'title', 'xml'
22199 ];
22200 Roo.HtmlEditorCore.remove = [
22201     'font'
22202 ];
22203 // attributes..
22204
22205 Roo.HtmlEditorCore.ablack = [
22206     'on'
22207 ];
22208     
22209 Roo.HtmlEditorCore.aclean = [ 
22210     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22211 ];
22212
22213 // protocols..
22214 Roo.HtmlEditorCore.pwhite= [
22215         'http',  'https',  'mailto'
22216 ];
22217
22218 // white listed style attributes.
22219 Roo.HtmlEditorCore.cwhite= [
22220       //  'text-align', /// default is to allow most things..
22221       
22222          
22223 //        'font-size'//??
22224 ];
22225
22226 // black listed style attributes.
22227 Roo.HtmlEditorCore.cblack= [
22228       //  'font-size' -- this can be set by the project 
22229 ];
22230
22231
22232 Roo.HtmlEditorCore.swapCodes   =[ 
22233     [    8211, "--" ], 
22234     [    8212, "--" ], 
22235     [    8216,  "'" ],  
22236     [    8217, "'" ],  
22237     [    8220, '"' ],  
22238     [    8221, '"' ],  
22239     [    8226, "*" ],  
22240     [    8230, "..." ]
22241 ]; 
22242
22243     /*
22244  * - LGPL
22245  *
22246  * HtmlEditor
22247  * 
22248  */
22249
22250 /**
22251  * @class Roo.bootstrap.HtmlEditor
22252  * @extends Roo.bootstrap.TextArea
22253  * Bootstrap HtmlEditor class
22254
22255  * @constructor
22256  * Create a new HtmlEditor
22257  * @param {Object} config The config object
22258  */
22259
22260 Roo.bootstrap.HtmlEditor = function(config){
22261     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22262     if (!this.toolbars) {
22263         this.toolbars = [];
22264     }
22265     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22266     this.addEvents({
22267             /**
22268              * @event initialize
22269              * Fires when the editor is fully initialized (including the iframe)
22270              * @param {HtmlEditor} this
22271              */
22272             initialize: true,
22273             /**
22274              * @event activate
22275              * Fires when the editor is first receives the focus. Any insertion must wait
22276              * until after this event.
22277              * @param {HtmlEditor} this
22278              */
22279             activate: true,
22280              /**
22281              * @event beforesync
22282              * Fires before the textarea is updated with content from the editor iframe. Return false
22283              * to cancel the sync.
22284              * @param {HtmlEditor} this
22285              * @param {String} html
22286              */
22287             beforesync: true,
22288              /**
22289              * @event beforepush
22290              * Fires before the iframe editor is updated with content from the textarea. Return false
22291              * to cancel the push.
22292              * @param {HtmlEditor} this
22293              * @param {String} html
22294              */
22295             beforepush: true,
22296              /**
22297              * @event sync
22298              * Fires when the textarea is updated with content from the editor iframe.
22299              * @param {HtmlEditor} this
22300              * @param {String} html
22301              */
22302             sync: true,
22303              /**
22304              * @event push
22305              * Fires when the iframe editor is updated with content from the textarea.
22306              * @param {HtmlEditor} this
22307              * @param {String} html
22308              */
22309             push: true,
22310              /**
22311              * @event editmodechange
22312              * Fires when the editor switches edit modes
22313              * @param {HtmlEditor} this
22314              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22315              */
22316             editmodechange: true,
22317             /**
22318              * @event editorevent
22319              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22320              * @param {HtmlEditor} this
22321              */
22322             editorevent: true,
22323             /**
22324              * @event firstfocus
22325              * Fires when on first focus - needed by toolbars..
22326              * @param {HtmlEditor} this
22327              */
22328             firstfocus: true,
22329             /**
22330              * @event autosave
22331              * Auto save the htmlEditor value as a file into Events
22332              * @param {HtmlEditor} this
22333              */
22334             autosave: true,
22335             /**
22336              * @event savedpreview
22337              * preview the saved version of htmlEditor
22338              * @param {HtmlEditor} this
22339              */
22340             savedpreview: true
22341         });
22342 };
22343
22344
22345 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22346     
22347     
22348       /**
22349      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22350      */
22351     toolbars : false,
22352    
22353      /**
22354      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22355      *                        Roo.resizable.
22356      */
22357     resizable : false,
22358      /**
22359      * @cfg {Number} height (in pixels)
22360      */   
22361     height: 300,
22362    /**
22363      * @cfg {Number} width (in pixels)
22364      */   
22365     width: false,
22366     
22367     /**
22368      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22369      * 
22370      */
22371     stylesheets: false,
22372     
22373     // id of frame..
22374     frameId: false,
22375     
22376     // private properties
22377     validationEvent : false,
22378     deferHeight: true,
22379     initialized : false,
22380     activated : false,
22381     
22382     onFocus : Roo.emptyFn,
22383     iframePad:3,
22384     hideMode:'offsets',
22385     
22386     
22387     tbContainer : false,
22388     
22389     toolbarContainer :function() {
22390         return this.wrap.select('.x-html-editor-tb',true).first();
22391     },
22392
22393     /**
22394      * Protected method that will not generally be called directly. It
22395      * is called when the editor creates its toolbar. Override this method if you need to
22396      * add custom toolbar buttons.
22397      * @param {HtmlEditor} editor
22398      */
22399     createToolbar : function(){
22400         
22401         Roo.log("create toolbars");
22402         
22403         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22404         this.toolbars[0].render(this.toolbarContainer());
22405         
22406         return;
22407         
22408 //        if (!editor.toolbars || !editor.toolbars.length) {
22409 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22410 //        }
22411 //        
22412 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22413 //            editor.toolbars[i] = Roo.factory(
22414 //                    typeof(editor.toolbars[i]) == 'string' ?
22415 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22416 //                Roo.bootstrap.HtmlEditor);
22417 //            editor.toolbars[i].init(editor);
22418 //        }
22419     },
22420
22421      
22422     // private
22423     onRender : function(ct, position)
22424     {
22425        // Roo.log("Call onRender: " + this.xtype);
22426         var _t = this;
22427         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22428       
22429         this.wrap = this.inputEl().wrap({
22430             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22431         });
22432         
22433         this.editorcore.onRender(ct, position);
22434          
22435         if (this.resizable) {
22436             this.resizeEl = new Roo.Resizable(this.wrap, {
22437                 pinned : true,
22438                 wrap: true,
22439                 dynamic : true,
22440                 minHeight : this.height,
22441                 height: this.height,
22442                 handles : this.resizable,
22443                 width: this.width,
22444                 listeners : {
22445                     resize : function(r, w, h) {
22446                         _t.onResize(w,h); // -something
22447                     }
22448                 }
22449             });
22450             
22451         }
22452         this.createToolbar(this);
22453        
22454         
22455         if(!this.width && this.resizable){
22456             this.setSize(this.wrap.getSize());
22457         }
22458         if (this.resizeEl) {
22459             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22460             // should trigger onReize..
22461         }
22462         
22463     },
22464
22465     // private
22466     onResize : function(w, h)
22467     {
22468         Roo.log('resize: ' +w + ',' + h );
22469         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22470         var ew = false;
22471         var eh = false;
22472         
22473         if(this.inputEl() ){
22474             if(typeof w == 'number'){
22475                 var aw = w - this.wrap.getFrameWidth('lr');
22476                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22477                 ew = aw;
22478             }
22479             if(typeof h == 'number'){
22480                  var tbh = -11;  // fixme it needs to tool bar size!
22481                 for (var i =0; i < this.toolbars.length;i++) {
22482                     // fixme - ask toolbars for heights?
22483                     tbh += this.toolbars[i].el.getHeight();
22484                     //if (this.toolbars[i].footer) {
22485                     //    tbh += this.toolbars[i].footer.el.getHeight();
22486                     //}
22487                 }
22488               
22489                 
22490                 
22491                 
22492                 
22493                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22494                 ah -= 5; // knock a few pixes off for look..
22495                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22496                 var eh = ah;
22497             }
22498         }
22499         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22500         this.editorcore.onResize(ew,eh);
22501         
22502     },
22503
22504     /**
22505      * Toggles the editor between standard and source edit mode.
22506      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22507      */
22508     toggleSourceEdit : function(sourceEditMode)
22509     {
22510         this.editorcore.toggleSourceEdit(sourceEditMode);
22511         
22512         if(this.editorcore.sourceEditMode){
22513             Roo.log('editor - showing textarea');
22514             
22515 //            Roo.log('in');
22516 //            Roo.log(this.syncValue());
22517             this.syncValue();
22518             this.inputEl().removeClass(['hide', 'x-hidden']);
22519             this.inputEl().dom.removeAttribute('tabIndex');
22520             this.inputEl().focus();
22521         }else{
22522             Roo.log('editor - hiding textarea');
22523 //            Roo.log('out')
22524 //            Roo.log(this.pushValue()); 
22525             this.pushValue();
22526             
22527             this.inputEl().addClass(['hide', 'x-hidden']);
22528             this.inputEl().dom.setAttribute('tabIndex', -1);
22529             //this.deferFocus();
22530         }
22531          
22532         if(this.resizable){
22533             this.setSize(this.wrap.getSize());
22534         }
22535         
22536         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22537     },
22538  
22539     // private (for BoxComponent)
22540     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22541
22542     // private (for BoxComponent)
22543     getResizeEl : function(){
22544         return this.wrap;
22545     },
22546
22547     // private (for BoxComponent)
22548     getPositionEl : function(){
22549         return this.wrap;
22550     },
22551
22552     // private
22553     initEvents : function(){
22554         this.originalValue = this.getValue();
22555     },
22556
22557 //    /**
22558 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22559 //     * @method
22560 //     */
22561 //    markInvalid : Roo.emptyFn,
22562 //    /**
22563 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22564 //     * @method
22565 //     */
22566 //    clearInvalid : Roo.emptyFn,
22567
22568     setValue : function(v){
22569         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22570         this.editorcore.pushValue();
22571     },
22572
22573      
22574     // private
22575     deferFocus : function(){
22576         this.focus.defer(10, this);
22577     },
22578
22579     // doc'ed in Field
22580     focus : function(){
22581         this.editorcore.focus();
22582         
22583     },
22584       
22585
22586     // private
22587     onDestroy : function(){
22588         
22589         
22590         
22591         if(this.rendered){
22592             
22593             for (var i =0; i < this.toolbars.length;i++) {
22594                 // fixme - ask toolbars for heights?
22595                 this.toolbars[i].onDestroy();
22596             }
22597             
22598             this.wrap.dom.innerHTML = '';
22599             this.wrap.remove();
22600         }
22601     },
22602
22603     // private
22604     onFirstFocus : function(){
22605         //Roo.log("onFirstFocus");
22606         this.editorcore.onFirstFocus();
22607          for (var i =0; i < this.toolbars.length;i++) {
22608             this.toolbars[i].onFirstFocus();
22609         }
22610         
22611     },
22612     
22613     // private
22614     syncValue : function()
22615     {   
22616         this.editorcore.syncValue();
22617     },
22618     
22619     pushValue : function()
22620     {   
22621         this.editorcore.pushValue();
22622     }
22623      
22624     
22625     // hide stuff that is not compatible
22626     /**
22627      * @event blur
22628      * @hide
22629      */
22630     /**
22631      * @event change
22632      * @hide
22633      */
22634     /**
22635      * @event focus
22636      * @hide
22637      */
22638     /**
22639      * @event specialkey
22640      * @hide
22641      */
22642     /**
22643      * @cfg {String} fieldClass @hide
22644      */
22645     /**
22646      * @cfg {String} focusClass @hide
22647      */
22648     /**
22649      * @cfg {String} autoCreate @hide
22650      */
22651     /**
22652      * @cfg {String} inputType @hide
22653      */
22654     /**
22655      * @cfg {String} invalidClass @hide
22656      */
22657     /**
22658      * @cfg {String} invalidText @hide
22659      */
22660     /**
22661      * @cfg {String} msgFx @hide
22662      */
22663     /**
22664      * @cfg {String} validateOnBlur @hide
22665      */
22666 });
22667  
22668     
22669    
22670    
22671    
22672       
22673 Roo.namespace('Roo.bootstrap.htmleditor');
22674 /**
22675  * @class Roo.bootstrap.HtmlEditorToolbar1
22676  * Basic Toolbar
22677  * 
22678  * Usage:
22679  *
22680  new Roo.bootstrap.HtmlEditor({
22681     ....
22682     toolbars : [
22683         new Roo.bootstrap.HtmlEditorToolbar1({
22684             disable : { fonts: 1 , format: 1, ..., ... , ...],
22685             btns : [ .... ]
22686         })
22687     }
22688      
22689  * 
22690  * @cfg {Object} disable List of elements to disable..
22691  * @cfg {Array} btns List of additional buttons.
22692  * 
22693  * 
22694  * NEEDS Extra CSS? 
22695  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22696  */
22697  
22698 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22699 {
22700     
22701     Roo.apply(this, config);
22702     
22703     // default disabled, based on 'good practice'..
22704     this.disable = this.disable || {};
22705     Roo.applyIf(this.disable, {
22706         fontSize : true,
22707         colors : true,
22708         specialElements : true
22709     });
22710     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22711     
22712     this.editor = config.editor;
22713     this.editorcore = config.editor.editorcore;
22714     
22715     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22716     
22717     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22718     // dont call parent... till later.
22719 }
22720 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22721      
22722     bar : true,
22723     
22724     editor : false,
22725     editorcore : false,
22726     
22727     
22728     formats : [
22729         "p" ,  
22730         "h1","h2","h3","h4","h5","h6", 
22731         "pre", "code", 
22732         "abbr", "acronym", "address", "cite", "samp", "var",
22733         'div','span'
22734     ],
22735     
22736     onRender : function(ct, position)
22737     {
22738        // Roo.log("Call onRender: " + this.xtype);
22739         
22740        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22741        Roo.log(this.el);
22742        this.el.dom.style.marginBottom = '0';
22743        var _this = this;
22744        var editorcore = this.editorcore;
22745        var editor= this.editor;
22746        
22747        var children = [];
22748        var btn = function(id,cmd , toggle, handler){
22749        
22750             var  event = toggle ? 'toggle' : 'click';
22751        
22752             var a = {
22753                 size : 'sm',
22754                 xtype: 'Button',
22755                 xns: Roo.bootstrap,
22756                 glyphicon : id,
22757                 cmd : id || cmd,
22758                 enableToggle:toggle !== false,
22759                 //html : 'submit'
22760                 pressed : toggle ? false : null,
22761                 listeners : {}
22762             };
22763             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22764                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22765             };
22766             children.push(a);
22767             return a;
22768        }
22769         
22770         var style = {
22771                 xtype: 'Button',
22772                 size : 'sm',
22773                 xns: Roo.bootstrap,
22774                 glyphicon : 'font',
22775                 //html : 'submit'
22776                 menu : {
22777                     xtype: 'Menu',
22778                     xns: Roo.bootstrap,
22779                     items:  []
22780                 }
22781         };
22782         Roo.each(this.formats, function(f) {
22783             style.menu.items.push({
22784                 xtype :'MenuItem',
22785                 xns: Roo.bootstrap,
22786                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22787                 tagname : f,
22788                 listeners : {
22789                     click : function()
22790                     {
22791                         editorcore.insertTag(this.tagname);
22792                         editor.focus();
22793                     }
22794                 }
22795                 
22796             });
22797         });
22798          children.push(style);   
22799             
22800             
22801         btn('bold',false,true);
22802         btn('italic',false,true);
22803         btn('align-left', 'justifyleft',true);
22804         btn('align-center', 'justifycenter',true);
22805         btn('align-right' , 'justifyright',true);
22806         btn('link', false, false, function(btn) {
22807             //Roo.log("create link?");
22808             var url = prompt(this.createLinkText, this.defaultLinkValue);
22809             if(url && url != 'http:/'+'/'){
22810                 this.editorcore.relayCmd('createlink', url);
22811             }
22812         }),
22813         btn('list','insertunorderedlist',true);
22814         btn('pencil', false,true, function(btn){
22815                 Roo.log(this);
22816                 
22817                 this.toggleSourceEdit(btn.pressed);
22818         });
22819         /*
22820         var cog = {
22821                 xtype: 'Button',
22822                 size : 'sm',
22823                 xns: Roo.bootstrap,
22824                 glyphicon : 'cog',
22825                 //html : 'submit'
22826                 menu : {
22827                     xtype: 'Menu',
22828                     xns: Roo.bootstrap,
22829                     items:  []
22830                 }
22831         };
22832         
22833         cog.menu.items.push({
22834             xtype :'MenuItem',
22835             xns: Roo.bootstrap,
22836             html : Clean styles,
22837             tagname : f,
22838             listeners : {
22839                 click : function()
22840                 {
22841                     editorcore.insertTag(this.tagname);
22842                     editor.focus();
22843                 }
22844             }
22845             
22846         });
22847        */
22848         
22849          
22850        this.xtype = 'NavSimplebar';
22851         
22852         for(var i=0;i< children.length;i++) {
22853             
22854             this.buttons.add(this.addxtypeChild(children[i]));
22855             
22856         }
22857         
22858         editor.on('editorevent', this.updateToolbar, this);
22859     },
22860     onBtnClick : function(id)
22861     {
22862        this.editorcore.relayCmd(id);
22863        this.editorcore.focus();
22864     },
22865     
22866     /**
22867      * Protected method that will not generally be called directly. It triggers
22868      * a toolbar update by reading the markup state of the current selection in the editor.
22869      */
22870     updateToolbar: function(){
22871
22872         if(!this.editorcore.activated){
22873             this.editor.onFirstFocus(); // is this neeed?
22874             return;
22875         }
22876
22877         var btns = this.buttons; 
22878         var doc = this.editorcore.doc;
22879         btns.get('bold').setActive(doc.queryCommandState('bold'));
22880         btns.get('italic').setActive(doc.queryCommandState('italic'));
22881         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22882         
22883         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22884         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22885         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22886         
22887         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22888         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22889          /*
22890         
22891         var ans = this.editorcore.getAllAncestors();
22892         if (this.formatCombo) {
22893             
22894             
22895             var store = this.formatCombo.store;
22896             this.formatCombo.setValue("");
22897             for (var i =0; i < ans.length;i++) {
22898                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22899                     // select it..
22900                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22901                     break;
22902                 }
22903             }
22904         }
22905         
22906         
22907         
22908         // hides menus... - so this cant be on a menu...
22909         Roo.bootstrap.MenuMgr.hideAll();
22910         */
22911         Roo.bootstrap.MenuMgr.hideAll();
22912         //this.editorsyncValue();
22913     },
22914     onFirstFocus: function() {
22915         this.buttons.each(function(item){
22916            item.enable();
22917         });
22918     },
22919     toggleSourceEdit : function(sourceEditMode){
22920         
22921           
22922         if(sourceEditMode){
22923             Roo.log("disabling buttons");
22924            this.buttons.each( function(item){
22925                 if(item.cmd != 'pencil'){
22926                     item.disable();
22927                 }
22928             });
22929           
22930         }else{
22931             Roo.log("enabling buttons");
22932             if(this.editorcore.initialized){
22933                 this.buttons.each( function(item){
22934                     item.enable();
22935                 });
22936             }
22937             
22938         }
22939         Roo.log("calling toggole on editor");
22940         // tell the editor that it's been pressed..
22941         this.editor.toggleSourceEdit(sourceEditMode);
22942        
22943     }
22944 });
22945
22946
22947
22948
22949
22950 /**
22951  * @class Roo.bootstrap.Table.AbstractSelectionModel
22952  * @extends Roo.util.Observable
22953  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22954  * implemented by descendant classes.  This class should not be directly instantiated.
22955  * @constructor
22956  */
22957 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22958     this.locked = false;
22959     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22960 };
22961
22962
22963 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22964     /** @ignore Called by the grid automatically. Do not call directly. */
22965     init : function(grid){
22966         this.grid = grid;
22967         this.initEvents();
22968     },
22969
22970     /**
22971      * Locks the selections.
22972      */
22973     lock : function(){
22974         this.locked = true;
22975     },
22976
22977     /**
22978      * Unlocks the selections.
22979      */
22980     unlock : function(){
22981         this.locked = false;
22982     },
22983
22984     /**
22985      * Returns true if the selections are locked.
22986      * @return {Boolean}
22987      */
22988     isLocked : function(){
22989         return this.locked;
22990     }
22991 });
22992 /**
22993  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22994  * @class Roo.bootstrap.Table.RowSelectionModel
22995  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22996  * It supports multiple selections and keyboard selection/navigation. 
22997  * @constructor
22998  * @param {Object} config
22999  */
23000
23001 Roo.bootstrap.Table.RowSelectionModel = function(config){
23002     Roo.apply(this, config);
23003     this.selections = new Roo.util.MixedCollection(false, function(o){
23004         return o.id;
23005     });
23006
23007     this.last = false;
23008     this.lastActive = false;
23009
23010     this.addEvents({
23011         /**
23012              * @event selectionchange
23013              * Fires when the selection changes
23014              * @param {SelectionModel} this
23015              */
23016             "selectionchange" : true,
23017         /**
23018              * @event afterselectionchange
23019              * Fires after the selection changes (eg. by key press or clicking)
23020              * @param {SelectionModel} this
23021              */
23022             "afterselectionchange" : true,
23023         /**
23024              * @event beforerowselect
23025              * Fires when a row is selected being selected, return false to cancel.
23026              * @param {SelectionModel} this
23027              * @param {Number} rowIndex The selected index
23028              * @param {Boolean} keepExisting False if other selections will be cleared
23029              */
23030             "beforerowselect" : true,
23031         /**
23032              * @event rowselect
23033              * Fires when a row is selected.
23034              * @param {SelectionModel} this
23035              * @param {Number} rowIndex The selected index
23036              * @param {Roo.data.Record} r The record
23037              */
23038             "rowselect" : true,
23039         /**
23040              * @event rowdeselect
23041              * Fires when a row is deselected.
23042              * @param {SelectionModel} this
23043              * @param {Number} rowIndex The selected index
23044              */
23045         "rowdeselect" : true
23046     });
23047     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23048     this.locked = false;
23049  };
23050
23051 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23052     /**
23053      * @cfg {Boolean} singleSelect
23054      * True to allow selection of only one row at a time (defaults to false)
23055      */
23056     singleSelect : false,
23057
23058     // private
23059     initEvents : function()
23060     {
23061
23062         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23063         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23064         //}else{ // allow click to work like normal
23065          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23066         //}
23067         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23068         this.grid.on("rowclick", this.handleMouseDown, this);
23069         
23070         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23071             "up" : function(e){
23072                 if(!e.shiftKey){
23073                     this.selectPrevious(e.shiftKey);
23074                 }else if(this.last !== false && this.lastActive !== false){
23075                     var last = this.last;
23076                     this.selectRange(this.last,  this.lastActive-1);
23077                     this.grid.getView().focusRow(this.lastActive);
23078                     if(last !== false){
23079                         this.last = last;
23080                     }
23081                 }else{
23082                     this.selectFirstRow();
23083                 }
23084                 this.fireEvent("afterselectionchange", this);
23085             },
23086             "down" : function(e){
23087                 if(!e.shiftKey){
23088                     this.selectNext(e.shiftKey);
23089                 }else if(this.last !== false && this.lastActive !== false){
23090                     var last = this.last;
23091                     this.selectRange(this.last,  this.lastActive+1);
23092                     this.grid.getView().focusRow(this.lastActive);
23093                     if(last !== false){
23094                         this.last = last;
23095                     }
23096                 }else{
23097                     this.selectFirstRow();
23098                 }
23099                 this.fireEvent("afterselectionchange", this);
23100             },
23101             scope: this
23102         });
23103         this.grid.store.on('load', function(){
23104             this.selections.clear();
23105         },this);
23106         /*
23107         var view = this.grid.view;
23108         view.on("refresh", this.onRefresh, this);
23109         view.on("rowupdated", this.onRowUpdated, this);
23110         view.on("rowremoved", this.onRemove, this);
23111         */
23112     },
23113
23114     // private
23115     onRefresh : function()
23116     {
23117         var ds = this.grid.store, i, v = this.grid.view;
23118         var s = this.selections;
23119         s.each(function(r){
23120             if((i = ds.indexOfId(r.id)) != -1){
23121                 v.onRowSelect(i);
23122             }else{
23123                 s.remove(r);
23124             }
23125         });
23126     },
23127
23128     // private
23129     onRemove : function(v, index, r){
23130         this.selections.remove(r);
23131     },
23132
23133     // private
23134     onRowUpdated : function(v, index, r){
23135         if(this.isSelected(r)){
23136             v.onRowSelect(index);
23137         }
23138     },
23139
23140     /**
23141      * Select records.
23142      * @param {Array} records The records to select
23143      * @param {Boolean} keepExisting (optional) True to keep existing selections
23144      */
23145     selectRecords : function(records, keepExisting)
23146     {
23147         if(!keepExisting){
23148             this.clearSelections();
23149         }
23150             var ds = this.grid.store;
23151         for(var i = 0, len = records.length; i < len; i++){
23152             this.selectRow(ds.indexOf(records[i]), true);
23153         }
23154     },
23155
23156     /**
23157      * Gets the number of selected rows.
23158      * @return {Number}
23159      */
23160     getCount : function(){
23161         return this.selections.length;
23162     },
23163
23164     /**
23165      * Selects the first row in the grid.
23166      */
23167     selectFirstRow : function(){
23168         this.selectRow(0);
23169     },
23170
23171     /**
23172      * Select the last row.
23173      * @param {Boolean} keepExisting (optional) True to keep existing selections
23174      */
23175     selectLastRow : function(keepExisting){
23176         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23177         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23178     },
23179
23180     /**
23181      * Selects the row immediately following the last selected row.
23182      * @param {Boolean} keepExisting (optional) True to keep existing selections
23183      */
23184     selectNext : function(keepExisting)
23185     {
23186             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23187             this.selectRow(this.last+1, keepExisting);
23188             this.grid.getView().focusRow(this.last);
23189         }
23190     },
23191
23192     /**
23193      * Selects the row that precedes the last selected row.
23194      * @param {Boolean} keepExisting (optional) True to keep existing selections
23195      */
23196     selectPrevious : function(keepExisting){
23197         if(this.last){
23198             this.selectRow(this.last-1, keepExisting);
23199             this.grid.getView().focusRow(this.last);
23200         }
23201     },
23202
23203     /**
23204      * Returns the selected records
23205      * @return {Array} Array of selected records
23206      */
23207     getSelections : function(){
23208         return [].concat(this.selections.items);
23209     },
23210
23211     /**
23212      * Returns the first selected record.
23213      * @return {Record}
23214      */
23215     getSelected : function(){
23216         return this.selections.itemAt(0);
23217     },
23218
23219
23220     /**
23221      * Clears all selections.
23222      */
23223     clearSelections : function(fast)
23224     {
23225         if(this.locked) {
23226             return;
23227         }
23228         if(fast !== true){
23229                 var ds = this.grid.store;
23230             var s = this.selections;
23231             s.each(function(r){
23232                 this.deselectRow(ds.indexOfId(r.id));
23233             }, this);
23234             s.clear();
23235         }else{
23236             this.selections.clear();
23237         }
23238         this.last = false;
23239     },
23240
23241
23242     /**
23243      * Selects all rows.
23244      */
23245     selectAll : function(){
23246         if(this.locked) {
23247             return;
23248         }
23249         this.selections.clear();
23250         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23251             this.selectRow(i, true);
23252         }
23253     },
23254
23255     /**
23256      * Returns True if there is a selection.
23257      * @return {Boolean}
23258      */
23259     hasSelection : function(){
23260         return this.selections.length > 0;
23261     },
23262
23263     /**
23264      * Returns True if the specified row is selected.
23265      * @param {Number/Record} record The record or index of the record to check
23266      * @return {Boolean}
23267      */
23268     isSelected : function(index){
23269             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23270         return (r && this.selections.key(r.id) ? true : false);
23271     },
23272
23273     /**
23274      * Returns True if the specified record id is selected.
23275      * @param {String} id The id of record to check
23276      * @return {Boolean}
23277      */
23278     isIdSelected : function(id){
23279         return (this.selections.key(id) ? true : false);
23280     },
23281
23282
23283     // private
23284     handleMouseDBClick : function(e, t){
23285         
23286     },
23287     // private
23288     handleMouseDown : function(e, t)
23289     {
23290             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23291         if(this.isLocked() || rowIndex < 0 ){
23292             return;
23293         };
23294         if(e.shiftKey && this.last !== false){
23295             var last = this.last;
23296             this.selectRange(last, rowIndex, e.ctrlKey);
23297             this.last = last; // reset the last
23298             t.focus();
23299     
23300         }else{
23301             var isSelected = this.isSelected(rowIndex);
23302             //Roo.log("select row:" + rowIndex);
23303             if(isSelected){
23304                 this.deselectRow(rowIndex);
23305             } else {
23306                         this.selectRow(rowIndex, true);
23307             }
23308     
23309             /*
23310                 if(e.button !== 0 && isSelected){
23311                 alert('rowIndex 2: ' + rowIndex);
23312                     view.focusRow(rowIndex);
23313                 }else if(e.ctrlKey && isSelected){
23314                     this.deselectRow(rowIndex);
23315                 }else if(!isSelected){
23316                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23317                     view.focusRow(rowIndex);
23318                 }
23319             */
23320         }
23321         this.fireEvent("afterselectionchange", this);
23322     },
23323     // private
23324     handleDragableRowClick :  function(grid, rowIndex, e) 
23325     {
23326         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23327             this.selectRow(rowIndex, false);
23328             grid.view.focusRow(rowIndex);
23329              this.fireEvent("afterselectionchange", this);
23330         }
23331     },
23332     
23333     /**
23334      * Selects multiple rows.
23335      * @param {Array} rows Array of the indexes of the row to select
23336      * @param {Boolean} keepExisting (optional) True to keep existing selections
23337      */
23338     selectRows : function(rows, keepExisting){
23339         if(!keepExisting){
23340             this.clearSelections();
23341         }
23342         for(var i = 0, len = rows.length; i < len; i++){
23343             this.selectRow(rows[i], true);
23344         }
23345     },
23346
23347     /**
23348      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23349      * @param {Number} startRow The index of the first row in the range
23350      * @param {Number} endRow The index of the last row in the range
23351      * @param {Boolean} keepExisting (optional) True to retain existing selections
23352      */
23353     selectRange : function(startRow, endRow, keepExisting){
23354         if(this.locked) {
23355             return;
23356         }
23357         if(!keepExisting){
23358             this.clearSelections();
23359         }
23360         if(startRow <= endRow){
23361             for(var i = startRow; i <= endRow; i++){
23362                 this.selectRow(i, true);
23363             }
23364         }else{
23365             for(var i = startRow; i >= endRow; i--){
23366                 this.selectRow(i, true);
23367             }
23368         }
23369     },
23370
23371     /**
23372      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23373      * @param {Number} startRow The index of the first row in the range
23374      * @param {Number} endRow The index of the last row in the range
23375      */
23376     deselectRange : function(startRow, endRow, preventViewNotify){
23377         if(this.locked) {
23378             return;
23379         }
23380         for(var i = startRow; i <= endRow; i++){
23381             this.deselectRow(i, preventViewNotify);
23382         }
23383     },
23384
23385     /**
23386      * Selects a row.
23387      * @param {Number} row The index of the row to select
23388      * @param {Boolean} keepExisting (optional) True to keep existing selections
23389      */
23390     selectRow : function(index, keepExisting, preventViewNotify)
23391     {
23392             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23393             return;
23394         }
23395         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23396             if(!keepExisting || this.singleSelect){
23397                 this.clearSelections();
23398             }
23399             
23400             var r = this.grid.store.getAt(index);
23401             //console.log('selectRow - record id :' + r.id);
23402             
23403             this.selections.add(r);
23404             this.last = this.lastActive = index;
23405             if(!preventViewNotify){
23406                 var proxy = new Roo.Element(
23407                                 this.grid.getRowDom(index)
23408                 );
23409                 proxy.addClass('bg-info info');
23410             }
23411             this.fireEvent("rowselect", this, index, r);
23412             this.fireEvent("selectionchange", this);
23413         }
23414     },
23415
23416     /**
23417      * Deselects a row.
23418      * @param {Number} row The index of the row to deselect
23419      */
23420     deselectRow : function(index, preventViewNotify)
23421     {
23422         if(this.locked) {
23423             return;
23424         }
23425         if(this.last == index){
23426             this.last = false;
23427         }
23428         if(this.lastActive == index){
23429             this.lastActive = false;
23430         }
23431         
23432         var r = this.grid.store.getAt(index);
23433         if (!r) {
23434             return;
23435         }
23436         
23437         this.selections.remove(r);
23438         //.console.log('deselectRow - record id :' + r.id);
23439         if(!preventViewNotify){
23440         
23441             var proxy = new Roo.Element(
23442                 this.grid.getRowDom(index)
23443             );
23444             proxy.removeClass('bg-info info');
23445         }
23446         this.fireEvent("rowdeselect", this, index);
23447         this.fireEvent("selectionchange", this);
23448     },
23449
23450     // private
23451     restoreLast : function(){
23452         if(this._last){
23453             this.last = this._last;
23454         }
23455     },
23456
23457     // private
23458     acceptsNav : function(row, col, cm){
23459         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23460     },
23461
23462     // private
23463     onEditorKey : function(field, e){
23464         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23465         if(k == e.TAB){
23466             e.stopEvent();
23467             ed.completeEdit();
23468             if(e.shiftKey){
23469                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23470             }else{
23471                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23472             }
23473         }else if(k == e.ENTER && !e.ctrlKey){
23474             e.stopEvent();
23475             ed.completeEdit();
23476             if(e.shiftKey){
23477                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23478             }else{
23479                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23480             }
23481         }else if(k == e.ESC){
23482             ed.cancelEdit();
23483         }
23484         if(newCell){
23485             g.startEditing(newCell[0], newCell[1]);
23486         }
23487     }
23488 });
23489 /*
23490  * Based on:
23491  * Ext JS Library 1.1.1
23492  * Copyright(c) 2006-2007, Ext JS, LLC.
23493  *
23494  * Originally Released Under LGPL - original licence link has changed is not relivant.
23495  *
23496  * Fork - LGPL
23497  * <script type="text/javascript">
23498  */
23499  
23500 /**
23501  * @class Roo.bootstrap.PagingToolbar
23502  * @extends Roo.bootstrap.NavSimplebar
23503  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23504  * @constructor
23505  * Create a new PagingToolbar
23506  * @param {Object} config The config object
23507  * @param {Roo.data.Store} store
23508  */
23509 Roo.bootstrap.PagingToolbar = function(config)
23510 {
23511     // old args format still supported... - xtype is prefered..
23512         // created from xtype...
23513     
23514     this.ds = config.dataSource;
23515     
23516     if (config.store && !this.ds) {
23517         this.store= Roo.factory(config.store, Roo.data);
23518         this.ds = this.store;
23519         this.ds.xmodule = this.xmodule || false;
23520     }
23521     
23522     this.toolbarItems = [];
23523     if (config.items) {
23524         this.toolbarItems = config.items;
23525     }
23526     
23527     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23528     
23529     this.cursor = 0;
23530     
23531     if (this.ds) { 
23532         this.bind(this.ds);
23533     }
23534     
23535     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23536     
23537 };
23538
23539 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23540     /**
23541      * @cfg {Roo.data.Store} dataSource
23542      * The underlying data store providing the paged data
23543      */
23544     /**
23545      * @cfg {String/HTMLElement/Element} container
23546      * container The id or element that will contain the toolbar
23547      */
23548     /**
23549      * @cfg {Boolean} displayInfo
23550      * True to display the displayMsg (defaults to false)
23551      */
23552     /**
23553      * @cfg {Number} pageSize
23554      * The number of records to display per page (defaults to 20)
23555      */
23556     pageSize: 20,
23557     /**
23558      * @cfg {String} displayMsg
23559      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23560      */
23561     displayMsg : 'Displaying {0} - {1} of {2}',
23562     /**
23563      * @cfg {String} emptyMsg
23564      * The message to display when no records are found (defaults to "No data to display")
23565      */
23566     emptyMsg : 'No data to display',
23567     /**
23568      * Customizable piece of the default paging text (defaults to "Page")
23569      * @type String
23570      */
23571     beforePageText : "Page",
23572     /**
23573      * Customizable piece of the default paging text (defaults to "of %0")
23574      * @type String
23575      */
23576     afterPageText : "of {0}",
23577     /**
23578      * Customizable piece of the default paging text (defaults to "First Page")
23579      * @type String
23580      */
23581     firstText : "First Page",
23582     /**
23583      * Customizable piece of the default paging text (defaults to "Previous Page")
23584      * @type String
23585      */
23586     prevText : "Previous Page",
23587     /**
23588      * Customizable piece of the default paging text (defaults to "Next Page")
23589      * @type String
23590      */
23591     nextText : "Next Page",
23592     /**
23593      * Customizable piece of the default paging text (defaults to "Last Page")
23594      * @type String
23595      */
23596     lastText : "Last Page",
23597     /**
23598      * Customizable piece of the default paging text (defaults to "Refresh")
23599      * @type String
23600      */
23601     refreshText : "Refresh",
23602
23603     buttons : false,
23604     // private
23605     onRender : function(ct, position) 
23606     {
23607         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23608         this.navgroup.parentId = this.id;
23609         this.navgroup.onRender(this.el, null);
23610         // add the buttons to the navgroup
23611         
23612         if(this.displayInfo){
23613             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23614             this.displayEl = this.el.select('.x-paging-info', true).first();
23615 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23616 //            this.displayEl = navel.el.select('span',true).first();
23617         }
23618         
23619         var _this = this;
23620         
23621         if(this.buttons){
23622             Roo.each(_this.buttons, function(e){ // this might need to use render????
23623                Roo.factory(e).onRender(_this.el, null);
23624             });
23625         }
23626             
23627         Roo.each(_this.toolbarItems, function(e) {
23628             _this.navgroup.addItem(e);
23629         });
23630         
23631         
23632         this.first = this.navgroup.addItem({
23633             tooltip: this.firstText,
23634             cls: "prev",
23635             icon : 'fa fa-backward',
23636             disabled: true,
23637             preventDefault: true,
23638             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23639         });
23640         
23641         this.prev =  this.navgroup.addItem({
23642             tooltip: this.prevText,
23643             cls: "prev",
23644             icon : 'fa fa-step-backward',
23645             disabled: true,
23646             preventDefault: true,
23647             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23648         });
23649     //this.addSeparator();
23650         
23651         
23652         var field = this.navgroup.addItem( {
23653             tagtype : 'span',
23654             cls : 'x-paging-position',
23655             
23656             html : this.beforePageText  +
23657                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23658                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23659          } ); //?? escaped?
23660         
23661         this.field = field.el.select('input', true).first();
23662         this.field.on("keydown", this.onPagingKeydown, this);
23663         this.field.on("focus", function(){this.dom.select();});
23664     
23665     
23666         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23667         //this.field.setHeight(18);
23668         //this.addSeparator();
23669         this.next = this.navgroup.addItem({
23670             tooltip: this.nextText,
23671             cls: "next",
23672             html : ' <i class="fa fa-step-forward">',
23673             disabled: true,
23674             preventDefault: true,
23675             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23676         });
23677         this.last = this.navgroup.addItem({
23678             tooltip: this.lastText,
23679             icon : 'fa fa-forward',
23680             cls: "next",
23681             disabled: true,
23682             preventDefault: true,
23683             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23684         });
23685     //this.addSeparator();
23686         this.loading = this.navgroup.addItem({
23687             tooltip: this.refreshText,
23688             icon: 'fa fa-refresh',
23689             preventDefault: true,
23690             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23691         });
23692         
23693     },
23694
23695     // private
23696     updateInfo : function(){
23697         if(this.displayEl){
23698             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23699             var msg = count == 0 ?
23700                 this.emptyMsg :
23701                 String.format(
23702                     this.displayMsg,
23703                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23704                 );
23705             this.displayEl.update(msg);
23706         }
23707     },
23708
23709     // private
23710     onLoad : function(ds, r, o){
23711        this.cursor = o.params ? o.params.start : 0;
23712        var d = this.getPageData(),
23713             ap = d.activePage,
23714             ps = d.pages;
23715         
23716        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23717        this.field.dom.value = ap;
23718        this.first.setDisabled(ap == 1);
23719        this.prev.setDisabled(ap == 1);
23720        this.next.setDisabled(ap == ps);
23721        this.last.setDisabled(ap == ps);
23722        this.loading.enable();
23723        this.updateInfo();
23724     },
23725
23726     // private
23727     getPageData : function(){
23728         var total = this.ds.getTotalCount();
23729         return {
23730             total : total,
23731             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23732             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23733         };
23734     },
23735
23736     // private
23737     onLoadError : function(){
23738         this.loading.enable();
23739     },
23740
23741     // private
23742     onPagingKeydown : function(e){
23743         var k = e.getKey();
23744         var d = this.getPageData();
23745         if(k == e.RETURN){
23746             var v = this.field.dom.value, pageNum;
23747             if(!v || isNaN(pageNum = parseInt(v, 10))){
23748                 this.field.dom.value = d.activePage;
23749                 return;
23750             }
23751             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23752             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23753             e.stopEvent();
23754         }
23755         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))
23756         {
23757           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23758           this.field.dom.value = pageNum;
23759           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23760           e.stopEvent();
23761         }
23762         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23763         {
23764           var v = this.field.dom.value, pageNum; 
23765           var increment = (e.shiftKey) ? 10 : 1;
23766           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23767                 increment *= -1;
23768           }
23769           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23770             this.field.dom.value = d.activePage;
23771             return;
23772           }
23773           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23774           {
23775             this.field.dom.value = parseInt(v, 10) + increment;
23776             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23777             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23778           }
23779           e.stopEvent();
23780         }
23781     },
23782
23783     // private
23784     beforeLoad : function(){
23785         if(this.loading){
23786             this.loading.disable();
23787         }
23788     },
23789
23790     // private
23791     onClick : function(which){
23792         
23793         var ds = this.ds;
23794         if (!ds) {
23795             return;
23796         }
23797         
23798         switch(which){
23799             case "first":
23800                 ds.load({params:{start: 0, limit: this.pageSize}});
23801             break;
23802             case "prev":
23803                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23804             break;
23805             case "next":
23806                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23807             break;
23808             case "last":
23809                 var total = ds.getTotalCount();
23810                 var extra = total % this.pageSize;
23811                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23812                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23813             break;
23814             case "refresh":
23815                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23816             break;
23817         }
23818     },
23819
23820     /**
23821      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23822      * @param {Roo.data.Store} store The data store to unbind
23823      */
23824     unbind : function(ds){
23825         ds.un("beforeload", this.beforeLoad, this);
23826         ds.un("load", this.onLoad, this);
23827         ds.un("loadexception", this.onLoadError, this);
23828         ds.un("remove", this.updateInfo, this);
23829         ds.un("add", this.updateInfo, this);
23830         this.ds = undefined;
23831     },
23832
23833     /**
23834      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23835      * @param {Roo.data.Store} store The data store to bind
23836      */
23837     bind : function(ds){
23838         ds.on("beforeload", this.beforeLoad, this);
23839         ds.on("load", this.onLoad, this);
23840         ds.on("loadexception", this.onLoadError, this);
23841         ds.on("remove", this.updateInfo, this);
23842         ds.on("add", this.updateInfo, this);
23843         this.ds = ds;
23844     }
23845 });/*
23846  * - LGPL
23847  *
23848  * element
23849  * 
23850  */
23851
23852 /**
23853  * @class Roo.bootstrap.MessageBar
23854  * @extends Roo.bootstrap.Component
23855  * Bootstrap MessageBar class
23856  * @cfg {String} html contents of the MessageBar
23857  * @cfg {String} weight (info | success | warning | danger) default info
23858  * @cfg {String} beforeClass insert the bar before the given class
23859  * @cfg {Boolean} closable (true | false) default false
23860  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23861  * 
23862  * @constructor
23863  * Create a new Element
23864  * @param {Object} config The config object
23865  */
23866
23867 Roo.bootstrap.MessageBar = function(config){
23868     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23869 };
23870
23871 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23872     
23873     html: '',
23874     weight: 'info',
23875     closable: false,
23876     fixed: false,
23877     beforeClass: 'bootstrap-sticky-wrap',
23878     
23879     getAutoCreate : function(){
23880         
23881         var cfg = {
23882             tag: 'div',
23883             cls: 'alert alert-dismissable alert-' + this.weight,
23884             cn: [
23885                 {
23886                     tag: 'span',
23887                     cls: 'message',
23888                     html: this.html || ''
23889                 }
23890             ]
23891         };
23892         
23893         if(this.fixed){
23894             cfg.cls += ' alert-messages-fixed';
23895         }
23896         
23897         if(this.closable){
23898             cfg.cn.push({
23899                 tag: 'button',
23900                 cls: 'close',
23901                 html: 'x'
23902             });
23903         }
23904         
23905         return cfg;
23906     },
23907     
23908     onRender : function(ct, position)
23909     {
23910         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23911         
23912         if(!this.el){
23913             var cfg = Roo.apply({},  this.getAutoCreate());
23914             cfg.id = Roo.id();
23915             
23916             if (this.cls) {
23917                 cfg.cls += ' ' + this.cls;
23918             }
23919             if (this.style) {
23920                 cfg.style = this.style;
23921             }
23922             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23923             
23924             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23925         }
23926         
23927         this.el.select('>button.close').on('click', this.hide, this);
23928         
23929     },
23930     
23931     show : function()
23932     {
23933         if (!this.rendered) {
23934             this.render();
23935         }
23936         
23937         this.el.show();
23938         
23939         this.fireEvent('show', this);
23940         
23941     },
23942     
23943     hide : function()
23944     {
23945         if (!this.rendered) {
23946             this.render();
23947         }
23948         
23949         this.el.hide();
23950         
23951         this.fireEvent('hide', this);
23952     },
23953     
23954     update : function()
23955     {
23956 //        var e = this.el.dom.firstChild;
23957 //        
23958 //        if(this.closable){
23959 //            e = e.nextSibling;
23960 //        }
23961 //        
23962 //        e.data = this.html || '';
23963
23964         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23965     }
23966    
23967 });
23968
23969  
23970
23971      /*
23972  * - LGPL
23973  *
23974  * Graph
23975  * 
23976  */
23977
23978
23979 /**
23980  * @class Roo.bootstrap.Graph
23981  * @extends Roo.bootstrap.Component
23982  * Bootstrap Graph class
23983 > Prameters
23984  -sm {number} sm 4
23985  -md {number} md 5
23986  @cfg {String} graphtype  bar | vbar | pie
23987  @cfg {number} g_x coodinator | centre x (pie)
23988  @cfg {number} g_y coodinator | centre y (pie)
23989  @cfg {number} g_r radius (pie)
23990  @cfg {number} g_height height of the chart (respected by all elements in the set)
23991  @cfg {number} g_width width of the chart (respected by all elements in the set)
23992  @cfg {Object} title The title of the chart
23993     
23994  -{Array}  values
23995  -opts (object) options for the chart 
23996      o {
23997      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23998      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23999      o vgutter (number)
24000      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.
24001      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24002      o to
24003      o stretch (boolean)
24004      o }
24005  -opts (object) options for the pie
24006      o{
24007      o cut
24008      o startAngle (number)
24009      o endAngle (number)
24010      } 
24011  *
24012  * @constructor
24013  * Create a new Input
24014  * @param {Object} config The config object
24015  */
24016
24017 Roo.bootstrap.Graph = function(config){
24018     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24019     
24020     this.addEvents({
24021         // img events
24022         /**
24023          * @event click
24024          * The img click event for the img.
24025          * @param {Roo.EventObject} e
24026          */
24027         "click" : true
24028     });
24029 };
24030
24031 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24032     
24033     sm: 4,
24034     md: 5,
24035     graphtype: 'bar',
24036     g_height: 250,
24037     g_width: 400,
24038     g_x: 50,
24039     g_y: 50,
24040     g_r: 30,
24041     opts:{
24042         //g_colors: this.colors,
24043         g_type: 'soft',
24044         g_gutter: '20%'
24045
24046     },
24047     title : false,
24048
24049     getAutoCreate : function(){
24050         
24051         var cfg = {
24052             tag: 'div',
24053             html : null
24054         };
24055         
24056         
24057         return  cfg;
24058     },
24059
24060     onRender : function(ct,position){
24061         
24062         
24063         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24064         
24065         if (typeof(Raphael) == 'undefined') {
24066             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24067             return;
24068         }
24069         
24070         this.raphael = Raphael(this.el.dom);
24071         
24072                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24073                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24074                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24075                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24076                 /*
24077                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24078                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24079                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24080                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24081                 
24082                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24083                 r.barchart(330, 10, 300, 220, data1);
24084                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24085                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24086                 */
24087                 
24088                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24089                 // r.barchart(30, 30, 560, 250,  xdata, {
24090                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24091                 //     axis : "0 0 1 1",
24092                 //     axisxlabels :  xdata
24093                 //     //yvalues : cols,
24094                    
24095                 // });
24096 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24097 //        
24098 //        this.load(null,xdata,{
24099 //                axis : "0 0 1 1",
24100 //                axisxlabels :  xdata
24101 //                });
24102
24103     },
24104
24105     load : function(graphtype,xdata,opts)
24106     {
24107         this.raphael.clear();
24108         if(!graphtype) {
24109             graphtype = this.graphtype;
24110         }
24111         if(!opts){
24112             opts = this.opts;
24113         }
24114         var r = this.raphael,
24115             fin = function () {
24116                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24117             },
24118             fout = function () {
24119                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24120             },
24121             pfin = function() {
24122                 this.sector.stop();
24123                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24124
24125                 if (this.label) {
24126                     this.label[0].stop();
24127                     this.label[0].attr({ r: 7.5 });
24128                     this.label[1].attr({ "font-weight": 800 });
24129                 }
24130             },
24131             pfout = function() {
24132                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24133
24134                 if (this.label) {
24135                     this.label[0].animate({ r: 5 }, 500, "bounce");
24136                     this.label[1].attr({ "font-weight": 400 });
24137                 }
24138             };
24139
24140         switch(graphtype){
24141             case 'bar':
24142                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24143                 break;
24144             case 'hbar':
24145                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24146                 break;
24147             case 'pie':
24148 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24149 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24150 //            
24151                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24152                 
24153                 break;
24154
24155         }
24156         
24157         if(this.title){
24158             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24159         }
24160         
24161     },
24162     
24163     setTitle: function(o)
24164     {
24165         this.title = o;
24166     },
24167     
24168     initEvents: function() {
24169         
24170         if(!this.href){
24171             this.el.on('click', this.onClick, this);
24172         }
24173     },
24174     
24175     onClick : function(e)
24176     {
24177         Roo.log('img onclick');
24178         this.fireEvent('click', this, e);
24179     }
24180    
24181 });
24182
24183  
24184 /*
24185  * - LGPL
24186  *
24187  * numberBox
24188  * 
24189  */
24190 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24191
24192 /**
24193  * @class Roo.bootstrap.dash.NumberBox
24194  * @extends Roo.bootstrap.Component
24195  * Bootstrap NumberBox class
24196  * @cfg {String} headline Box headline
24197  * @cfg {String} content Box content
24198  * @cfg {String} icon Box icon
24199  * @cfg {String} footer Footer text
24200  * @cfg {String} fhref Footer href
24201  * 
24202  * @constructor
24203  * Create a new NumberBox
24204  * @param {Object} config The config object
24205  */
24206
24207
24208 Roo.bootstrap.dash.NumberBox = function(config){
24209     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24210     
24211 };
24212
24213 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24214     
24215     headline : '',
24216     content : '',
24217     icon : '',
24218     footer : '',
24219     fhref : '',
24220     ficon : '',
24221     
24222     getAutoCreate : function(){
24223         
24224         var cfg = {
24225             tag : 'div',
24226             cls : 'small-box ',
24227             cn : [
24228                 {
24229                     tag : 'div',
24230                     cls : 'inner',
24231                     cn :[
24232                         {
24233                             tag : 'h3',
24234                             cls : 'roo-headline',
24235                             html : this.headline
24236                         },
24237                         {
24238                             tag : 'p',
24239                             cls : 'roo-content',
24240                             html : this.content
24241                         }
24242                     ]
24243                 }
24244             ]
24245         };
24246         
24247         if(this.icon){
24248             cfg.cn.push({
24249                 tag : 'div',
24250                 cls : 'icon',
24251                 cn :[
24252                     {
24253                         tag : 'i',
24254                         cls : 'ion ' + this.icon
24255                     }
24256                 ]
24257             });
24258         }
24259         
24260         if(this.footer){
24261             var footer = {
24262                 tag : 'a',
24263                 cls : 'small-box-footer',
24264                 href : this.fhref || '#',
24265                 html : this.footer
24266             };
24267             
24268             cfg.cn.push(footer);
24269             
24270         }
24271         
24272         return  cfg;
24273     },
24274
24275     onRender : function(ct,position){
24276         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24277
24278
24279        
24280                 
24281     },
24282
24283     setHeadline: function (value)
24284     {
24285         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24286     },
24287     
24288     setFooter: function (value, href)
24289     {
24290         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24291         
24292         if(href){
24293             this.el.select('a.small-box-footer',true).first().attr('href', href);
24294         }
24295         
24296     },
24297
24298     setContent: function (value)
24299     {
24300         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24301     },
24302
24303     initEvents: function() 
24304     {   
24305         
24306     }
24307     
24308 });
24309
24310  
24311 /*
24312  * - LGPL
24313  *
24314  * TabBox
24315  * 
24316  */
24317 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24318
24319 /**
24320  * @class Roo.bootstrap.dash.TabBox
24321  * @extends Roo.bootstrap.Component
24322  * Bootstrap TabBox class
24323  * @cfg {String} title Title of the TabBox
24324  * @cfg {String} icon Icon of the TabBox
24325  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24326  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24327  * 
24328  * @constructor
24329  * Create a new TabBox
24330  * @param {Object} config The config object
24331  */
24332
24333
24334 Roo.bootstrap.dash.TabBox = function(config){
24335     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24336     this.addEvents({
24337         // raw events
24338         /**
24339          * @event addpane
24340          * When a pane is added
24341          * @param {Roo.bootstrap.dash.TabPane} pane
24342          */
24343         "addpane" : true,
24344         /**
24345          * @event activatepane
24346          * When a pane is activated
24347          * @param {Roo.bootstrap.dash.TabPane} pane
24348          */
24349         "activatepane" : true
24350         
24351          
24352     });
24353     
24354     this.panes = [];
24355 };
24356
24357 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24358
24359     title : '',
24360     icon : false,
24361     showtabs : true,
24362     tabScrollable : false,
24363     
24364     getChildContainer : function()
24365     {
24366         return this.el.select('.tab-content', true).first();
24367     },
24368     
24369     getAutoCreate : function(){
24370         
24371         var header = {
24372             tag: 'li',
24373             cls: 'pull-left header',
24374             html: this.title,
24375             cn : []
24376         };
24377         
24378         if(this.icon){
24379             header.cn.push({
24380                 tag: 'i',
24381                 cls: 'fa ' + this.icon
24382             });
24383         }
24384         
24385         var h = {
24386             tag: 'ul',
24387             cls: 'nav nav-tabs pull-right',
24388             cn: [
24389                 header
24390             ]
24391         };
24392         
24393         if(this.tabScrollable){
24394             h = {
24395                 tag: 'div',
24396                 cls: 'tab-header',
24397                 cn: [
24398                     {
24399                         tag: 'ul',
24400                         cls: 'nav nav-tabs pull-right',
24401                         cn: [
24402                             header
24403                         ]
24404                     }
24405                 ]
24406             };
24407         }
24408         
24409         var cfg = {
24410             tag: 'div',
24411             cls: 'nav-tabs-custom',
24412             cn: [
24413                 h,
24414                 {
24415                     tag: 'div',
24416                     cls: 'tab-content no-padding',
24417                     cn: []
24418                 }
24419             ]
24420         };
24421
24422         return  cfg;
24423     },
24424     initEvents : function()
24425     {
24426         //Roo.log('add add pane handler');
24427         this.on('addpane', this.onAddPane, this);
24428     },
24429      /**
24430      * Updates the box title
24431      * @param {String} html to set the title to.
24432      */
24433     setTitle : function(value)
24434     {
24435         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24436     },
24437     onAddPane : function(pane)
24438     {
24439         this.panes.push(pane);
24440         //Roo.log('addpane');
24441         //Roo.log(pane);
24442         // tabs are rendere left to right..
24443         if(!this.showtabs){
24444             return;
24445         }
24446         
24447         var ctr = this.el.select('.nav-tabs', true).first();
24448          
24449          
24450         var existing = ctr.select('.nav-tab',true);
24451         var qty = existing.getCount();;
24452         
24453         
24454         var tab = ctr.createChild({
24455             tag : 'li',
24456             cls : 'nav-tab' + (qty ? '' : ' active'),
24457             cn : [
24458                 {
24459                     tag : 'a',
24460                     href:'#',
24461                     html : pane.title
24462                 }
24463             ]
24464         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24465         pane.tab = tab;
24466         
24467         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24468         if (!qty) {
24469             pane.el.addClass('active');
24470         }
24471         
24472                 
24473     },
24474     onTabClick : function(ev,un,ob,pane)
24475     {
24476         //Roo.log('tab - prev default');
24477         ev.preventDefault();
24478         
24479         
24480         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24481         pane.tab.addClass('active');
24482         //Roo.log(pane.title);
24483         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24484         // technically we should have a deactivate event.. but maybe add later.
24485         // and it should not de-activate the selected tab...
24486         this.fireEvent('activatepane', pane);
24487         pane.el.addClass('active');
24488         pane.fireEvent('activate');
24489         
24490         
24491     },
24492     
24493     getActivePane : function()
24494     {
24495         var r = false;
24496         Roo.each(this.panes, function(p) {
24497             if(p.el.hasClass('active')){
24498                 r = p;
24499                 return false;
24500             }
24501             
24502             return;
24503         });
24504         
24505         return r;
24506     }
24507     
24508     
24509 });
24510
24511  
24512 /*
24513  * - LGPL
24514  *
24515  * Tab pane
24516  * 
24517  */
24518 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24519 /**
24520  * @class Roo.bootstrap.TabPane
24521  * @extends Roo.bootstrap.Component
24522  * Bootstrap TabPane class
24523  * @cfg {Boolean} active (false | true) Default false
24524  * @cfg {String} title title of panel
24525
24526  * 
24527  * @constructor
24528  * Create a new TabPane
24529  * @param {Object} config The config object
24530  */
24531
24532 Roo.bootstrap.dash.TabPane = function(config){
24533     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24534     
24535     this.addEvents({
24536         // raw events
24537         /**
24538          * @event activate
24539          * When a pane is activated
24540          * @param {Roo.bootstrap.dash.TabPane} pane
24541          */
24542         "activate" : true
24543          
24544     });
24545 };
24546
24547 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24548     
24549     active : false,
24550     title : '',
24551     
24552     // the tabBox that this is attached to.
24553     tab : false,
24554      
24555     getAutoCreate : function() 
24556     {
24557         var cfg = {
24558             tag: 'div',
24559             cls: 'tab-pane'
24560         };
24561         
24562         if(this.active){
24563             cfg.cls += ' active';
24564         }
24565         
24566         return cfg;
24567     },
24568     initEvents  : function()
24569     {
24570         //Roo.log('trigger add pane handler');
24571         this.parent().fireEvent('addpane', this)
24572     },
24573     
24574      /**
24575      * Updates the tab title 
24576      * @param {String} html to set the title to.
24577      */
24578     setTitle: function(str)
24579     {
24580         if (!this.tab) {
24581             return;
24582         }
24583         this.title = str;
24584         this.tab.select('a', true).first().dom.innerHTML = str;
24585         
24586     }
24587     
24588     
24589     
24590 });
24591
24592  
24593
24594
24595  /*
24596  * - LGPL
24597  *
24598  * menu
24599  * 
24600  */
24601 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24602
24603 /**
24604  * @class Roo.bootstrap.menu.Menu
24605  * @extends Roo.bootstrap.Component
24606  * Bootstrap Menu class - container for Menu
24607  * @cfg {String} html Text of the menu
24608  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24609  * @cfg {String} icon Font awesome icon
24610  * @cfg {String} pos Menu align to (top | bottom) default bottom
24611  * 
24612  * 
24613  * @constructor
24614  * Create a new Menu
24615  * @param {Object} config The config object
24616  */
24617
24618
24619 Roo.bootstrap.menu.Menu = function(config){
24620     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24621     
24622     this.addEvents({
24623         /**
24624          * @event beforeshow
24625          * Fires before this menu is displayed
24626          * @param {Roo.bootstrap.menu.Menu} this
24627          */
24628         beforeshow : true,
24629         /**
24630          * @event beforehide
24631          * Fires before this menu is hidden
24632          * @param {Roo.bootstrap.menu.Menu} this
24633          */
24634         beforehide : true,
24635         /**
24636          * @event show
24637          * Fires after this menu is displayed
24638          * @param {Roo.bootstrap.menu.Menu} this
24639          */
24640         show : true,
24641         /**
24642          * @event hide
24643          * Fires after this menu is hidden
24644          * @param {Roo.bootstrap.menu.Menu} this
24645          */
24646         hide : true,
24647         /**
24648          * @event click
24649          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24650          * @param {Roo.bootstrap.menu.Menu} this
24651          * @param {Roo.EventObject} e
24652          */
24653         click : true
24654     });
24655     
24656 };
24657
24658 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24659     
24660     submenu : false,
24661     html : '',
24662     weight : 'default',
24663     icon : false,
24664     pos : 'bottom',
24665     
24666     
24667     getChildContainer : function() {
24668         if(this.isSubMenu){
24669             return this.el;
24670         }
24671         
24672         return this.el.select('ul.dropdown-menu', true).first();  
24673     },
24674     
24675     getAutoCreate : function()
24676     {
24677         var text = [
24678             {
24679                 tag : 'span',
24680                 cls : 'roo-menu-text',
24681                 html : this.html
24682             }
24683         ];
24684         
24685         if(this.icon){
24686             text.unshift({
24687                 tag : 'i',
24688                 cls : 'fa ' + this.icon
24689             })
24690         }
24691         
24692         
24693         var cfg = {
24694             tag : 'div',
24695             cls : 'btn-group',
24696             cn : [
24697                 {
24698                     tag : 'button',
24699                     cls : 'dropdown-button btn btn-' + this.weight,
24700                     cn : text
24701                 },
24702                 {
24703                     tag : 'button',
24704                     cls : 'dropdown-toggle btn btn-' + this.weight,
24705                     cn : [
24706                         {
24707                             tag : 'span',
24708                             cls : 'caret'
24709                         }
24710                     ]
24711                 },
24712                 {
24713                     tag : 'ul',
24714                     cls : 'dropdown-menu'
24715                 }
24716             ]
24717             
24718         };
24719         
24720         if(this.pos == 'top'){
24721             cfg.cls += ' dropup';
24722         }
24723         
24724         if(this.isSubMenu){
24725             cfg = {
24726                 tag : 'ul',
24727                 cls : 'dropdown-menu'
24728             }
24729         }
24730         
24731         return cfg;
24732     },
24733     
24734     onRender : function(ct, position)
24735     {
24736         this.isSubMenu = ct.hasClass('dropdown-submenu');
24737         
24738         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24739     },
24740     
24741     initEvents : function() 
24742     {
24743         if(this.isSubMenu){
24744             return;
24745         }
24746         
24747         this.hidden = true;
24748         
24749         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24750         this.triggerEl.on('click', this.onTriggerPress, this);
24751         
24752         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24753         this.buttonEl.on('click', this.onClick, this);
24754         
24755     },
24756     
24757     list : function()
24758     {
24759         if(this.isSubMenu){
24760             return this.el;
24761         }
24762         
24763         return this.el.select('ul.dropdown-menu', true).first();
24764     },
24765     
24766     onClick : function(e)
24767     {
24768         this.fireEvent("click", this, e);
24769     },
24770     
24771     onTriggerPress  : function(e)
24772     {   
24773         if (this.isVisible()) {
24774             this.hide();
24775         } else {
24776             this.show();
24777         }
24778     },
24779     
24780     isVisible : function(){
24781         return !this.hidden;
24782     },
24783     
24784     show : function()
24785     {
24786         this.fireEvent("beforeshow", this);
24787         
24788         this.hidden = false;
24789         this.el.addClass('open');
24790         
24791         Roo.get(document).on("mouseup", this.onMouseUp, this);
24792         
24793         this.fireEvent("show", this);
24794         
24795         
24796     },
24797     
24798     hide : function()
24799     {
24800         this.fireEvent("beforehide", this);
24801         
24802         this.hidden = true;
24803         this.el.removeClass('open');
24804         
24805         Roo.get(document).un("mouseup", this.onMouseUp);
24806         
24807         this.fireEvent("hide", this);
24808     },
24809     
24810     onMouseUp : function()
24811     {
24812         this.hide();
24813     }
24814     
24815 });
24816
24817  
24818  /*
24819  * - LGPL
24820  *
24821  * menu item
24822  * 
24823  */
24824 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24825
24826 /**
24827  * @class Roo.bootstrap.menu.Item
24828  * @extends Roo.bootstrap.Component
24829  * Bootstrap MenuItem class
24830  * @cfg {Boolean} submenu (true | false) default false
24831  * @cfg {String} html text of the item
24832  * @cfg {String} href the link
24833  * @cfg {Boolean} disable (true | false) default false
24834  * @cfg {Boolean} preventDefault (true | false) default true
24835  * @cfg {String} icon Font awesome icon
24836  * @cfg {String} pos Submenu align to (left | right) default right 
24837  * 
24838  * 
24839  * @constructor
24840  * Create a new Item
24841  * @param {Object} config The config object
24842  */
24843
24844
24845 Roo.bootstrap.menu.Item = function(config){
24846     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24847     this.addEvents({
24848         /**
24849          * @event mouseover
24850          * Fires when the mouse is hovering over this menu
24851          * @param {Roo.bootstrap.menu.Item} this
24852          * @param {Roo.EventObject} e
24853          */
24854         mouseover : true,
24855         /**
24856          * @event mouseout
24857          * Fires when the mouse exits this menu
24858          * @param {Roo.bootstrap.menu.Item} this
24859          * @param {Roo.EventObject} e
24860          */
24861         mouseout : true,
24862         // raw events
24863         /**
24864          * @event click
24865          * The raw click event for the entire grid.
24866          * @param {Roo.EventObject} e
24867          */
24868         click : true
24869     });
24870 };
24871
24872 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24873     
24874     submenu : false,
24875     href : '',
24876     html : '',
24877     preventDefault: true,
24878     disable : false,
24879     icon : false,
24880     pos : 'right',
24881     
24882     getAutoCreate : function()
24883     {
24884         var text = [
24885             {
24886                 tag : 'span',
24887                 cls : 'roo-menu-item-text',
24888                 html : this.html
24889             }
24890         ];
24891         
24892         if(this.icon){
24893             text.unshift({
24894                 tag : 'i',
24895                 cls : 'fa ' + this.icon
24896             })
24897         }
24898         
24899         var cfg = {
24900             tag : 'li',
24901             cn : [
24902                 {
24903                     tag : 'a',
24904                     href : this.href || '#',
24905                     cn : text
24906                 }
24907             ]
24908         };
24909         
24910         if(this.disable){
24911             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24912         }
24913         
24914         if(this.submenu){
24915             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24916             
24917             if(this.pos == 'left'){
24918                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24919             }
24920         }
24921         
24922         return cfg;
24923     },
24924     
24925     initEvents : function() 
24926     {
24927         this.el.on('mouseover', this.onMouseOver, this);
24928         this.el.on('mouseout', this.onMouseOut, this);
24929         
24930         this.el.select('a', true).first().on('click', this.onClick, this);
24931         
24932     },
24933     
24934     onClick : function(e)
24935     {
24936         if(this.preventDefault){
24937             e.preventDefault();
24938         }
24939         
24940         this.fireEvent("click", this, e);
24941     },
24942     
24943     onMouseOver : function(e)
24944     {
24945         if(this.submenu && this.pos == 'left'){
24946             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24947         }
24948         
24949         this.fireEvent("mouseover", this, e);
24950     },
24951     
24952     onMouseOut : function(e)
24953     {
24954         this.fireEvent("mouseout", this, e);
24955     }
24956 });
24957
24958  
24959
24960  /*
24961  * - LGPL
24962  *
24963  * menu separator
24964  * 
24965  */
24966 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24967
24968 /**
24969  * @class Roo.bootstrap.menu.Separator
24970  * @extends Roo.bootstrap.Component
24971  * Bootstrap Separator class
24972  * 
24973  * @constructor
24974  * Create a new Separator
24975  * @param {Object} config The config object
24976  */
24977
24978
24979 Roo.bootstrap.menu.Separator = function(config){
24980     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24981 };
24982
24983 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24984     
24985     getAutoCreate : function(){
24986         var cfg = {
24987             tag : 'li',
24988             cls: 'divider'
24989         };
24990         
24991         return cfg;
24992     }
24993    
24994 });
24995
24996  
24997
24998  /*
24999  * - LGPL
25000  *
25001  * Tooltip
25002  * 
25003  */
25004
25005 /**
25006  * @class Roo.bootstrap.Tooltip
25007  * Bootstrap Tooltip class
25008  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25009  * to determine which dom element triggers the tooltip.
25010  * 
25011  * It needs to add support for additional attributes like tooltip-position
25012  * 
25013  * @constructor
25014  * Create a new Toolti
25015  * @param {Object} config The config object
25016  */
25017
25018 Roo.bootstrap.Tooltip = function(config){
25019     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25020     
25021     this.alignment = Roo.bootstrap.Tooltip.alignment;
25022     
25023     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25024         this.alignment = config.alignment;
25025     }
25026     
25027 };
25028
25029 Roo.apply(Roo.bootstrap.Tooltip, {
25030     /**
25031      * @function init initialize tooltip monitoring.
25032      * @static
25033      */
25034     currentEl : false,
25035     currentTip : false,
25036     currentRegion : false,
25037     
25038     //  init : delay?
25039     
25040     init : function()
25041     {
25042         Roo.get(document).on('mouseover', this.enter ,this);
25043         Roo.get(document).on('mouseout', this.leave, this);
25044          
25045         
25046         this.currentTip = new Roo.bootstrap.Tooltip();
25047     },
25048     
25049     enter : function(ev)
25050     {
25051         var dom = ev.getTarget();
25052         
25053         //Roo.log(['enter',dom]);
25054         var el = Roo.fly(dom);
25055         if (this.currentEl) {
25056             //Roo.log(dom);
25057             //Roo.log(this.currentEl);
25058             //Roo.log(this.currentEl.contains(dom));
25059             if (this.currentEl == el) {
25060                 return;
25061             }
25062             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25063                 return;
25064             }
25065
25066         }
25067         
25068         if (this.currentTip.el) {
25069             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25070         }    
25071         //Roo.log(ev);
25072         
25073         if(!el || el.dom == document){
25074             return;
25075         }
25076         
25077         var bindEl = el;
25078         
25079         // you can not look for children, as if el is the body.. then everythign is the child..
25080         if (!el.attr('tooltip')) { //
25081             if (!el.select("[tooltip]").elements.length) {
25082                 return;
25083             }
25084             // is the mouse over this child...?
25085             bindEl = el.select("[tooltip]").first();
25086             var xy = ev.getXY();
25087             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25088                 //Roo.log("not in region.");
25089                 return;
25090             }
25091             //Roo.log("child element over..");
25092             
25093         }
25094         this.currentEl = bindEl;
25095         this.currentTip.bind(bindEl);
25096         this.currentRegion = Roo.lib.Region.getRegion(dom);
25097         this.currentTip.enter();
25098         
25099     },
25100     leave : function(ev)
25101     {
25102         var dom = ev.getTarget();
25103         //Roo.log(['leave',dom]);
25104         if (!this.currentEl) {
25105             return;
25106         }
25107         
25108         
25109         if (dom != this.currentEl.dom) {
25110             return;
25111         }
25112         var xy = ev.getXY();
25113         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25114             return;
25115         }
25116         // only activate leave if mouse cursor is outside... bounding box..
25117         
25118         
25119         
25120         
25121         if (this.currentTip) {
25122             this.currentTip.leave();
25123         }
25124         //Roo.log('clear currentEl');
25125         this.currentEl = false;
25126         
25127         
25128     },
25129     alignment : {
25130         'left' : ['r-l', [-2,0], 'right'],
25131         'right' : ['l-r', [2,0], 'left'],
25132         'bottom' : ['t-b', [0,2], 'top'],
25133         'top' : [ 'b-t', [0,-2], 'bottom']
25134     }
25135     
25136 });
25137
25138
25139 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25140     
25141     
25142     bindEl : false,
25143     
25144     delay : null, // can be { show : 300 , hide: 500}
25145     
25146     timeout : null,
25147     
25148     hoverState : null, //???
25149     
25150     placement : 'bottom', 
25151     
25152     alignment : false,
25153     
25154     getAutoCreate : function(){
25155     
25156         var cfg = {
25157            cls : 'tooltip',
25158            role : 'tooltip',
25159            cn : [
25160                 {
25161                     cls : 'tooltip-arrow'
25162                 },
25163                 {
25164                     cls : 'tooltip-inner'
25165                 }
25166            ]
25167         };
25168         
25169         return cfg;
25170     },
25171     bind : function(el)
25172     {
25173         this.bindEl = el;
25174     },
25175       
25176     
25177     enter : function () {
25178        
25179         if (this.timeout != null) {
25180             clearTimeout(this.timeout);
25181         }
25182         
25183         this.hoverState = 'in';
25184          //Roo.log("enter - show");
25185         if (!this.delay || !this.delay.show) {
25186             this.show();
25187             return;
25188         }
25189         var _t = this;
25190         this.timeout = setTimeout(function () {
25191             if (_t.hoverState == 'in') {
25192                 _t.show();
25193             }
25194         }, this.delay.show);
25195     },
25196     leave : function()
25197     {
25198         clearTimeout(this.timeout);
25199     
25200         this.hoverState = 'out';
25201          if (!this.delay || !this.delay.hide) {
25202             this.hide();
25203             return;
25204         }
25205        
25206         var _t = this;
25207         this.timeout = setTimeout(function () {
25208             //Roo.log("leave - timeout");
25209             
25210             if (_t.hoverState == 'out') {
25211                 _t.hide();
25212                 Roo.bootstrap.Tooltip.currentEl = false;
25213             }
25214         }, delay);
25215     },
25216     
25217     show : function (msg)
25218     {
25219         if (!this.el) {
25220             this.render(document.body);
25221         }
25222         // set content.
25223         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25224         
25225         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25226         
25227         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25228         
25229         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25230         
25231         var placement = typeof this.placement == 'function' ?
25232             this.placement.call(this, this.el, on_el) :
25233             this.placement;
25234             
25235         var autoToken = /\s?auto?\s?/i;
25236         var autoPlace = autoToken.test(placement);
25237         if (autoPlace) {
25238             placement = placement.replace(autoToken, '') || 'top';
25239         }
25240         
25241         //this.el.detach()
25242         //this.el.setXY([0,0]);
25243         this.el.show();
25244         //this.el.dom.style.display='block';
25245         
25246         //this.el.appendTo(on_el);
25247         
25248         var p = this.getPosition();
25249         var box = this.el.getBox();
25250         
25251         if (autoPlace) {
25252             // fixme..
25253         }
25254         
25255         var align = this.alignment[placement];
25256         
25257         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25258         
25259         if(placement == 'top' || placement == 'bottom'){
25260             if(xy[0] < 0){
25261                 placement = 'right';
25262             }
25263             
25264             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25265                 placement = 'left';
25266             }
25267             
25268             var scroll = Roo.select('body', true).first().getScroll();
25269             
25270             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25271                 placement = 'top';
25272             }
25273             
25274         }
25275         
25276         this.el.alignTo(this.bindEl, align[0],align[1]);
25277         //var arrow = this.el.select('.arrow',true).first();
25278         //arrow.set(align[2], 
25279         
25280         this.el.addClass(placement);
25281         
25282         this.el.addClass('in fade');
25283         
25284         this.hoverState = null;
25285         
25286         if (this.el.hasClass('fade')) {
25287             // fade it?
25288         }
25289         
25290     },
25291     hide : function()
25292     {
25293          
25294         if (!this.el) {
25295             return;
25296         }
25297         //this.el.setXY([0,0]);
25298         this.el.removeClass('in');
25299         //this.el.hide();
25300         
25301     }
25302     
25303 });
25304  
25305
25306  /*
25307  * - LGPL
25308  *
25309  * Location Picker
25310  * 
25311  */
25312
25313 /**
25314  * @class Roo.bootstrap.LocationPicker
25315  * @extends Roo.bootstrap.Component
25316  * Bootstrap LocationPicker class
25317  * @cfg {Number} latitude Position when init default 0
25318  * @cfg {Number} longitude Position when init default 0
25319  * @cfg {Number} zoom default 15
25320  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25321  * @cfg {Boolean} mapTypeControl default false
25322  * @cfg {Boolean} disableDoubleClickZoom default false
25323  * @cfg {Boolean} scrollwheel default true
25324  * @cfg {Boolean} streetViewControl default false
25325  * @cfg {Number} radius default 0
25326  * @cfg {String} locationName
25327  * @cfg {Boolean} draggable default true
25328  * @cfg {Boolean} enableAutocomplete default false
25329  * @cfg {Boolean} enableReverseGeocode default true
25330  * @cfg {String} markerTitle
25331  * 
25332  * @constructor
25333  * Create a new LocationPicker
25334  * @param {Object} config The config object
25335  */
25336
25337
25338 Roo.bootstrap.LocationPicker = function(config){
25339     
25340     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25341     
25342     this.addEvents({
25343         /**
25344          * @event initial
25345          * Fires when the picker initialized.
25346          * @param {Roo.bootstrap.LocationPicker} this
25347          * @param {Google Location} location
25348          */
25349         initial : true,
25350         /**
25351          * @event positionchanged
25352          * Fires when the picker position changed.
25353          * @param {Roo.bootstrap.LocationPicker} this
25354          * @param {Google Location} location
25355          */
25356         positionchanged : true,
25357         /**
25358          * @event resize
25359          * Fires when the map resize.
25360          * @param {Roo.bootstrap.LocationPicker} this
25361          */
25362         resize : true,
25363         /**
25364          * @event show
25365          * Fires when the map show.
25366          * @param {Roo.bootstrap.LocationPicker} this
25367          */
25368         show : true,
25369         /**
25370          * @event hide
25371          * Fires when the map hide.
25372          * @param {Roo.bootstrap.LocationPicker} this
25373          */
25374         hide : true,
25375         /**
25376          * @event mapClick
25377          * Fires when click the map.
25378          * @param {Roo.bootstrap.LocationPicker} this
25379          * @param {Map event} e
25380          */
25381         mapClick : true,
25382         /**
25383          * @event mapRightClick
25384          * Fires when right click the map.
25385          * @param {Roo.bootstrap.LocationPicker} this
25386          * @param {Map event} e
25387          */
25388         mapRightClick : true,
25389         /**
25390          * @event markerClick
25391          * Fires when click the marker.
25392          * @param {Roo.bootstrap.LocationPicker} this
25393          * @param {Map event} e
25394          */
25395         markerClick : true,
25396         /**
25397          * @event markerRightClick
25398          * Fires when right click the marker.
25399          * @param {Roo.bootstrap.LocationPicker} this
25400          * @param {Map event} e
25401          */
25402         markerRightClick : true,
25403         /**
25404          * @event OverlayViewDraw
25405          * Fires when OverlayView Draw
25406          * @param {Roo.bootstrap.LocationPicker} this
25407          */
25408         OverlayViewDraw : true,
25409         /**
25410          * @event OverlayViewOnAdd
25411          * Fires when OverlayView Draw
25412          * @param {Roo.bootstrap.LocationPicker} this
25413          */
25414         OverlayViewOnAdd : true,
25415         /**
25416          * @event OverlayViewOnRemove
25417          * Fires when OverlayView Draw
25418          * @param {Roo.bootstrap.LocationPicker} this
25419          */
25420         OverlayViewOnRemove : true,
25421         /**
25422          * @event OverlayViewShow
25423          * Fires when OverlayView Draw
25424          * @param {Roo.bootstrap.LocationPicker} this
25425          * @param {Pixel} cpx
25426          */
25427         OverlayViewShow : true,
25428         /**
25429          * @event OverlayViewHide
25430          * Fires when OverlayView Draw
25431          * @param {Roo.bootstrap.LocationPicker} this
25432          */
25433         OverlayViewHide : true,
25434         /**
25435          * @event loadexception
25436          * Fires when load google lib failed.
25437          * @param {Roo.bootstrap.LocationPicker} this
25438          */
25439         loadexception : true
25440     });
25441         
25442 };
25443
25444 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25445     
25446     gMapContext: false,
25447     
25448     latitude: 0,
25449     longitude: 0,
25450     zoom: 15,
25451     mapTypeId: false,
25452     mapTypeControl: false,
25453     disableDoubleClickZoom: false,
25454     scrollwheel: true,
25455     streetViewControl: false,
25456     radius: 0,
25457     locationName: '',
25458     draggable: true,
25459     enableAutocomplete: false,
25460     enableReverseGeocode: true,
25461     markerTitle: '',
25462     
25463     getAutoCreate: function()
25464     {
25465
25466         var cfg = {
25467             tag: 'div',
25468             cls: 'roo-location-picker'
25469         };
25470         
25471         return cfg
25472     },
25473     
25474     initEvents: function(ct, position)
25475     {       
25476         if(!this.el.getWidth() || this.isApplied()){
25477             return;
25478         }
25479         
25480         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25481         
25482         this.initial();
25483     },
25484     
25485     initial: function()
25486     {
25487         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25488             this.fireEvent('loadexception', this);
25489             return;
25490         }
25491         
25492         if(!this.mapTypeId){
25493             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25494         }
25495         
25496         this.gMapContext = this.GMapContext();
25497         
25498         this.initOverlayView();
25499         
25500         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25501         
25502         var _this = this;
25503                 
25504         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25505             _this.setPosition(_this.gMapContext.marker.position);
25506         });
25507         
25508         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25509             _this.fireEvent('mapClick', this, event);
25510             
25511         });
25512
25513         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25514             _this.fireEvent('mapRightClick', this, event);
25515             
25516         });
25517         
25518         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25519             _this.fireEvent('markerClick', this, event);
25520             
25521         });
25522
25523         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25524             _this.fireEvent('markerRightClick', this, event);
25525             
25526         });
25527         
25528         this.setPosition(this.gMapContext.location);
25529         
25530         this.fireEvent('initial', this, this.gMapContext.location);
25531     },
25532     
25533     initOverlayView: function()
25534     {
25535         var _this = this;
25536         
25537         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25538             
25539             draw: function()
25540             {
25541                 _this.fireEvent('OverlayViewDraw', _this);
25542             },
25543             
25544             onAdd: function()
25545             {
25546                 _this.fireEvent('OverlayViewOnAdd', _this);
25547             },
25548             
25549             onRemove: function()
25550             {
25551                 _this.fireEvent('OverlayViewOnRemove', _this);
25552             },
25553             
25554             show: function(cpx)
25555             {
25556                 _this.fireEvent('OverlayViewShow', _this, cpx);
25557             },
25558             
25559             hide: function()
25560             {
25561                 _this.fireEvent('OverlayViewHide', _this);
25562             }
25563             
25564         });
25565     },
25566     
25567     fromLatLngToContainerPixel: function(event)
25568     {
25569         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25570     },
25571     
25572     isApplied: function() 
25573     {
25574         return this.getGmapContext() == false ? false : true;
25575     },
25576     
25577     getGmapContext: function() 
25578     {
25579         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25580     },
25581     
25582     GMapContext: function() 
25583     {
25584         var position = new google.maps.LatLng(this.latitude, this.longitude);
25585         
25586         var _map = new google.maps.Map(this.el.dom, {
25587             center: position,
25588             zoom: this.zoom,
25589             mapTypeId: this.mapTypeId,
25590             mapTypeControl: this.mapTypeControl,
25591             disableDoubleClickZoom: this.disableDoubleClickZoom,
25592             scrollwheel: this.scrollwheel,
25593             streetViewControl: this.streetViewControl,
25594             locationName: this.locationName,
25595             draggable: this.draggable,
25596             enableAutocomplete: this.enableAutocomplete,
25597             enableReverseGeocode: this.enableReverseGeocode
25598         });
25599         
25600         var _marker = new google.maps.Marker({
25601             position: position,
25602             map: _map,
25603             title: this.markerTitle,
25604             draggable: this.draggable
25605         });
25606         
25607         return {
25608             map: _map,
25609             marker: _marker,
25610             circle: null,
25611             location: position,
25612             radius: this.radius,
25613             locationName: this.locationName,
25614             addressComponents: {
25615                 formatted_address: null,
25616                 addressLine1: null,
25617                 addressLine2: null,
25618                 streetName: null,
25619                 streetNumber: null,
25620                 city: null,
25621                 district: null,
25622                 state: null,
25623                 stateOrProvince: null
25624             },
25625             settings: this,
25626             domContainer: this.el.dom,
25627             geodecoder: new google.maps.Geocoder()
25628         };
25629     },
25630     
25631     drawCircle: function(center, radius, options) 
25632     {
25633         if (this.gMapContext.circle != null) {
25634             this.gMapContext.circle.setMap(null);
25635         }
25636         if (radius > 0) {
25637             radius *= 1;
25638             options = Roo.apply({}, options, {
25639                 strokeColor: "#0000FF",
25640                 strokeOpacity: .35,
25641                 strokeWeight: 2,
25642                 fillColor: "#0000FF",
25643                 fillOpacity: .2
25644             });
25645             
25646             options.map = this.gMapContext.map;
25647             options.radius = radius;
25648             options.center = center;
25649             this.gMapContext.circle = new google.maps.Circle(options);
25650             return this.gMapContext.circle;
25651         }
25652         
25653         return null;
25654     },
25655     
25656     setPosition: function(location) 
25657     {
25658         this.gMapContext.location = location;
25659         this.gMapContext.marker.setPosition(location);
25660         this.gMapContext.map.panTo(location);
25661         this.drawCircle(location, this.gMapContext.radius, {});
25662         
25663         var _this = this;
25664         
25665         if (this.gMapContext.settings.enableReverseGeocode) {
25666             this.gMapContext.geodecoder.geocode({
25667                 latLng: this.gMapContext.location
25668             }, function(results, status) {
25669                 
25670                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25671                     _this.gMapContext.locationName = results[0].formatted_address;
25672                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25673                     
25674                     _this.fireEvent('positionchanged', this, location);
25675                 }
25676             });
25677             
25678             return;
25679         }
25680         
25681         this.fireEvent('positionchanged', this, location);
25682     },
25683     
25684     resize: function()
25685     {
25686         google.maps.event.trigger(this.gMapContext.map, "resize");
25687         
25688         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25689         
25690         this.fireEvent('resize', this);
25691     },
25692     
25693     setPositionByLatLng: function(latitude, longitude)
25694     {
25695         this.setPosition(new google.maps.LatLng(latitude, longitude));
25696     },
25697     
25698     getCurrentPosition: function() 
25699     {
25700         return {
25701             latitude: this.gMapContext.location.lat(),
25702             longitude: this.gMapContext.location.lng()
25703         };
25704     },
25705     
25706     getAddressName: function() 
25707     {
25708         return this.gMapContext.locationName;
25709     },
25710     
25711     getAddressComponents: function() 
25712     {
25713         return this.gMapContext.addressComponents;
25714     },
25715     
25716     address_component_from_google_geocode: function(address_components) 
25717     {
25718         var result = {};
25719         
25720         for (var i = 0; i < address_components.length; i++) {
25721             var component = address_components[i];
25722             if (component.types.indexOf("postal_code") >= 0) {
25723                 result.postalCode = component.short_name;
25724             } else if (component.types.indexOf("street_number") >= 0) {
25725                 result.streetNumber = component.short_name;
25726             } else if (component.types.indexOf("route") >= 0) {
25727                 result.streetName = component.short_name;
25728             } else if (component.types.indexOf("neighborhood") >= 0) {
25729                 result.city = component.short_name;
25730             } else if (component.types.indexOf("locality") >= 0) {
25731                 result.city = component.short_name;
25732             } else if (component.types.indexOf("sublocality") >= 0) {
25733                 result.district = component.short_name;
25734             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25735                 result.stateOrProvince = component.short_name;
25736             } else if (component.types.indexOf("country") >= 0) {
25737                 result.country = component.short_name;
25738             }
25739         }
25740         
25741         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25742         result.addressLine2 = "";
25743         return result;
25744     },
25745     
25746     setZoomLevel: function(zoom)
25747     {
25748         this.gMapContext.map.setZoom(zoom);
25749     },
25750     
25751     show: function()
25752     {
25753         if(!this.el){
25754             return;
25755         }
25756         
25757         this.el.show();
25758         
25759         this.resize();
25760         
25761         this.fireEvent('show', this);
25762     },
25763     
25764     hide: function()
25765     {
25766         if(!this.el){
25767             return;
25768         }
25769         
25770         this.el.hide();
25771         
25772         this.fireEvent('hide', this);
25773     }
25774     
25775 });
25776
25777 Roo.apply(Roo.bootstrap.LocationPicker, {
25778     
25779     OverlayView : function(map, options)
25780     {
25781         options = options || {};
25782         
25783         this.setMap(map);
25784     }
25785     
25786     
25787 });/*
25788  * - LGPL
25789  *
25790  * Alert
25791  * 
25792  */
25793
25794 /**
25795  * @class Roo.bootstrap.Alert
25796  * @extends Roo.bootstrap.Component
25797  * Bootstrap Alert class
25798  * @cfg {String} title The title of alert
25799  * @cfg {String} html The content of alert
25800  * @cfg {String} weight (  success | info | warning | danger )
25801  * @cfg {String} faicon font-awesomeicon
25802  * 
25803  * @constructor
25804  * Create a new alert
25805  * @param {Object} config The config object
25806  */
25807
25808
25809 Roo.bootstrap.Alert = function(config){
25810     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25811     
25812 };
25813
25814 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25815     
25816     title: '',
25817     html: '',
25818     weight: false,
25819     faicon: false,
25820     
25821     getAutoCreate : function()
25822     {
25823         
25824         var cfg = {
25825             tag : 'div',
25826             cls : 'alert',
25827             cn : [
25828                 {
25829                     tag : 'i',
25830                     cls : 'roo-alert-icon'
25831                     
25832                 },
25833                 {
25834                     tag : 'b',
25835                     cls : 'roo-alert-title',
25836                     html : this.title
25837                 },
25838                 {
25839                     tag : 'span',
25840                     cls : 'roo-alert-text',
25841                     html : this.html
25842                 }
25843             ]
25844         };
25845         
25846         if(this.faicon){
25847             cfg.cn[0].cls += ' fa ' + this.faicon;
25848         }
25849         
25850         if(this.weight){
25851             cfg.cls += ' alert-' + this.weight;
25852         }
25853         
25854         return cfg;
25855     },
25856     
25857     initEvents: function() 
25858     {
25859         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25860     },
25861     
25862     setTitle : function(str)
25863     {
25864         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25865     },
25866     
25867     setText : function(str)
25868     {
25869         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25870     },
25871     
25872     setWeight : function(weight)
25873     {
25874         if(this.weight){
25875             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25876         }
25877         
25878         this.weight = weight;
25879         
25880         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25881     },
25882     
25883     setIcon : function(icon)
25884     {
25885         if(this.faicon){
25886             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25887         }
25888         
25889         this.faicon = icon;
25890         
25891         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25892     },
25893     
25894     hide: function() 
25895     {
25896         this.el.hide();   
25897     },
25898     
25899     show: function() 
25900     {  
25901         this.el.show();   
25902     }
25903     
25904 });
25905
25906  
25907 /*
25908 * Licence: LGPL
25909 */
25910
25911 /**
25912  * @class Roo.bootstrap.UploadCropbox
25913  * @extends Roo.bootstrap.Component
25914  * Bootstrap UploadCropbox class
25915  * @cfg {String} emptyText show when image has been loaded
25916  * @cfg {String} rotateNotify show when image too small to rotate
25917  * @cfg {Number} errorTimeout default 3000
25918  * @cfg {Number} minWidth default 300
25919  * @cfg {Number} minHeight default 300
25920  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25921  * @cfg {Boolean} isDocument (true|false) default false
25922  * @cfg {String} url action url
25923  * @cfg {String} paramName default 'imageUpload'
25924  * @cfg {String} method default POST
25925  * @cfg {Boolean} loadMask (true|false) default true
25926  * @cfg {Boolean} loadingText default 'Loading...'
25927  * 
25928  * @constructor
25929  * Create a new UploadCropbox
25930  * @param {Object} config The config object
25931  */
25932
25933 Roo.bootstrap.UploadCropbox = function(config){
25934     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25935     
25936     this.addEvents({
25937         /**
25938          * @event beforeselectfile
25939          * Fire before select file
25940          * @param {Roo.bootstrap.UploadCropbox} this
25941          */
25942         "beforeselectfile" : true,
25943         /**
25944          * @event initial
25945          * Fire after initEvent
25946          * @param {Roo.bootstrap.UploadCropbox} this
25947          */
25948         "initial" : true,
25949         /**
25950          * @event crop
25951          * Fire after initEvent
25952          * @param {Roo.bootstrap.UploadCropbox} this
25953          * @param {String} data
25954          */
25955         "crop" : true,
25956         /**
25957          * @event prepare
25958          * Fire when preparing the file data
25959          * @param {Roo.bootstrap.UploadCropbox} this
25960          * @param {Object} file
25961          */
25962         "prepare" : true,
25963         /**
25964          * @event exception
25965          * Fire when get exception
25966          * @param {Roo.bootstrap.UploadCropbox} this
25967          * @param {XMLHttpRequest} xhr
25968          */
25969         "exception" : true,
25970         /**
25971          * @event beforeloadcanvas
25972          * Fire before load the canvas
25973          * @param {Roo.bootstrap.UploadCropbox} this
25974          * @param {String} src
25975          */
25976         "beforeloadcanvas" : true,
25977         /**
25978          * @event trash
25979          * Fire when trash image
25980          * @param {Roo.bootstrap.UploadCropbox} this
25981          */
25982         "trash" : true,
25983         /**
25984          * @event download
25985          * Fire when download the image
25986          * @param {Roo.bootstrap.UploadCropbox} this
25987          */
25988         "download" : true,
25989         /**
25990          * @event footerbuttonclick
25991          * Fire when footerbuttonclick
25992          * @param {Roo.bootstrap.UploadCropbox} this
25993          * @param {String} type
25994          */
25995         "footerbuttonclick" : true,
25996         /**
25997          * @event resize
25998          * Fire when resize
25999          * @param {Roo.bootstrap.UploadCropbox} this
26000          */
26001         "resize" : true,
26002         /**
26003          * @event rotate
26004          * Fire when rotate the image
26005          * @param {Roo.bootstrap.UploadCropbox} this
26006          * @param {String} pos
26007          */
26008         "rotate" : true,
26009         /**
26010          * @event inspect
26011          * Fire when inspect the file
26012          * @param {Roo.bootstrap.UploadCropbox} this
26013          * @param {Object} file
26014          */
26015         "inspect" : true,
26016         /**
26017          * @event upload
26018          * Fire when xhr upload the file
26019          * @param {Roo.bootstrap.UploadCropbox} this
26020          * @param {Object} data
26021          */
26022         "upload" : true,
26023         /**
26024          * @event arrange
26025          * Fire when arrange the file data
26026          * @param {Roo.bootstrap.UploadCropbox} this
26027          * @param {Object} formData
26028          */
26029         "arrange" : true
26030     });
26031     
26032     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26033 };
26034
26035 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26036     
26037     emptyText : 'Click to upload image',
26038     rotateNotify : 'Image is too small to rotate',
26039     errorTimeout : 3000,
26040     scale : 0,
26041     baseScale : 1,
26042     rotate : 0,
26043     dragable : false,
26044     pinching : false,
26045     mouseX : 0,
26046     mouseY : 0,
26047     cropData : false,
26048     minWidth : 300,
26049     minHeight : 300,
26050     file : false,
26051     exif : {},
26052     baseRotate : 1,
26053     cropType : 'image/jpeg',
26054     buttons : false,
26055     canvasLoaded : false,
26056     isDocument : false,
26057     method : 'POST',
26058     paramName : 'imageUpload',
26059     loadMask : true,
26060     loadingText : 'Loading...',
26061     maskEl : false,
26062     
26063     getAutoCreate : function()
26064     {
26065         var cfg = {
26066             tag : 'div',
26067             cls : 'roo-upload-cropbox',
26068             cn : [
26069                 {
26070                     tag : 'input',
26071                     cls : 'roo-upload-cropbox-selector',
26072                     type : 'file'
26073                 },
26074                 {
26075                     tag : 'div',
26076                     cls : 'roo-upload-cropbox-body',
26077                     style : 'cursor:pointer',
26078                     cn : [
26079                         {
26080                             tag : 'div',
26081                             cls : 'roo-upload-cropbox-preview'
26082                         },
26083                         {
26084                             tag : 'div',
26085                             cls : 'roo-upload-cropbox-thumb'
26086                         },
26087                         {
26088                             tag : 'div',
26089                             cls : 'roo-upload-cropbox-empty-notify',
26090                             html : this.emptyText
26091                         },
26092                         {
26093                             tag : 'div',
26094                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26095                             html : this.rotateNotify
26096                         }
26097                     ]
26098                 },
26099                 {
26100                     tag : 'div',
26101                     cls : 'roo-upload-cropbox-footer',
26102                     cn : {
26103                         tag : 'div',
26104                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26105                         cn : []
26106                     }
26107                 }
26108             ]
26109         };
26110         
26111         return cfg;
26112     },
26113     
26114     onRender : function(ct, position)
26115     {
26116         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26117         
26118         if (this.buttons.length) {
26119             
26120             Roo.each(this.buttons, function(bb) {
26121                 
26122                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26123                 
26124                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26125                 
26126             }, this);
26127         }
26128         
26129         if(this.loadMask){
26130             this.maskEl = this.el;
26131         }
26132     },
26133     
26134     initEvents : function()
26135     {
26136         this.urlAPI = (window.createObjectURL && window) || 
26137                                 (window.URL && URL.revokeObjectURL && URL) || 
26138                                 (window.webkitURL && webkitURL);
26139                         
26140         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26141         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26142         
26143         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26144         this.selectorEl.hide();
26145         
26146         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26147         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26148         
26149         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26150         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26151         this.thumbEl.hide();
26152         
26153         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26154         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26155         
26156         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26157         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26158         this.errorEl.hide();
26159         
26160         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26161         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26162         this.footerEl.hide();
26163         
26164         this.setThumbBoxSize();
26165         
26166         this.bind();
26167         
26168         this.resize();
26169         
26170         this.fireEvent('initial', this);
26171     },
26172
26173     bind : function()
26174     {
26175         var _this = this;
26176         
26177         window.addEventListener("resize", function() { _this.resize(); } );
26178         
26179         this.bodyEl.on('click', this.beforeSelectFile, this);
26180         
26181         if(Roo.isTouch){
26182             this.bodyEl.on('touchstart', this.onTouchStart, this);
26183             this.bodyEl.on('touchmove', this.onTouchMove, this);
26184             this.bodyEl.on('touchend', this.onTouchEnd, this);
26185         }
26186         
26187         if(!Roo.isTouch){
26188             this.bodyEl.on('mousedown', this.onMouseDown, this);
26189             this.bodyEl.on('mousemove', this.onMouseMove, this);
26190             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26191             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26192             Roo.get(document).on('mouseup', this.onMouseUp, this);
26193         }
26194         
26195         this.selectorEl.on('change', this.onFileSelected, this);
26196     },
26197     
26198     reset : function()
26199     {    
26200         this.scale = 0;
26201         this.baseScale = 1;
26202         this.rotate = 0;
26203         this.baseRotate = 1;
26204         this.dragable = false;
26205         this.pinching = false;
26206         this.mouseX = 0;
26207         this.mouseY = 0;
26208         this.cropData = false;
26209         this.notifyEl.dom.innerHTML = this.emptyText;
26210         
26211         this.selectorEl.dom.value = '';
26212         
26213     },
26214     
26215     resize : function()
26216     {
26217         if(this.fireEvent('resize', this) != false){
26218             this.setThumbBoxPosition();
26219             this.setCanvasPosition();
26220         }
26221     },
26222     
26223     onFooterButtonClick : function(e, el, o, type)
26224     {
26225         switch (type) {
26226             case 'rotate-left' :
26227                 this.onRotateLeft(e);
26228                 break;
26229             case 'rotate-right' :
26230                 this.onRotateRight(e);
26231                 break;
26232             case 'picture' :
26233                 this.beforeSelectFile(e);
26234                 break;
26235             case 'trash' :
26236                 this.trash(e);
26237                 break;
26238             case 'crop' :
26239                 this.crop(e);
26240                 break;
26241             case 'download' :
26242                 this.download(e);
26243                 break;
26244             default :
26245                 break;
26246         }
26247         
26248         this.fireEvent('footerbuttonclick', this, type);
26249     },
26250     
26251     beforeSelectFile : function(e)
26252     {
26253         e.preventDefault();
26254         
26255         if(this.fireEvent('beforeselectfile', this) != false){
26256             this.selectorEl.dom.click();
26257         }
26258     },
26259     
26260     onFileSelected : function(e)
26261     {
26262         e.preventDefault();
26263         
26264         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26265             return;
26266         }
26267         
26268         var file = this.selectorEl.dom.files[0];
26269         
26270         if(this.fireEvent('inspect', this, file) != false){
26271             this.prepare(file);
26272         }
26273         
26274     },
26275     
26276     trash : function(e)
26277     {
26278         this.fireEvent('trash', this);
26279     },
26280     
26281     download : function(e)
26282     {
26283         this.fireEvent('download', this);
26284     },
26285     
26286     loadCanvas : function(src)
26287     {   
26288         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26289             
26290             this.reset();
26291             
26292             this.imageEl = document.createElement('img');
26293             
26294             var _this = this;
26295             
26296             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26297             
26298             this.imageEl.src = src;
26299         }
26300     },
26301     
26302     onLoadCanvas : function()
26303     {   
26304         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26305         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26306         
26307         this.bodyEl.un('click', this.beforeSelectFile, this);
26308         
26309         this.notifyEl.hide();
26310         this.thumbEl.show();
26311         this.footerEl.show();
26312         
26313         this.baseRotateLevel();
26314         
26315         if(this.isDocument){
26316             this.setThumbBoxSize();
26317         }
26318         
26319         this.setThumbBoxPosition();
26320         
26321         this.baseScaleLevel();
26322         
26323         this.draw();
26324         
26325         this.resize();
26326         
26327         this.canvasLoaded = true;
26328         
26329         if(this.loadMask){
26330             this.maskEl.unmask();
26331         }
26332         
26333     },
26334     
26335     setCanvasPosition : function()
26336     {   
26337         if(!this.canvasEl){
26338             return;
26339         }
26340         
26341         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26342         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26343         
26344         this.previewEl.setLeft(pw);
26345         this.previewEl.setTop(ph);
26346         
26347     },
26348     
26349     onMouseDown : function(e)
26350     {   
26351         e.stopEvent();
26352         
26353         this.dragable = true;
26354         this.pinching = false;
26355         
26356         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26357             this.dragable = false;
26358             return;
26359         }
26360         
26361         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26362         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26363         
26364     },
26365     
26366     onMouseMove : function(e)
26367     {   
26368         e.stopEvent();
26369         
26370         if(!this.canvasLoaded){
26371             return;
26372         }
26373         
26374         if (!this.dragable){
26375             return;
26376         }
26377         
26378         var minX = Math.ceil(this.thumbEl.getLeft(true));
26379         var minY = Math.ceil(this.thumbEl.getTop(true));
26380         
26381         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26382         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26383         
26384         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26385         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26386         
26387         x = x - this.mouseX;
26388         y = y - this.mouseY;
26389         
26390         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26391         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26392         
26393         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26394         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26395         
26396         this.previewEl.setLeft(bgX);
26397         this.previewEl.setTop(bgY);
26398         
26399         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26400         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26401     },
26402     
26403     onMouseUp : function(e)
26404     {   
26405         e.stopEvent();
26406         
26407         this.dragable = false;
26408     },
26409     
26410     onMouseWheel : function(e)
26411     {   
26412         e.stopEvent();
26413         
26414         this.startScale = this.scale;
26415         
26416         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26417         
26418         if(!this.zoomable()){
26419             this.scale = this.startScale;
26420             return;
26421         }
26422         
26423         this.draw();
26424         
26425         return;
26426     },
26427     
26428     zoomable : function()
26429     {
26430         var minScale = this.thumbEl.getWidth() / this.minWidth;
26431         
26432         if(this.minWidth < this.minHeight){
26433             minScale = this.thumbEl.getHeight() / this.minHeight;
26434         }
26435         
26436         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26437         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26438         
26439         if(
26440                 this.isDocument &&
26441                 (this.rotate == 0 || this.rotate == 180) && 
26442                 (
26443                     width > this.imageEl.OriginWidth || 
26444                     height > this.imageEl.OriginHeight ||
26445                     (width < this.minWidth && height < this.minHeight)
26446                 )
26447         ){
26448             return false;
26449         }
26450         
26451         if(
26452                 this.isDocument &&
26453                 (this.rotate == 90 || this.rotate == 270) && 
26454                 (
26455                     width > this.imageEl.OriginWidth || 
26456                     height > this.imageEl.OriginHeight ||
26457                     (width < this.minHeight && height < this.minWidth)
26458                 )
26459         ){
26460             return false;
26461         }
26462         
26463         if(
26464                 !this.isDocument &&
26465                 (this.rotate == 0 || this.rotate == 180) && 
26466                 (
26467                     width < this.minWidth || 
26468                     width > this.imageEl.OriginWidth || 
26469                     height < this.minHeight || 
26470                     height > this.imageEl.OriginHeight
26471                 )
26472         ){
26473             return false;
26474         }
26475         
26476         if(
26477                 !this.isDocument &&
26478                 (this.rotate == 90 || this.rotate == 270) && 
26479                 (
26480                     width < this.minHeight || 
26481                     width > this.imageEl.OriginWidth || 
26482                     height < this.minWidth || 
26483                     height > this.imageEl.OriginHeight
26484                 )
26485         ){
26486             return false;
26487         }
26488         
26489         return true;
26490         
26491     },
26492     
26493     onRotateLeft : function(e)
26494     {   
26495         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26496             
26497             var minScale = this.thumbEl.getWidth() / this.minWidth;
26498             
26499             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26500             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26501             
26502             this.startScale = this.scale;
26503             
26504             while (this.getScaleLevel() < minScale){
26505             
26506                 this.scale = this.scale + 1;
26507                 
26508                 if(!this.zoomable()){
26509                     break;
26510                 }
26511                 
26512                 if(
26513                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26514                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26515                 ){
26516                     continue;
26517                 }
26518                 
26519                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26520
26521                 this.draw();
26522                 
26523                 return;
26524             }
26525             
26526             this.scale = this.startScale;
26527             
26528             this.onRotateFail();
26529             
26530             return false;
26531         }
26532         
26533         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26534
26535         if(this.isDocument){
26536             this.setThumbBoxSize();
26537             this.setThumbBoxPosition();
26538             this.setCanvasPosition();
26539         }
26540         
26541         this.draw();
26542         
26543         this.fireEvent('rotate', this, 'left');
26544         
26545     },
26546     
26547     onRotateRight : function(e)
26548     {
26549         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26550             
26551             var minScale = this.thumbEl.getWidth() / this.minWidth;
26552         
26553             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26554             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26555             
26556             this.startScale = this.scale;
26557             
26558             while (this.getScaleLevel() < minScale){
26559             
26560                 this.scale = this.scale + 1;
26561                 
26562                 if(!this.zoomable()){
26563                     break;
26564                 }
26565                 
26566                 if(
26567                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26568                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26569                 ){
26570                     continue;
26571                 }
26572                 
26573                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26574
26575                 this.draw();
26576                 
26577                 return;
26578             }
26579             
26580             this.scale = this.startScale;
26581             
26582             this.onRotateFail();
26583             
26584             return false;
26585         }
26586         
26587         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26588
26589         if(this.isDocument){
26590             this.setThumbBoxSize();
26591             this.setThumbBoxPosition();
26592             this.setCanvasPosition();
26593         }
26594         
26595         this.draw();
26596         
26597         this.fireEvent('rotate', this, 'right');
26598     },
26599     
26600     onRotateFail : function()
26601     {
26602         this.errorEl.show(true);
26603         
26604         var _this = this;
26605         
26606         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26607     },
26608     
26609     draw : function()
26610     {
26611         this.previewEl.dom.innerHTML = '';
26612         
26613         var canvasEl = document.createElement("canvas");
26614         
26615         var contextEl = canvasEl.getContext("2d");
26616         
26617         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26618         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26619         var center = this.imageEl.OriginWidth / 2;
26620         
26621         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26622             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26623             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26624             center = this.imageEl.OriginHeight / 2;
26625         }
26626         
26627         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26628         
26629         contextEl.translate(center, center);
26630         contextEl.rotate(this.rotate * Math.PI / 180);
26631
26632         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26633         
26634         this.canvasEl = document.createElement("canvas");
26635         
26636         this.contextEl = this.canvasEl.getContext("2d");
26637         
26638         switch (this.rotate) {
26639             case 0 :
26640                 
26641                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26642                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26643                 
26644                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26645                 
26646                 break;
26647             case 90 : 
26648                 
26649                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26650                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26651                 
26652                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26653                     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);
26654                     break;
26655                 }
26656                 
26657                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26658                 
26659                 break;
26660             case 180 :
26661                 
26662                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26663                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26664                 
26665                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26666                     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);
26667                     break;
26668                 }
26669                 
26670                 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);
26671                 
26672                 break;
26673             case 270 :
26674                 
26675                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26676                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26677         
26678                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26679                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26680                     break;
26681                 }
26682                 
26683                 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);
26684                 
26685                 break;
26686             default : 
26687                 break;
26688         }
26689         
26690         this.previewEl.appendChild(this.canvasEl);
26691         
26692         this.setCanvasPosition();
26693     },
26694     
26695     crop : function()
26696     {
26697         if(!this.canvasLoaded){
26698             return;
26699         }
26700         
26701         var imageCanvas = document.createElement("canvas");
26702         
26703         var imageContext = imageCanvas.getContext("2d");
26704         
26705         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26706         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26707         
26708         var center = imageCanvas.width / 2;
26709         
26710         imageContext.translate(center, center);
26711         
26712         imageContext.rotate(this.rotate * Math.PI / 180);
26713         
26714         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26715         
26716         var canvas = document.createElement("canvas");
26717         
26718         var context = canvas.getContext("2d");
26719                 
26720         canvas.width = this.minWidth;
26721         canvas.height = this.minHeight;
26722
26723         switch (this.rotate) {
26724             case 0 :
26725                 
26726                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26727                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26728                 
26729                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26730                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26731                 
26732                 var targetWidth = this.minWidth - 2 * x;
26733                 var targetHeight = this.minHeight - 2 * y;
26734                 
26735                 var scale = 1;
26736                 
26737                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26738                     scale = targetWidth / width;
26739                 }
26740                 
26741                 if(x > 0 && y == 0){
26742                     scale = targetHeight / height;
26743                 }
26744                 
26745                 if(x > 0 && y > 0){
26746                     scale = targetWidth / width;
26747                     
26748                     if(width < height){
26749                         scale = targetHeight / height;
26750                     }
26751                 }
26752                 
26753                 context.scale(scale, scale);
26754                 
26755                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26756                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26757
26758                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26759                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26760
26761                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26762                 
26763                 break;
26764             case 90 : 
26765                 
26766                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26767                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26768                 
26769                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26770                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26771                 
26772                 var targetWidth = this.minWidth - 2 * x;
26773                 var targetHeight = this.minHeight - 2 * y;
26774                 
26775                 var scale = 1;
26776                 
26777                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26778                     scale = targetWidth / width;
26779                 }
26780                 
26781                 if(x > 0 && y == 0){
26782                     scale = targetHeight / height;
26783                 }
26784                 
26785                 if(x > 0 && y > 0){
26786                     scale = targetWidth / width;
26787                     
26788                     if(width < height){
26789                         scale = targetHeight / height;
26790                     }
26791                 }
26792                 
26793                 context.scale(scale, scale);
26794                 
26795                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26796                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26797
26798                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26799                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26800                 
26801                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26802                 
26803                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26804                 
26805                 break;
26806             case 180 :
26807                 
26808                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26809                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26810                 
26811                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26812                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26813                 
26814                 var targetWidth = this.minWidth - 2 * x;
26815                 var targetHeight = this.minHeight - 2 * y;
26816                 
26817                 var scale = 1;
26818                 
26819                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26820                     scale = targetWidth / width;
26821                 }
26822                 
26823                 if(x > 0 && y == 0){
26824                     scale = targetHeight / height;
26825                 }
26826                 
26827                 if(x > 0 && y > 0){
26828                     scale = targetWidth / width;
26829                     
26830                     if(width < height){
26831                         scale = targetHeight / height;
26832                     }
26833                 }
26834                 
26835                 context.scale(scale, scale);
26836                 
26837                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26838                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26839
26840                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26841                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26842
26843                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26844                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26845                 
26846                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26847                 
26848                 break;
26849             case 270 :
26850                 
26851                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26852                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26853                 
26854                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26855                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26856                 
26857                 var targetWidth = this.minWidth - 2 * x;
26858                 var targetHeight = this.minHeight - 2 * y;
26859                 
26860                 var scale = 1;
26861                 
26862                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26863                     scale = targetWidth / width;
26864                 }
26865                 
26866                 if(x > 0 && y == 0){
26867                     scale = targetHeight / height;
26868                 }
26869                 
26870                 if(x > 0 && y > 0){
26871                     scale = targetWidth / width;
26872                     
26873                     if(width < height){
26874                         scale = targetHeight / height;
26875                     }
26876                 }
26877                 
26878                 context.scale(scale, scale);
26879                 
26880                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26881                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26882
26883                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26884                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26885                 
26886                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26887                 
26888                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26889                 
26890                 break;
26891             default : 
26892                 break;
26893         }
26894         
26895         this.cropData = canvas.toDataURL(this.cropType);
26896         
26897         if(this.fireEvent('crop', this, this.cropData) !== false){
26898             this.process(this.file, this.cropData);
26899         }
26900         
26901         return;
26902         
26903     },
26904     
26905     setThumbBoxSize : function()
26906     {
26907         var width, height;
26908         
26909         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26910             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26911             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26912             
26913             this.minWidth = width;
26914             this.minHeight = height;
26915             
26916             if(this.rotate == 90 || this.rotate == 270){
26917                 this.minWidth = height;
26918                 this.minHeight = width;
26919             }
26920         }
26921         
26922         height = 300;
26923         width = Math.ceil(this.minWidth * height / this.minHeight);
26924         
26925         if(this.minWidth > this.minHeight){
26926             width = 300;
26927             height = Math.ceil(this.minHeight * width / this.minWidth);
26928         }
26929         
26930         this.thumbEl.setStyle({
26931             width : width + 'px',
26932             height : height + 'px'
26933         });
26934
26935         return;
26936             
26937     },
26938     
26939     setThumbBoxPosition : function()
26940     {
26941         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26942         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26943         
26944         this.thumbEl.setLeft(x);
26945         this.thumbEl.setTop(y);
26946         
26947     },
26948     
26949     baseRotateLevel : function()
26950     {
26951         this.baseRotate = 1;
26952         
26953         if(
26954                 typeof(this.exif) != 'undefined' &&
26955                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26956                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26957         ){
26958             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26959         }
26960         
26961         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26962         
26963     },
26964     
26965     baseScaleLevel : function()
26966     {
26967         var width, height;
26968         
26969         if(this.isDocument){
26970             
26971             if(this.baseRotate == 6 || this.baseRotate == 8){
26972             
26973                 height = this.thumbEl.getHeight();
26974                 this.baseScale = height / this.imageEl.OriginWidth;
26975
26976                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26977                     width = this.thumbEl.getWidth();
26978                     this.baseScale = width / this.imageEl.OriginHeight;
26979                 }
26980
26981                 return;
26982             }
26983
26984             height = this.thumbEl.getHeight();
26985             this.baseScale = height / this.imageEl.OriginHeight;
26986
26987             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26988                 width = this.thumbEl.getWidth();
26989                 this.baseScale = width / this.imageEl.OriginWidth;
26990             }
26991
26992             return;
26993         }
26994         
26995         if(this.baseRotate == 6 || this.baseRotate == 8){
26996             
26997             width = this.thumbEl.getHeight();
26998             this.baseScale = width / this.imageEl.OriginHeight;
26999             
27000             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27001                 height = this.thumbEl.getWidth();
27002                 this.baseScale = height / this.imageEl.OriginHeight;
27003             }
27004             
27005             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27006                 height = this.thumbEl.getWidth();
27007                 this.baseScale = height / this.imageEl.OriginHeight;
27008                 
27009                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27010                     width = this.thumbEl.getHeight();
27011                     this.baseScale = width / this.imageEl.OriginWidth;
27012                 }
27013             }
27014             
27015             return;
27016         }
27017         
27018         width = this.thumbEl.getWidth();
27019         this.baseScale = width / this.imageEl.OriginWidth;
27020         
27021         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27022             height = this.thumbEl.getHeight();
27023             this.baseScale = height / this.imageEl.OriginHeight;
27024         }
27025         
27026         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27027             
27028             height = this.thumbEl.getHeight();
27029             this.baseScale = height / this.imageEl.OriginHeight;
27030             
27031             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27032                 width = this.thumbEl.getWidth();
27033                 this.baseScale = width / this.imageEl.OriginWidth;
27034             }
27035             
27036         }
27037         
27038         return;
27039     },
27040     
27041     getScaleLevel : function()
27042     {
27043         return this.baseScale * Math.pow(1.1, this.scale);
27044     },
27045     
27046     onTouchStart : function(e)
27047     {
27048         if(!this.canvasLoaded){
27049             this.beforeSelectFile(e);
27050             return;
27051         }
27052         
27053         var touches = e.browserEvent.touches;
27054         
27055         if(!touches){
27056             return;
27057         }
27058         
27059         if(touches.length == 1){
27060             this.onMouseDown(e);
27061             return;
27062         }
27063         
27064         if(touches.length != 2){
27065             return;
27066         }
27067         
27068         var coords = [];
27069         
27070         for(var i = 0, finger; finger = touches[i]; i++){
27071             coords.push(finger.pageX, finger.pageY);
27072         }
27073         
27074         var x = Math.pow(coords[0] - coords[2], 2);
27075         var y = Math.pow(coords[1] - coords[3], 2);
27076         
27077         this.startDistance = Math.sqrt(x + y);
27078         
27079         this.startScale = this.scale;
27080         
27081         this.pinching = true;
27082         this.dragable = false;
27083         
27084     },
27085     
27086     onTouchMove : function(e)
27087     {
27088         if(!this.pinching && !this.dragable){
27089             return;
27090         }
27091         
27092         var touches = e.browserEvent.touches;
27093         
27094         if(!touches){
27095             return;
27096         }
27097         
27098         if(this.dragable){
27099             this.onMouseMove(e);
27100             return;
27101         }
27102         
27103         var coords = [];
27104         
27105         for(var i = 0, finger; finger = touches[i]; i++){
27106             coords.push(finger.pageX, finger.pageY);
27107         }
27108         
27109         var x = Math.pow(coords[0] - coords[2], 2);
27110         var y = Math.pow(coords[1] - coords[3], 2);
27111         
27112         this.endDistance = Math.sqrt(x + y);
27113         
27114         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27115         
27116         if(!this.zoomable()){
27117             this.scale = this.startScale;
27118             return;
27119         }
27120         
27121         this.draw();
27122         
27123     },
27124     
27125     onTouchEnd : function(e)
27126     {
27127         this.pinching = false;
27128         this.dragable = false;
27129         
27130     },
27131     
27132     process : function(file, crop)
27133     {
27134         if(this.loadMask){
27135             this.maskEl.mask(this.loadingText);
27136         }
27137         
27138         this.xhr = new XMLHttpRequest();
27139         
27140         file.xhr = this.xhr;
27141
27142         this.xhr.open(this.method, this.url, true);
27143         
27144         var headers = {
27145             "Accept": "application/json",
27146             "Cache-Control": "no-cache",
27147             "X-Requested-With": "XMLHttpRequest"
27148         };
27149         
27150         for (var headerName in headers) {
27151             var headerValue = headers[headerName];
27152             if (headerValue) {
27153                 this.xhr.setRequestHeader(headerName, headerValue);
27154             }
27155         }
27156         
27157         var _this = this;
27158         
27159         this.xhr.onload = function()
27160         {
27161             _this.xhrOnLoad(_this.xhr);
27162         }
27163         
27164         this.xhr.onerror = function()
27165         {
27166             _this.xhrOnError(_this.xhr);
27167         }
27168         
27169         var formData = new FormData();
27170
27171         formData.append('returnHTML', 'NO');
27172         
27173         if(crop){
27174             formData.append('crop', crop);
27175         }
27176         
27177         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27178             formData.append(this.paramName, file, file.name);
27179         }
27180         
27181         if(typeof(file.filename) != 'undefined'){
27182             formData.append('filename', file.filename);
27183         }
27184         
27185         if(typeof(file.mimetype) != 'undefined'){
27186             formData.append('mimetype', file.mimetype);
27187         }
27188         
27189         if(this.fireEvent('arrange', this, formData) != false){
27190             this.xhr.send(formData);
27191         };
27192     },
27193     
27194     xhrOnLoad : function(xhr)
27195     {
27196         if(this.loadMask){
27197             this.maskEl.unmask();
27198         }
27199         
27200         if (xhr.readyState !== 4) {
27201             this.fireEvent('exception', this, xhr);
27202             return;
27203         }
27204
27205         var response = Roo.decode(xhr.responseText);
27206         
27207         if(!response.success){
27208             this.fireEvent('exception', this, xhr);
27209             return;
27210         }
27211         
27212         var response = Roo.decode(xhr.responseText);
27213         
27214         this.fireEvent('upload', this, response);
27215         
27216     },
27217     
27218     xhrOnError : function()
27219     {
27220         if(this.loadMask){
27221             this.maskEl.unmask();
27222         }
27223         
27224         Roo.log('xhr on error');
27225         
27226         var response = Roo.decode(xhr.responseText);
27227           
27228         Roo.log(response);
27229         
27230     },
27231     
27232     prepare : function(file)
27233     {   
27234         if(this.loadMask){
27235             this.maskEl.mask(this.loadingText);
27236         }
27237         
27238         this.file = false;
27239         this.exif = {};
27240         
27241         if(typeof(file) === 'string'){
27242             this.loadCanvas(file);
27243             return;
27244         }
27245         
27246         if(!file || !this.urlAPI){
27247             return;
27248         }
27249         
27250         this.file = file;
27251         this.cropType = file.type;
27252         
27253         var _this = this;
27254         
27255         if(this.fireEvent('prepare', this, this.file) != false){
27256             
27257             var reader = new FileReader();
27258             
27259             reader.onload = function (e) {
27260                 if (e.target.error) {
27261                     Roo.log(e.target.error);
27262                     return;
27263                 }
27264                 
27265                 var buffer = e.target.result,
27266                     dataView = new DataView(buffer),
27267                     offset = 2,
27268                     maxOffset = dataView.byteLength - 4,
27269                     markerBytes,
27270                     markerLength;
27271                 
27272                 if (dataView.getUint16(0) === 0xffd8) {
27273                     while (offset < maxOffset) {
27274                         markerBytes = dataView.getUint16(offset);
27275                         
27276                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27277                             markerLength = dataView.getUint16(offset + 2) + 2;
27278                             if (offset + markerLength > dataView.byteLength) {
27279                                 Roo.log('Invalid meta data: Invalid segment size.');
27280                                 break;
27281                             }
27282                             
27283                             if(markerBytes == 0xffe1){
27284                                 _this.parseExifData(
27285                                     dataView,
27286                                     offset,
27287                                     markerLength
27288                                 );
27289                             }
27290                             
27291                             offset += markerLength;
27292                             
27293                             continue;
27294                         }
27295                         
27296                         break;
27297                     }
27298                     
27299                 }
27300                 
27301                 var url = _this.urlAPI.createObjectURL(_this.file);
27302                 
27303                 _this.loadCanvas(url);
27304                 
27305                 return;
27306             }
27307             
27308             reader.readAsArrayBuffer(this.file);
27309             
27310         }
27311         
27312     },
27313     
27314     parseExifData : function(dataView, offset, length)
27315     {
27316         var tiffOffset = offset + 10,
27317             littleEndian,
27318             dirOffset;
27319     
27320         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27321             // No Exif data, might be XMP data instead
27322             return;
27323         }
27324         
27325         // Check for the ASCII code for "Exif" (0x45786966):
27326         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27327             // No Exif data, might be XMP data instead
27328             return;
27329         }
27330         if (tiffOffset + 8 > dataView.byteLength) {
27331             Roo.log('Invalid Exif data: Invalid segment size.');
27332             return;
27333         }
27334         // Check for the two null bytes:
27335         if (dataView.getUint16(offset + 8) !== 0x0000) {
27336             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27337             return;
27338         }
27339         // Check the byte alignment:
27340         switch (dataView.getUint16(tiffOffset)) {
27341         case 0x4949:
27342             littleEndian = true;
27343             break;
27344         case 0x4D4D:
27345             littleEndian = false;
27346             break;
27347         default:
27348             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27349             return;
27350         }
27351         // Check for the TIFF tag marker (0x002A):
27352         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27353             Roo.log('Invalid Exif data: Missing TIFF marker.');
27354             return;
27355         }
27356         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27357         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27358         
27359         this.parseExifTags(
27360             dataView,
27361             tiffOffset,
27362             tiffOffset + dirOffset,
27363             littleEndian
27364         );
27365     },
27366     
27367     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27368     {
27369         var tagsNumber,
27370             dirEndOffset,
27371             i;
27372         if (dirOffset + 6 > dataView.byteLength) {
27373             Roo.log('Invalid Exif data: Invalid directory offset.');
27374             return;
27375         }
27376         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27377         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27378         if (dirEndOffset + 4 > dataView.byteLength) {
27379             Roo.log('Invalid Exif data: Invalid directory size.');
27380             return;
27381         }
27382         for (i = 0; i < tagsNumber; i += 1) {
27383             this.parseExifTag(
27384                 dataView,
27385                 tiffOffset,
27386                 dirOffset + 2 + 12 * i, // tag offset
27387                 littleEndian
27388             );
27389         }
27390         // Return the offset to the next directory:
27391         return dataView.getUint32(dirEndOffset, littleEndian);
27392     },
27393     
27394     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27395     {
27396         var tag = dataView.getUint16(offset, littleEndian);
27397         
27398         this.exif[tag] = this.getExifValue(
27399             dataView,
27400             tiffOffset,
27401             offset,
27402             dataView.getUint16(offset + 2, littleEndian), // tag type
27403             dataView.getUint32(offset + 4, littleEndian), // tag length
27404             littleEndian
27405         );
27406     },
27407     
27408     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27409     {
27410         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27411             tagSize,
27412             dataOffset,
27413             values,
27414             i,
27415             str,
27416             c;
27417     
27418         if (!tagType) {
27419             Roo.log('Invalid Exif data: Invalid tag type.');
27420             return;
27421         }
27422         
27423         tagSize = tagType.size * length;
27424         // Determine if the value is contained in the dataOffset bytes,
27425         // or if the value at the dataOffset is a pointer to the actual data:
27426         dataOffset = tagSize > 4 ?
27427                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27428         if (dataOffset + tagSize > dataView.byteLength) {
27429             Roo.log('Invalid Exif data: Invalid data offset.');
27430             return;
27431         }
27432         if (length === 1) {
27433             return tagType.getValue(dataView, dataOffset, littleEndian);
27434         }
27435         values = [];
27436         for (i = 0; i < length; i += 1) {
27437             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27438         }
27439         
27440         if (tagType.ascii) {
27441             str = '';
27442             // Concatenate the chars:
27443             for (i = 0; i < values.length; i += 1) {
27444                 c = values[i];
27445                 // Ignore the terminating NULL byte(s):
27446                 if (c === '\u0000') {
27447                     break;
27448                 }
27449                 str += c;
27450             }
27451             return str;
27452         }
27453         return values;
27454     }
27455     
27456 });
27457
27458 Roo.apply(Roo.bootstrap.UploadCropbox, {
27459     tags : {
27460         'Orientation': 0x0112
27461     },
27462     
27463     Orientation: {
27464             1: 0, //'top-left',
27465 //            2: 'top-right',
27466             3: 180, //'bottom-right',
27467 //            4: 'bottom-left',
27468 //            5: 'left-top',
27469             6: 90, //'right-top',
27470 //            7: 'right-bottom',
27471             8: 270 //'left-bottom'
27472     },
27473     
27474     exifTagTypes : {
27475         // byte, 8-bit unsigned int:
27476         1: {
27477             getValue: function (dataView, dataOffset) {
27478                 return dataView.getUint8(dataOffset);
27479             },
27480             size: 1
27481         },
27482         // ascii, 8-bit byte:
27483         2: {
27484             getValue: function (dataView, dataOffset) {
27485                 return String.fromCharCode(dataView.getUint8(dataOffset));
27486             },
27487             size: 1,
27488             ascii: true
27489         },
27490         // short, 16 bit int:
27491         3: {
27492             getValue: function (dataView, dataOffset, littleEndian) {
27493                 return dataView.getUint16(dataOffset, littleEndian);
27494             },
27495             size: 2
27496         },
27497         // long, 32 bit int:
27498         4: {
27499             getValue: function (dataView, dataOffset, littleEndian) {
27500                 return dataView.getUint32(dataOffset, littleEndian);
27501             },
27502             size: 4
27503         },
27504         // rational = two long values, first is numerator, second is denominator:
27505         5: {
27506             getValue: function (dataView, dataOffset, littleEndian) {
27507                 return dataView.getUint32(dataOffset, littleEndian) /
27508                     dataView.getUint32(dataOffset + 4, littleEndian);
27509             },
27510             size: 8
27511         },
27512         // slong, 32 bit signed int:
27513         9: {
27514             getValue: function (dataView, dataOffset, littleEndian) {
27515                 return dataView.getInt32(dataOffset, littleEndian);
27516             },
27517             size: 4
27518         },
27519         // srational, two slongs, first is numerator, second is denominator:
27520         10: {
27521             getValue: function (dataView, dataOffset, littleEndian) {
27522                 return dataView.getInt32(dataOffset, littleEndian) /
27523                     dataView.getInt32(dataOffset + 4, littleEndian);
27524             },
27525             size: 8
27526         }
27527     },
27528     
27529     footer : {
27530         STANDARD : [
27531             {
27532                 tag : 'div',
27533                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27534                 action : 'rotate-left',
27535                 cn : [
27536                     {
27537                         tag : 'button',
27538                         cls : 'btn btn-default',
27539                         html : '<i class="fa fa-undo"></i>'
27540                     }
27541                 ]
27542             },
27543             {
27544                 tag : 'div',
27545                 cls : 'btn-group roo-upload-cropbox-picture',
27546                 action : 'picture',
27547                 cn : [
27548                     {
27549                         tag : 'button',
27550                         cls : 'btn btn-default',
27551                         html : '<i class="fa fa-picture-o"></i>'
27552                     }
27553                 ]
27554             },
27555             {
27556                 tag : 'div',
27557                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27558                 action : 'rotate-right',
27559                 cn : [
27560                     {
27561                         tag : 'button',
27562                         cls : 'btn btn-default',
27563                         html : '<i class="fa fa-repeat"></i>'
27564                     }
27565                 ]
27566             }
27567         ],
27568         DOCUMENT : [
27569             {
27570                 tag : 'div',
27571                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27572                 action : 'rotate-left',
27573                 cn : [
27574                     {
27575                         tag : 'button',
27576                         cls : 'btn btn-default',
27577                         html : '<i class="fa fa-undo"></i>'
27578                     }
27579                 ]
27580             },
27581             {
27582                 tag : 'div',
27583                 cls : 'btn-group roo-upload-cropbox-download',
27584                 action : 'download',
27585                 cn : [
27586                     {
27587                         tag : 'button',
27588                         cls : 'btn btn-default',
27589                         html : '<i class="fa fa-download"></i>'
27590                     }
27591                 ]
27592             },
27593             {
27594                 tag : 'div',
27595                 cls : 'btn-group roo-upload-cropbox-crop',
27596                 action : 'crop',
27597                 cn : [
27598                     {
27599                         tag : 'button',
27600                         cls : 'btn btn-default',
27601                         html : '<i class="fa fa-crop"></i>'
27602                     }
27603                 ]
27604             },
27605             {
27606                 tag : 'div',
27607                 cls : 'btn-group roo-upload-cropbox-trash',
27608                 action : 'trash',
27609                 cn : [
27610                     {
27611                         tag : 'button',
27612                         cls : 'btn btn-default',
27613                         html : '<i class="fa fa-trash"></i>'
27614                     }
27615                 ]
27616             },
27617             {
27618                 tag : 'div',
27619                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27620                 action : 'rotate-right',
27621                 cn : [
27622                     {
27623                         tag : 'button',
27624                         cls : 'btn btn-default',
27625                         html : '<i class="fa fa-repeat"></i>'
27626                     }
27627                 ]
27628             }
27629         ],
27630         ROTATOR : [
27631             {
27632                 tag : 'div',
27633                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27634                 action : 'rotate-left',
27635                 cn : [
27636                     {
27637                         tag : 'button',
27638                         cls : 'btn btn-default',
27639                         html : '<i class="fa fa-undo"></i>'
27640                     }
27641                 ]
27642             },
27643             {
27644                 tag : 'div',
27645                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27646                 action : 'rotate-right',
27647                 cn : [
27648                     {
27649                         tag : 'button',
27650                         cls : 'btn btn-default',
27651                         html : '<i class="fa fa-repeat"></i>'
27652                     }
27653                 ]
27654             }
27655         ]
27656     }
27657 });
27658
27659 /*
27660 * Licence: LGPL
27661 */
27662
27663 /**
27664  * @class Roo.bootstrap.DocumentManager
27665  * @extends Roo.bootstrap.Component
27666  * Bootstrap DocumentManager class
27667  * @cfg {String} paramName default 'imageUpload'
27668  * @cfg {String} toolTipName default 'filename'
27669  * @cfg {String} method default POST
27670  * @cfg {String} url action url
27671  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27672  * @cfg {Boolean} multiple multiple upload default true
27673  * @cfg {Number} thumbSize default 300
27674  * @cfg {String} fieldLabel
27675  * @cfg {Number} labelWidth default 4
27676  * @cfg {String} labelAlign (left|top) default left
27677  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27678 * @cfg {Number} labellg set the width of label (1-12)
27679  * @cfg {Number} labelmd set the width of label (1-12)
27680  * @cfg {Number} labelsm set the width of label (1-12)
27681  * @cfg {Number} labelxs set the width of label (1-12)
27682  * 
27683  * @constructor
27684  * Create a new DocumentManager
27685  * @param {Object} config The config object
27686  */
27687
27688 Roo.bootstrap.DocumentManager = function(config){
27689     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27690     
27691     this.files = [];
27692     this.delegates = [];
27693     
27694     this.addEvents({
27695         /**
27696          * @event initial
27697          * Fire when initial the DocumentManager
27698          * @param {Roo.bootstrap.DocumentManager} this
27699          */
27700         "initial" : true,
27701         /**
27702          * @event inspect
27703          * inspect selected file
27704          * @param {Roo.bootstrap.DocumentManager} this
27705          * @param {File} file
27706          */
27707         "inspect" : true,
27708         /**
27709          * @event exception
27710          * Fire when xhr load exception
27711          * @param {Roo.bootstrap.DocumentManager} this
27712          * @param {XMLHttpRequest} xhr
27713          */
27714         "exception" : true,
27715         /**
27716          * @event afterupload
27717          * Fire when xhr load exception
27718          * @param {Roo.bootstrap.DocumentManager} this
27719          * @param {XMLHttpRequest} xhr
27720          */
27721         "afterupload" : true,
27722         /**
27723          * @event prepare
27724          * prepare the form data
27725          * @param {Roo.bootstrap.DocumentManager} this
27726          * @param {Object} formData
27727          */
27728         "prepare" : true,
27729         /**
27730          * @event remove
27731          * Fire when remove the file
27732          * @param {Roo.bootstrap.DocumentManager} this
27733          * @param {Object} file
27734          */
27735         "remove" : true,
27736         /**
27737          * @event refresh
27738          * Fire after refresh the file
27739          * @param {Roo.bootstrap.DocumentManager} this
27740          */
27741         "refresh" : true,
27742         /**
27743          * @event click
27744          * Fire after click the image
27745          * @param {Roo.bootstrap.DocumentManager} this
27746          * @param {Object} file
27747          */
27748         "click" : true,
27749         /**
27750          * @event edit
27751          * Fire when upload a image and editable set to true
27752          * @param {Roo.bootstrap.DocumentManager} this
27753          * @param {Object} file
27754          */
27755         "edit" : true,
27756         /**
27757          * @event beforeselectfile
27758          * Fire before select file
27759          * @param {Roo.bootstrap.DocumentManager} this
27760          */
27761         "beforeselectfile" : true,
27762         /**
27763          * @event process
27764          * Fire before process file
27765          * @param {Roo.bootstrap.DocumentManager} this
27766          * @param {Object} file
27767          */
27768         "process" : true
27769         
27770     });
27771 };
27772
27773 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27774     
27775     boxes : 0,
27776     inputName : '',
27777     thumbSize : 300,
27778     multiple : true,
27779     files : false,
27780     method : 'POST',
27781     url : '',
27782     paramName : 'imageUpload',
27783     toolTipName : 'filename',
27784     fieldLabel : '',
27785     labelWidth : 4,
27786     labelAlign : 'left',
27787     editable : true,
27788     delegates : false,
27789     xhr : false, 
27790     
27791     labellg : 0,
27792     labelmd : 0,
27793     labelsm : 0,
27794     labelxs : 0,
27795     
27796     getAutoCreate : function()
27797     {   
27798         var managerWidget = {
27799             tag : 'div',
27800             cls : 'roo-document-manager',
27801             cn : [
27802                 {
27803                     tag : 'input',
27804                     cls : 'roo-document-manager-selector',
27805                     type : 'file'
27806                 },
27807                 {
27808                     tag : 'div',
27809                     cls : 'roo-document-manager-uploader',
27810                     cn : [
27811                         {
27812                             tag : 'div',
27813                             cls : 'roo-document-manager-upload-btn',
27814                             html : '<i class="fa fa-plus"></i>'
27815                         }
27816                     ]
27817                     
27818                 }
27819             ]
27820         };
27821         
27822         var content = [
27823             {
27824                 tag : 'div',
27825                 cls : 'column col-md-12',
27826                 cn : managerWidget
27827             }
27828         ];
27829         
27830         if(this.fieldLabel.length){
27831             
27832             content = [
27833                 {
27834                     tag : 'div',
27835                     cls : 'column col-md-12',
27836                     html : this.fieldLabel
27837                 },
27838                 {
27839                     tag : 'div',
27840                     cls : 'column col-md-12',
27841                     cn : managerWidget
27842                 }
27843             ];
27844
27845             if(this.labelAlign == 'left'){
27846                 content = [
27847                     {
27848                         tag : 'div',
27849                         cls : 'column',
27850                         html : this.fieldLabel
27851                     },
27852                     {
27853                         tag : 'div',
27854                         cls : 'column',
27855                         cn : managerWidget
27856                     }
27857                 ];
27858                 
27859                 if(this.labelWidth > 12){
27860                     content[0].style = "width: " + this.labelWidth + 'px';
27861                 }
27862
27863                 if(this.labelWidth < 13 && this.labelmd == 0){
27864                     this.labelmd = this.labelWidth;
27865                 }
27866
27867                 if(this.labellg > 0){
27868                     content[0].cls += ' col-lg-' + this.labellg;
27869                     content[1].cls += ' col-lg-' + (12 - this.labellg);
27870                 }
27871
27872                 if(this.labelmd > 0){
27873                     content[0].cls += ' col-md-' + this.labelmd;
27874                     content[1].cls += ' col-md-' + (12 - this.labelmd);
27875                 }
27876
27877                 if(this.labelsm > 0){
27878                     content[0].cls += ' col-sm-' + this.labelsm;
27879                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
27880                 }
27881
27882                 if(this.labelxs > 0){
27883                     content[0].cls += ' col-xs-' + this.labelxs;
27884                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
27885                 }
27886                 
27887             }
27888         }
27889         
27890         var cfg = {
27891             tag : 'div',
27892             cls : 'row clearfix',
27893             cn : content
27894         };
27895         
27896         return cfg;
27897         
27898     },
27899     
27900     initEvents : function()
27901     {
27902         this.managerEl = this.el.select('.roo-document-manager', true).first();
27903         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27904         
27905         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27906         this.selectorEl.hide();
27907         
27908         if(this.multiple){
27909             this.selectorEl.attr('multiple', 'multiple');
27910         }
27911         
27912         this.selectorEl.on('change', this.onFileSelected, this);
27913         
27914         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27915         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27916         
27917         this.uploader.on('click', this.onUploaderClick, this);
27918         
27919         this.renderProgressDialog();
27920         
27921         var _this = this;
27922         
27923         window.addEventListener("resize", function() { _this.refresh(); } );
27924         
27925         this.fireEvent('initial', this);
27926     },
27927     
27928     renderProgressDialog : function()
27929     {
27930         var _this = this;
27931         
27932         this.progressDialog = new Roo.bootstrap.Modal({
27933             cls : 'roo-document-manager-progress-dialog',
27934             allow_close : false,
27935             title : '',
27936             buttons : [
27937                 {
27938                     name  :'cancel',
27939                     weight : 'danger',
27940                     html : 'Cancel'
27941                 }
27942             ], 
27943             listeners : { 
27944                 btnclick : function() {
27945                     _this.uploadCancel();
27946                     this.hide();
27947                 }
27948             }
27949         });
27950          
27951         this.progressDialog.render(Roo.get(document.body));
27952          
27953         this.progress = new Roo.bootstrap.Progress({
27954             cls : 'roo-document-manager-progress',
27955             active : true,
27956             striped : true
27957         });
27958         
27959         this.progress.render(this.progressDialog.getChildContainer());
27960         
27961         this.progressBar = new Roo.bootstrap.ProgressBar({
27962             cls : 'roo-document-manager-progress-bar',
27963             aria_valuenow : 0,
27964             aria_valuemin : 0,
27965             aria_valuemax : 12,
27966             panel : 'success'
27967         });
27968         
27969         this.progressBar.render(this.progress.getChildContainer());
27970     },
27971     
27972     onUploaderClick : function(e)
27973     {
27974         e.preventDefault();
27975      
27976         if(this.fireEvent('beforeselectfile', this) != false){
27977             this.selectorEl.dom.click();
27978         }
27979         
27980     },
27981     
27982     onFileSelected : function(e)
27983     {
27984         e.preventDefault();
27985         
27986         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27987             return;
27988         }
27989         
27990         Roo.each(this.selectorEl.dom.files, function(file){
27991             if(this.fireEvent('inspect', this, file) != false){
27992                 this.files.push(file);
27993             }
27994         }, this);
27995         
27996         this.queue();
27997         
27998     },
27999     
28000     queue : function()
28001     {
28002         this.selectorEl.dom.value = '';
28003         
28004         if(!this.files.length){
28005             return;
28006         }
28007         
28008         if(this.boxes > 0 && this.files.length > this.boxes){
28009             this.files = this.files.slice(0, this.boxes);
28010         }
28011         
28012         this.uploader.show();
28013         
28014         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28015             this.uploader.hide();
28016         }
28017         
28018         var _this = this;
28019         
28020         var files = [];
28021         
28022         var docs = [];
28023         
28024         Roo.each(this.files, function(file){
28025             
28026             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28027                 var f = this.renderPreview(file);
28028                 files.push(f);
28029                 return;
28030             }
28031             
28032             if(file.type.indexOf('image') != -1){
28033                 this.delegates.push(
28034                     (function(){
28035                         _this.process(file);
28036                     }).createDelegate(this)
28037                 );
28038         
28039                 return;
28040             }
28041             
28042             docs.push(
28043                 (function(){
28044                     _this.process(file);
28045                 }).createDelegate(this)
28046             );
28047             
28048         }, this);
28049         
28050         this.files = files;
28051         
28052         this.delegates = this.delegates.concat(docs);
28053         
28054         if(!this.delegates.length){
28055             this.refresh();
28056             return;
28057         }
28058         
28059         this.progressBar.aria_valuemax = this.delegates.length;
28060         
28061         this.arrange();
28062         
28063         return;
28064     },
28065     
28066     arrange : function()
28067     {
28068         if(!this.delegates.length){
28069             this.progressDialog.hide();
28070             this.refresh();
28071             return;
28072         }
28073         
28074         var delegate = this.delegates.shift();
28075         
28076         this.progressDialog.show();
28077         
28078         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28079         
28080         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28081         
28082         delegate();
28083     },
28084     
28085     refresh : function()
28086     {
28087         this.uploader.show();
28088         
28089         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28090             this.uploader.hide();
28091         }
28092         
28093         Roo.isTouch ? this.closable(false) : this.closable(true);
28094         
28095         this.fireEvent('refresh', this);
28096     },
28097     
28098     onRemove : function(e, el, o)
28099     {
28100         e.preventDefault();
28101         
28102         this.fireEvent('remove', this, o);
28103         
28104     },
28105     
28106     remove : function(o)
28107     {
28108         var files = [];
28109         
28110         Roo.each(this.files, function(file){
28111             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28112                 files.push(file);
28113                 return;
28114             }
28115
28116             o.target.remove();
28117
28118         }, this);
28119         
28120         this.files = files;
28121         
28122         this.refresh();
28123     },
28124     
28125     clear : function()
28126     {
28127         Roo.each(this.files, function(file){
28128             if(!file.target){
28129                 return;
28130             }
28131             
28132             file.target.remove();
28133
28134         }, this);
28135         
28136         this.files = [];
28137         
28138         this.refresh();
28139     },
28140     
28141     onClick : function(e, el, o)
28142     {
28143         e.preventDefault();
28144         
28145         this.fireEvent('click', this, o);
28146         
28147     },
28148     
28149     closable : function(closable)
28150     {
28151         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28152             
28153             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28154             
28155             if(closable){
28156                 el.show();
28157                 return;
28158             }
28159             
28160             el.hide();
28161             
28162         }, this);
28163     },
28164     
28165     xhrOnLoad : function(xhr)
28166     {
28167         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28168             el.remove();
28169         }, this);
28170         
28171         if (xhr.readyState !== 4) {
28172             this.arrange();
28173             this.fireEvent('exception', this, xhr);
28174             return;
28175         }
28176
28177         var response = Roo.decode(xhr.responseText);
28178         
28179         if(!response.success){
28180             this.arrange();
28181             this.fireEvent('exception', this, xhr);
28182             return;
28183         }
28184         
28185         var file = this.renderPreview(response.data);
28186         
28187         this.files.push(file);
28188         
28189         this.arrange();
28190         
28191         this.fireEvent('afterupload', this, xhr);
28192         
28193     },
28194     
28195     xhrOnError : function(xhr)
28196     {
28197         Roo.log('xhr on error');
28198         
28199         var response = Roo.decode(xhr.responseText);
28200           
28201         Roo.log(response);
28202         
28203         this.arrange();
28204     },
28205     
28206     process : function(file)
28207     {
28208         if(this.fireEvent('process', this, file) !== false){
28209             if(this.editable && file.type.indexOf('image') != -1){
28210                 this.fireEvent('edit', this, file);
28211                 return;
28212             }
28213
28214             this.uploadStart(file, false);
28215
28216             return;
28217         }
28218         
28219     },
28220     
28221     uploadStart : function(file, crop)
28222     {
28223         this.xhr = new XMLHttpRequest();
28224         
28225         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28226             this.arrange();
28227             return;
28228         }
28229         
28230         file.xhr = this.xhr;
28231             
28232         this.managerEl.createChild({
28233             tag : 'div',
28234             cls : 'roo-document-manager-loading',
28235             cn : [
28236                 {
28237                     tag : 'div',
28238                     tooltip : file.name,
28239                     cls : 'roo-document-manager-thumb',
28240                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28241                 }
28242             ]
28243
28244         });
28245
28246         this.xhr.open(this.method, this.url, true);
28247         
28248         var headers = {
28249             "Accept": "application/json",
28250             "Cache-Control": "no-cache",
28251             "X-Requested-With": "XMLHttpRequest"
28252         };
28253         
28254         for (var headerName in headers) {
28255             var headerValue = headers[headerName];
28256             if (headerValue) {
28257                 this.xhr.setRequestHeader(headerName, headerValue);
28258             }
28259         }
28260         
28261         var _this = this;
28262         
28263         this.xhr.onload = function()
28264         {
28265             _this.xhrOnLoad(_this.xhr);
28266         }
28267         
28268         this.xhr.onerror = function()
28269         {
28270             _this.xhrOnError(_this.xhr);
28271         }
28272         
28273         var formData = new FormData();
28274
28275         formData.append('returnHTML', 'NO');
28276         
28277         if(crop){
28278             formData.append('crop', crop);
28279         }
28280         
28281         formData.append(this.paramName, file, file.name);
28282         
28283         var options = {
28284             file : file, 
28285             manually : false
28286         };
28287         
28288         if(this.fireEvent('prepare', this, formData, options) != false){
28289             
28290             if(options.manually){
28291                 return;
28292             }
28293             
28294             this.xhr.send(formData);
28295             return;
28296         };
28297         
28298         this.uploadCancel();
28299     },
28300     
28301     uploadCancel : function()
28302     {
28303         if (this.xhr) {
28304             this.xhr.abort();
28305         }
28306         
28307         this.delegates = [];
28308         
28309         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28310             el.remove();
28311         }, this);
28312         
28313         this.arrange();
28314     },
28315     
28316     renderPreview : function(file)
28317     {
28318         if(typeof(file.target) != 'undefined' && file.target){
28319             return file;
28320         }
28321         
28322         var previewEl = this.managerEl.createChild({
28323             tag : 'div',
28324             cls : 'roo-document-manager-preview',
28325             cn : [
28326                 {
28327                     tag : 'div',
28328                     tooltip : file[this.toolTipName],
28329                     cls : 'roo-document-manager-thumb',
28330                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28331                 },
28332                 {
28333                     tag : 'button',
28334                     cls : 'close',
28335                     html : '<i class="fa fa-times-circle"></i>'
28336                 }
28337             ]
28338         });
28339
28340         var close = previewEl.select('button.close', true).first();
28341
28342         close.on('click', this.onRemove, this, file);
28343
28344         file.target = previewEl;
28345
28346         var image = previewEl.select('img', true).first();
28347         
28348         var _this = this;
28349         
28350         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28351         
28352         image.on('click', this.onClick, this, file);
28353         
28354         return file;
28355         
28356     },
28357     
28358     onPreviewLoad : function(file, image)
28359     {
28360         if(typeof(file.target) == 'undefined' || !file.target){
28361             return;
28362         }
28363         
28364         var width = image.dom.naturalWidth || image.dom.width;
28365         var height = image.dom.naturalHeight || image.dom.height;
28366         
28367         if(width > height){
28368             file.target.addClass('wide');
28369             return;
28370         }
28371         
28372         file.target.addClass('tall');
28373         return;
28374         
28375     },
28376     
28377     uploadFromSource : function(file, crop)
28378     {
28379         this.xhr = new XMLHttpRequest();
28380         
28381         this.managerEl.createChild({
28382             tag : 'div',
28383             cls : 'roo-document-manager-loading',
28384             cn : [
28385                 {
28386                     tag : 'div',
28387                     tooltip : file.name,
28388                     cls : 'roo-document-manager-thumb',
28389                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28390                 }
28391             ]
28392
28393         });
28394
28395         this.xhr.open(this.method, this.url, true);
28396         
28397         var headers = {
28398             "Accept": "application/json",
28399             "Cache-Control": "no-cache",
28400             "X-Requested-With": "XMLHttpRequest"
28401         };
28402         
28403         for (var headerName in headers) {
28404             var headerValue = headers[headerName];
28405             if (headerValue) {
28406                 this.xhr.setRequestHeader(headerName, headerValue);
28407             }
28408         }
28409         
28410         var _this = this;
28411         
28412         this.xhr.onload = function()
28413         {
28414             _this.xhrOnLoad(_this.xhr);
28415         }
28416         
28417         this.xhr.onerror = function()
28418         {
28419             _this.xhrOnError(_this.xhr);
28420         }
28421         
28422         var formData = new FormData();
28423
28424         formData.append('returnHTML', 'NO');
28425         
28426         formData.append('crop', crop);
28427         
28428         if(typeof(file.filename) != 'undefined'){
28429             formData.append('filename', file.filename);
28430         }
28431         
28432         if(typeof(file.mimetype) != 'undefined'){
28433             formData.append('mimetype', file.mimetype);
28434         }
28435         
28436         if(this.fireEvent('prepare', this, formData) != false){
28437             this.xhr.send(formData);
28438         };
28439     }
28440 });
28441
28442 /*
28443 * Licence: LGPL
28444 */
28445
28446 /**
28447  * @class Roo.bootstrap.DocumentViewer
28448  * @extends Roo.bootstrap.Component
28449  * Bootstrap DocumentViewer class
28450  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28451  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28452  * 
28453  * @constructor
28454  * Create a new DocumentViewer
28455  * @param {Object} config The config object
28456  */
28457
28458 Roo.bootstrap.DocumentViewer = function(config){
28459     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28460     
28461     this.addEvents({
28462         /**
28463          * @event initial
28464          * Fire after initEvent
28465          * @param {Roo.bootstrap.DocumentViewer} this
28466          */
28467         "initial" : true,
28468         /**
28469          * @event click
28470          * Fire after click
28471          * @param {Roo.bootstrap.DocumentViewer} this
28472          */
28473         "click" : true,
28474         /**
28475          * @event download
28476          * Fire after download button
28477          * @param {Roo.bootstrap.DocumentViewer} this
28478          */
28479         "download" : true,
28480         /**
28481          * @event trash
28482          * Fire after trash button
28483          * @param {Roo.bootstrap.DocumentViewer} this
28484          */
28485         "trash" : true
28486         
28487     });
28488 };
28489
28490 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28491     
28492     showDownload : true,
28493     
28494     showTrash : true,
28495     
28496     getAutoCreate : function()
28497     {
28498         var cfg = {
28499             tag : 'div',
28500             cls : 'roo-document-viewer',
28501             cn : [
28502                 {
28503                     tag : 'div',
28504                     cls : 'roo-document-viewer-body',
28505                     cn : [
28506                         {
28507                             tag : 'div',
28508                             cls : 'roo-document-viewer-thumb',
28509                             cn : [
28510                                 {
28511                                     tag : 'img',
28512                                     cls : 'roo-document-viewer-image'
28513                                 }
28514                             ]
28515                         }
28516                     ]
28517                 },
28518                 {
28519                     tag : 'div',
28520                     cls : 'roo-document-viewer-footer',
28521                     cn : {
28522                         tag : 'div',
28523                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28524                         cn : [
28525                             {
28526                                 tag : 'div',
28527                                 cls : 'btn-group roo-document-viewer-download',
28528                                 cn : [
28529                                     {
28530                                         tag : 'button',
28531                                         cls : 'btn btn-default',
28532                                         html : '<i class="fa fa-download"></i>'
28533                                     }
28534                                 ]
28535                             },
28536                             {
28537                                 tag : 'div',
28538                                 cls : 'btn-group roo-document-viewer-trash',
28539                                 cn : [
28540                                     {
28541                                         tag : 'button',
28542                                         cls : 'btn btn-default',
28543                                         html : '<i class="fa fa-trash"></i>'
28544                                     }
28545                                 ]
28546                             }
28547                         ]
28548                     }
28549                 }
28550             ]
28551         };
28552         
28553         return cfg;
28554     },
28555     
28556     initEvents : function()
28557     {
28558         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28559         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28560         
28561         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28562         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28563         
28564         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28565         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28566         
28567         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28568         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28569         
28570         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28571         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28572         
28573         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28574         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28575         
28576         this.bodyEl.on('click', this.onClick, this);
28577         this.downloadBtn.on('click', this.onDownload, this);
28578         this.trashBtn.on('click', this.onTrash, this);
28579         
28580         this.downloadBtn.hide();
28581         this.trashBtn.hide();
28582         
28583         if(this.showDownload){
28584             this.downloadBtn.show();
28585         }
28586         
28587         if(this.showTrash){
28588             this.trashBtn.show();
28589         }
28590         
28591         if(!this.showDownload && !this.showTrash) {
28592             this.footerEl.hide();
28593         }
28594         
28595     },
28596     
28597     initial : function()
28598     {
28599         this.fireEvent('initial', this);
28600         
28601     },
28602     
28603     onClick : function(e)
28604     {
28605         e.preventDefault();
28606         
28607         this.fireEvent('click', this);
28608     },
28609     
28610     onDownload : function(e)
28611     {
28612         e.preventDefault();
28613         
28614         this.fireEvent('download', this);
28615     },
28616     
28617     onTrash : function(e)
28618     {
28619         e.preventDefault();
28620         
28621         this.fireEvent('trash', this);
28622     }
28623     
28624 });
28625 /*
28626  * - LGPL
28627  *
28628  * nav progress bar
28629  * 
28630  */
28631
28632 /**
28633  * @class Roo.bootstrap.NavProgressBar
28634  * @extends Roo.bootstrap.Component
28635  * Bootstrap NavProgressBar class
28636  * 
28637  * @constructor
28638  * Create a new nav progress bar
28639  * @param {Object} config The config object
28640  */
28641
28642 Roo.bootstrap.NavProgressBar = function(config){
28643     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28644
28645     this.bullets = this.bullets || [];
28646    
28647 //    Roo.bootstrap.NavProgressBar.register(this);
28648      this.addEvents({
28649         /**
28650              * @event changed
28651              * Fires when the active item changes
28652              * @param {Roo.bootstrap.NavProgressBar} this
28653              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28654              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28655          */
28656         'changed': true
28657      });
28658     
28659 };
28660
28661 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28662     
28663     bullets : [],
28664     barItems : [],
28665     
28666     getAutoCreate : function()
28667     {
28668         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28669         
28670         cfg = {
28671             tag : 'div',
28672             cls : 'roo-navigation-bar-group',
28673             cn : [
28674                 {
28675                     tag : 'div',
28676                     cls : 'roo-navigation-top-bar'
28677                 },
28678                 {
28679                     tag : 'div',
28680                     cls : 'roo-navigation-bullets-bar',
28681                     cn : [
28682                         {
28683                             tag : 'ul',
28684                             cls : 'roo-navigation-bar'
28685                         }
28686                     ]
28687                 },
28688                 
28689                 {
28690                     tag : 'div',
28691                     cls : 'roo-navigation-bottom-bar'
28692                 }
28693             ]
28694             
28695         };
28696         
28697         return cfg;
28698         
28699     },
28700     
28701     initEvents: function() 
28702     {
28703         
28704     },
28705     
28706     onRender : function(ct, position) 
28707     {
28708         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28709         
28710         if(this.bullets.length){
28711             Roo.each(this.bullets, function(b){
28712                this.addItem(b);
28713             }, this);
28714         }
28715         
28716         this.format();
28717         
28718     },
28719     
28720     addItem : function(cfg)
28721     {
28722         var item = new Roo.bootstrap.NavProgressItem(cfg);
28723         
28724         item.parentId = this.id;
28725         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28726         
28727         if(cfg.html){
28728             var top = new Roo.bootstrap.Element({
28729                 tag : 'div',
28730                 cls : 'roo-navigation-bar-text'
28731             });
28732             
28733             var bottom = new Roo.bootstrap.Element({
28734                 tag : 'div',
28735                 cls : 'roo-navigation-bar-text'
28736             });
28737             
28738             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28739             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28740             
28741             var topText = new Roo.bootstrap.Element({
28742                 tag : 'span',
28743                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28744             });
28745             
28746             var bottomText = new Roo.bootstrap.Element({
28747                 tag : 'span',
28748                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28749             });
28750             
28751             topText.onRender(top.el, null);
28752             bottomText.onRender(bottom.el, null);
28753             
28754             item.topEl = top;
28755             item.bottomEl = bottom;
28756         }
28757         
28758         this.barItems.push(item);
28759         
28760         return item;
28761     },
28762     
28763     getActive : function()
28764     {
28765         var active = false;
28766         
28767         Roo.each(this.barItems, function(v){
28768             
28769             if (!v.isActive()) {
28770                 return;
28771             }
28772             
28773             active = v;
28774             return false;
28775             
28776         });
28777         
28778         return active;
28779     },
28780     
28781     setActiveItem : function(item)
28782     {
28783         var prev = false;
28784         
28785         Roo.each(this.barItems, function(v){
28786             if (v.rid == item.rid) {
28787                 return ;
28788             }
28789             
28790             if (v.isActive()) {
28791                 v.setActive(false);
28792                 prev = v;
28793             }
28794         });
28795
28796         item.setActive(true);
28797         
28798         this.fireEvent('changed', this, item, prev);
28799     },
28800     
28801     getBarItem: function(rid)
28802     {
28803         var ret = false;
28804         
28805         Roo.each(this.barItems, function(e) {
28806             if (e.rid != rid) {
28807                 return;
28808             }
28809             
28810             ret =  e;
28811             return false;
28812         });
28813         
28814         return ret;
28815     },
28816     
28817     indexOfItem : function(item)
28818     {
28819         var index = false;
28820         
28821         Roo.each(this.barItems, function(v, i){
28822             
28823             if (v.rid != item.rid) {
28824                 return;
28825             }
28826             
28827             index = i;
28828             return false
28829         });
28830         
28831         return index;
28832     },
28833     
28834     setActiveNext : function()
28835     {
28836         var i = this.indexOfItem(this.getActive());
28837         
28838         if (i > this.barItems.length) {
28839             return;
28840         }
28841         
28842         this.setActiveItem(this.barItems[i+1]);
28843     },
28844     
28845     setActivePrev : function()
28846     {
28847         var i = this.indexOfItem(this.getActive());
28848         
28849         if (i  < 1) {
28850             return;
28851         }
28852         
28853         this.setActiveItem(this.barItems[i-1]);
28854     },
28855     
28856     format : function()
28857     {
28858         if(!this.barItems.length){
28859             return;
28860         }
28861      
28862         var width = 100 / this.barItems.length;
28863         
28864         Roo.each(this.barItems, function(i){
28865             i.el.setStyle('width', width + '%');
28866             i.topEl.el.setStyle('width', width + '%');
28867             i.bottomEl.el.setStyle('width', width + '%');
28868         }, this);
28869         
28870     }
28871     
28872 });
28873 /*
28874  * - LGPL
28875  *
28876  * Nav Progress Item
28877  * 
28878  */
28879
28880 /**
28881  * @class Roo.bootstrap.NavProgressItem
28882  * @extends Roo.bootstrap.Component
28883  * Bootstrap NavProgressItem class
28884  * @cfg {String} rid the reference id
28885  * @cfg {Boolean} active (true|false) Is item active default false
28886  * @cfg {Boolean} disabled (true|false) Is item active default false
28887  * @cfg {String} html
28888  * @cfg {String} position (top|bottom) text position default bottom
28889  * @cfg {String} icon show icon instead of number
28890  * 
28891  * @constructor
28892  * Create a new NavProgressItem
28893  * @param {Object} config The config object
28894  */
28895 Roo.bootstrap.NavProgressItem = function(config){
28896     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28897     this.addEvents({
28898         // raw events
28899         /**
28900          * @event click
28901          * The raw click event for the entire grid.
28902          * @param {Roo.bootstrap.NavProgressItem} this
28903          * @param {Roo.EventObject} e
28904          */
28905         "click" : true
28906     });
28907    
28908 };
28909
28910 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28911     
28912     rid : '',
28913     active : false,
28914     disabled : false,
28915     html : '',
28916     position : 'bottom',
28917     icon : false,
28918     
28919     getAutoCreate : function()
28920     {
28921         var iconCls = 'roo-navigation-bar-item-icon';
28922         
28923         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28924         
28925         var cfg = {
28926             tag: 'li',
28927             cls: 'roo-navigation-bar-item',
28928             cn : [
28929                 {
28930                     tag : 'i',
28931                     cls : iconCls
28932                 }
28933             ]
28934         };
28935         
28936         if(this.active){
28937             cfg.cls += ' active';
28938         }
28939         if(this.disabled){
28940             cfg.cls += ' disabled';
28941         }
28942         
28943         return cfg;
28944     },
28945     
28946     disable : function()
28947     {
28948         this.setDisabled(true);
28949     },
28950     
28951     enable : function()
28952     {
28953         this.setDisabled(false);
28954     },
28955     
28956     initEvents: function() 
28957     {
28958         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28959         
28960         this.iconEl.on('click', this.onClick, this);
28961     },
28962     
28963     onClick : function(e)
28964     {
28965         e.preventDefault();
28966         
28967         if(this.disabled){
28968             return;
28969         }
28970         
28971         if(this.fireEvent('click', this, e) === false){
28972             return;
28973         };
28974         
28975         this.parent().setActiveItem(this);
28976     },
28977     
28978     isActive: function () 
28979     {
28980         return this.active;
28981     },
28982     
28983     setActive : function(state)
28984     {
28985         if(this.active == state){
28986             return;
28987         }
28988         
28989         this.active = state;
28990         
28991         if (state) {
28992             this.el.addClass('active');
28993             return;
28994         }
28995         
28996         this.el.removeClass('active');
28997         
28998         return;
28999     },
29000     
29001     setDisabled : function(state)
29002     {
29003         if(this.disabled == state){
29004             return;
29005         }
29006         
29007         this.disabled = state;
29008         
29009         if (state) {
29010             this.el.addClass('disabled');
29011             return;
29012         }
29013         
29014         this.el.removeClass('disabled');
29015     },
29016     
29017     tooltipEl : function()
29018     {
29019         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29020     }
29021 });
29022  
29023
29024  /*
29025  * - LGPL
29026  *
29027  * FieldLabel
29028  * 
29029  */
29030
29031 /**
29032  * @class Roo.bootstrap.FieldLabel
29033  * @extends Roo.bootstrap.Component
29034  * Bootstrap FieldLabel class
29035  * @cfg {String} html contents of the element
29036  * @cfg {String} tag tag of the element default label
29037  * @cfg {String} cls class of the element
29038  * @cfg {String} target label target 
29039  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29040  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29041  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29042  * @cfg {String} iconTooltip default "This field is required"
29043  * 
29044  * @constructor
29045  * Create a new FieldLabel
29046  * @param {Object} config The config object
29047  */
29048
29049 Roo.bootstrap.FieldLabel = function(config){
29050     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29051     
29052     this.addEvents({
29053             /**
29054              * @event invalid
29055              * Fires after the field has been marked as invalid.
29056              * @param {Roo.form.FieldLabel} this
29057              * @param {String} msg The validation message
29058              */
29059             invalid : true,
29060             /**
29061              * @event valid
29062              * Fires after the field has been validated with no errors.
29063              * @param {Roo.form.FieldLabel} this
29064              */
29065             valid : true
29066         });
29067 };
29068
29069 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29070     
29071     tag: 'label',
29072     cls: '',
29073     html: '',
29074     target: '',
29075     allowBlank : true,
29076     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29077     validClass : 'text-success fa fa-lg fa-check',
29078     iconTooltip : 'This field is required',
29079     
29080     getAutoCreate : function(){
29081         
29082         var cfg = {
29083             tag : this.tag,
29084             cls : 'roo-bootstrap-field-label ' + this.cls,
29085             for : this.target,
29086             cn : [
29087                 {
29088                     tag : 'i',
29089                     cls : '',
29090                     tooltip : this.iconTooltip
29091                 },
29092                 {
29093                     tag : 'span',
29094                     html : this.html
29095                 }
29096             ] 
29097         };
29098         
29099         return cfg;
29100     },
29101     
29102     initEvents: function() 
29103     {
29104         Roo.bootstrap.Element.superclass.initEvents.call(this);
29105         
29106         this.iconEl = this.el.select('i', true).first();
29107         
29108         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29109         
29110         Roo.bootstrap.FieldLabel.register(this);
29111     },
29112     
29113     /**
29114      * Mark this field as valid
29115      */
29116     markValid : function()
29117     {
29118         this.iconEl.show();
29119         
29120         this.iconEl.removeClass(this.invalidClass);
29121         
29122         this.iconEl.addClass(this.validClass);
29123         
29124         this.fireEvent('valid', this);
29125     },
29126     
29127     /**
29128      * Mark this field as invalid
29129      * @param {String} msg The validation message
29130      */
29131     markInvalid : function(msg)
29132     {
29133         this.iconEl.show();
29134         
29135         this.iconEl.removeClass(this.validClass);
29136         
29137         this.iconEl.addClass(this.invalidClass);
29138         
29139         this.fireEvent('invalid', this, msg);
29140     }
29141     
29142    
29143 });
29144
29145 Roo.apply(Roo.bootstrap.FieldLabel, {
29146     
29147     groups: {},
29148     
29149      /**
29150     * register a FieldLabel Group
29151     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29152     */
29153     register : function(label)
29154     {
29155         if(this.groups.hasOwnProperty(label.target)){
29156             return;
29157         }
29158      
29159         this.groups[label.target] = label;
29160         
29161     },
29162     /**
29163     * fetch a FieldLabel Group based on the target
29164     * @param {string} target
29165     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29166     */
29167     get: function(target) {
29168         if (typeof(this.groups[target]) == 'undefined') {
29169             return false;
29170         }
29171         
29172         return this.groups[target] ;
29173     }
29174 });
29175
29176  
29177
29178  /*
29179  * - LGPL
29180  *
29181  * page DateSplitField.
29182  * 
29183  */
29184
29185
29186 /**
29187  * @class Roo.bootstrap.DateSplitField
29188  * @extends Roo.bootstrap.Component
29189  * Bootstrap DateSplitField class
29190  * @cfg {string} fieldLabel - the label associated
29191  * @cfg {Number} labelWidth set the width of label (0-12)
29192  * @cfg {String} labelAlign (top|left)
29193  * @cfg {Boolean} dayAllowBlank (true|false) default false
29194  * @cfg {Boolean} monthAllowBlank (true|false) default false
29195  * @cfg {Boolean} yearAllowBlank (true|false) default false
29196  * @cfg {string} dayPlaceholder 
29197  * @cfg {string} monthPlaceholder
29198  * @cfg {string} yearPlaceholder
29199  * @cfg {string} dayFormat default 'd'
29200  * @cfg {string} monthFormat default 'm'
29201  * @cfg {string} yearFormat default 'Y'
29202  * @cfg {Number} labellg set the width of label (1-12)
29203  * @cfg {Number} labelmd set the width of label (1-12)
29204  * @cfg {Number} labelsm set the width of label (1-12)
29205  * @cfg {Number} labelxs set the width of label (1-12)
29206
29207  *     
29208  * @constructor
29209  * Create a new DateSplitField
29210  * @param {Object} config The config object
29211  */
29212
29213 Roo.bootstrap.DateSplitField = function(config){
29214     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29215     
29216     this.addEvents({
29217         // raw events
29218          /**
29219          * @event years
29220          * getting the data of years
29221          * @param {Roo.bootstrap.DateSplitField} this
29222          * @param {Object} years
29223          */
29224         "years" : true,
29225         /**
29226          * @event days
29227          * getting the data of days
29228          * @param {Roo.bootstrap.DateSplitField} this
29229          * @param {Object} days
29230          */
29231         "days" : true,
29232         /**
29233          * @event invalid
29234          * Fires after the field has been marked as invalid.
29235          * @param {Roo.form.Field} this
29236          * @param {String} msg The validation message
29237          */
29238         invalid : true,
29239        /**
29240          * @event valid
29241          * Fires after the field has been validated with no errors.
29242          * @param {Roo.form.Field} this
29243          */
29244         valid : true
29245     });
29246 };
29247
29248 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29249     
29250     fieldLabel : '',
29251     labelAlign : 'top',
29252     labelWidth : 3,
29253     dayAllowBlank : false,
29254     monthAllowBlank : false,
29255     yearAllowBlank : false,
29256     dayPlaceholder : '',
29257     monthPlaceholder : '',
29258     yearPlaceholder : '',
29259     dayFormat : 'd',
29260     monthFormat : 'm',
29261     yearFormat : 'Y',
29262     isFormField : true,
29263     labellg : 0,
29264     labelmd : 0,
29265     labelsm : 0,
29266     labelxs : 0,
29267     
29268     getAutoCreate : function()
29269     {
29270         var cfg = {
29271             tag : 'div',
29272             cls : 'row roo-date-split-field-group',
29273             cn : [
29274                 {
29275                     tag : 'input',
29276                     type : 'hidden',
29277                     cls : 'form-hidden-field roo-date-split-field-group-value',
29278                     name : this.name
29279                 }
29280             ]
29281         };
29282         
29283         var labelCls = 'col-md-12';
29284         var contentCls = 'col-md-4';
29285         
29286         if(this.fieldLabel){
29287             
29288             var label = {
29289                 tag : 'div',
29290                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29291                 cn : [
29292                     {
29293                         tag : 'label',
29294                         html : this.fieldLabel
29295                     }
29296                 ]
29297             };
29298             
29299             if(this.labelAlign == 'left'){
29300             
29301                 if(this.labelWidth > 12){
29302                     label.style = "width: " + this.labelWidth + 'px';
29303                 }
29304
29305                 if(this.labelWidth < 13 && this.labelmd == 0){
29306                     this.labelmd = this.labelWidth;
29307                 }
29308
29309                 if(this.labellg > 0){
29310                     labelCls = ' col-lg-' + this.labellg;
29311                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29312                 }
29313
29314                 if(this.labelmd > 0){
29315                     labelCls = ' col-md-' + this.labelmd;
29316                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29317                 }
29318
29319                 if(this.labelsm > 0){
29320                     labelCls = ' col-sm-' + this.labelsm;
29321                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29322                 }
29323
29324                 if(this.labelxs > 0){
29325                     labelCls = ' col-xs-' + this.labelxs;
29326                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29327                 }
29328             }
29329             
29330             label.cls += ' ' + labelCls;
29331             
29332             cfg.cn.push(label);
29333         }
29334         
29335         Roo.each(['day', 'month', 'year'], function(t){
29336             cfg.cn.push({
29337                 tag : 'div',
29338                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29339             });
29340         }, this);
29341         
29342         return cfg;
29343     },
29344     
29345     inputEl: function ()
29346     {
29347         return this.el.select('.roo-date-split-field-group-value', true).first();
29348     },
29349     
29350     onRender : function(ct, position) 
29351     {
29352         var _this = this;
29353         
29354         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29355         
29356         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29357         
29358         this.dayField = new Roo.bootstrap.ComboBox({
29359             allowBlank : this.dayAllowBlank,
29360             alwaysQuery : true,
29361             displayField : 'value',
29362             editable : false,
29363             fieldLabel : '',
29364             forceSelection : true,
29365             mode : 'local',
29366             placeholder : this.dayPlaceholder,
29367             selectOnFocus : true,
29368             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29369             triggerAction : 'all',
29370             typeAhead : true,
29371             valueField : 'value',
29372             store : new Roo.data.SimpleStore({
29373                 data : (function() {    
29374                     var days = [];
29375                     _this.fireEvent('days', _this, days);
29376                     return days;
29377                 })(),
29378                 fields : [ 'value' ]
29379             }),
29380             listeners : {
29381                 select : function (_self, record, index)
29382                 {
29383                     _this.setValue(_this.getValue());
29384                 }
29385             }
29386         });
29387
29388         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29389         
29390         this.monthField = new Roo.bootstrap.MonthField({
29391             after : '<i class=\"fa fa-calendar\"></i>',
29392             allowBlank : this.monthAllowBlank,
29393             placeholder : this.monthPlaceholder,
29394             readOnly : true,
29395             listeners : {
29396                 render : function (_self)
29397                 {
29398                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29399                         e.preventDefault();
29400                         _self.focus();
29401                     });
29402                 },
29403                 select : function (_self, oldvalue, newvalue)
29404                 {
29405                     _this.setValue(_this.getValue());
29406                 }
29407             }
29408         });
29409         
29410         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29411         
29412         this.yearField = new Roo.bootstrap.ComboBox({
29413             allowBlank : this.yearAllowBlank,
29414             alwaysQuery : true,
29415             displayField : 'value',
29416             editable : false,
29417             fieldLabel : '',
29418             forceSelection : true,
29419             mode : 'local',
29420             placeholder : this.yearPlaceholder,
29421             selectOnFocus : true,
29422             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29423             triggerAction : 'all',
29424             typeAhead : true,
29425             valueField : 'value',
29426             store : new Roo.data.SimpleStore({
29427                 data : (function() {
29428                     var years = [];
29429                     _this.fireEvent('years', _this, years);
29430                     return years;
29431                 })(),
29432                 fields : [ 'value' ]
29433             }),
29434             listeners : {
29435                 select : function (_self, record, index)
29436                 {
29437                     _this.setValue(_this.getValue());
29438                 }
29439             }
29440         });
29441
29442         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29443     },
29444     
29445     setValue : function(v, format)
29446     {
29447         this.inputEl.dom.value = v;
29448         
29449         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29450         
29451         var d = Date.parseDate(v, f);
29452         
29453         if(!d){
29454             this.validate();
29455             return;
29456         }
29457         
29458         this.setDay(d.format(this.dayFormat));
29459         this.setMonth(d.format(this.monthFormat));
29460         this.setYear(d.format(this.yearFormat));
29461         
29462         this.validate();
29463         
29464         return;
29465     },
29466     
29467     setDay : function(v)
29468     {
29469         this.dayField.setValue(v);
29470         this.inputEl.dom.value = this.getValue();
29471         this.validate();
29472         return;
29473     },
29474     
29475     setMonth : function(v)
29476     {
29477         this.monthField.setValue(v, true);
29478         this.inputEl.dom.value = this.getValue();
29479         this.validate();
29480         return;
29481     },
29482     
29483     setYear : function(v)
29484     {
29485         this.yearField.setValue(v);
29486         this.inputEl.dom.value = this.getValue();
29487         this.validate();
29488         return;
29489     },
29490     
29491     getDay : function()
29492     {
29493         return this.dayField.getValue();
29494     },
29495     
29496     getMonth : function()
29497     {
29498         return this.monthField.getValue();
29499     },
29500     
29501     getYear : function()
29502     {
29503         return this.yearField.getValue();
29504     },
29505     
29506     getValue : function()
29507     {
29508         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29509         
29510         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29511         
29512         return date;
29513     },
29514     
29515     reset : function()
29516     {
29517         this.setDay('');
29518         this.setMonth('');
29519         this.setYear('');
29520         this.inputEl.dom.value = '';
29521         this.validate();
29522         return;
29523     },
29524     
29525     validate : function()
29526     {
29527         var d = this.dayField.validate();
29528         var m = this.monthField.validate();
29529         var y = this.yearField.validate();
29530         
29531         var valid = true;
29532         
29533         if(
29534                 (!this.dayAllowBlank && !d) ||
29535                 (!this.monthAllowBlank && !m) ||
29536                 (!this.yearAllowBlank && !y)
29537         ){
29538             valid = false;
29539         }
29540         
29541         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29542             return valid;
29543         }
29544         
29545         if(valid){
29546             this.markValid();
29547             return valid;
29548         }
29549         
29550         this.markInvalid();
29551         
29552         return valid;
29553     },
29554     
29555     markValid : function()
29556     {
29557         
29558         var label = this.el.select('label', true).first();
29559         var icon = this.el.select('i.fa-star', true).first();
29560
29561         if(label && icon){
29562             icon.remove();
29563         }
29564         
29565         this.fireEvent('valid', this);
29566     },
29567     
29568      /**
29569      * Mark this field as invalid
29570      * @param {String} msg The validation message
29571      */
29572     markInvalid : function(msg)
29573     {
29574         
29575         var label = this.el.select('label', true).first();
29576         var icon = this.el.select('i.fa-star', true).first();
29577
29578         if(label && !icon){
29579             this.el.select('.roo-date-split-field-label', true).createChild({
29580                 tag : 'i',
29581                 cls : 'text-danger fa fa-lg fa-star',
29582                 tooltip : 'This field is required',
29583                 style : 'margin-right:5px;'
29584             }, label, true);
29585         }
29586         
29587         this.fireEvent('invalid', this, msg);
29588     },
29589     
29590     clearInvalid : function()
29591     {
29592         var label = this.el.select('label', true).first();
29593         var icon = this.el.select('i.fa-star', true).first();
29594
29595         if(label && icon){
29596             icon.remove();
29597         }
29598         
29599         this.fireEvent('valid', this);
29600     },
29601     
29602     getName: function()
29603     {
29604         return this.name;
29605     }
29606     
29607 });
29608
29609  /**
29610  *
29611  * This is based on 
29612  * http://masonry.desandro.com
29613  *
29614  * The idea is to render all the bricks based on vertical width...
29615  *
29616  * The original code extends 'outlayer' - we might need to use that....
29617  * 
29618  */
29619
29620
29621 /**
29622  * @class Roo.bootstrap.LayoutMasonry
29623  * @extends Roo.bootstrap.Component
29624  * Bootstrap Layout Masonry class
29625  * 
29626  * @constructor
29627  * Create a new Element
29628  * @param {Object} config The config object
29629  */
29630
29631 Roo.bootstrap.LayoutMasonry = function(config){
29632     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29633     
29634     this.bricks = [];
29635     
29636 };
29637
29638 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29639     
29640     /**
29641      * @cfg {Boolean} isLayoutInstant = no animation?
29642      */   
29643     isLayoutInstant : false, // needed?
29644    
29645     /**
29646      * @cfg {Number} boxWidth  width of the columns
29647      */   
29648     boxWidth : 450,
29649     
29650       /**
29651      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29652      */   
29653     boxHeight : 0,
29654     
29655     /**
29656      * @cfg {Number} padWidth padding below box..
29657      */   
29658     padWidth : 10, 
29659     
29660     /**
29661      * @cfg {Number} gutter gutter width..
29662      */   
29663     gutter : 10,
29664     
29665      /**
29666      * @cfg {Number} maxCols maximum number of columns
29667      */   
29668     
29669     maxCols: 0,
29670     
29671     /**
29672      * @cfg {Boolean} isAutoInitial defalut true
29673      */   
29674     isAutoInitial : true, 
29675     
29676     containerWidth: 0,
29677     
29678     /**
29679      * @cfg {Boolean} isHorizontal defalut false
29680      */   
29681     isHorizontal : false, 
29682
29683     currentSize : null,
29684     
29685     tag: 'div',
29686     
29687     cls: '',
29688     
29689     bricks: null, //CompositeElement
29690     
29691     cols : 1,
29692     
29693     _isLayoutInited : false,
29694     
29695 //    isAlternative : false, // only use for vertical layout...
29696     
29697     /**
29698      * @cfg {Number} alternativePadWidth padding below box..
29699      */   
29700     alternativePadWidth : 50, 
29701     
29702     getAutoCreate : function(){
29703         
29704         var cfg = {
29705             tag: this.tag,
29706             cls: 'blog-masonary-wrapper ' + this.cls,
29707             cn : {
29708                 cls : 'mas-boxes masonary'
29709             }
29710         };
29711         
29712         return cfg;
29713     },
29714     
29715     getChildContainer: function( )
29716     {
29717         if (this.boxesEl) {
29718             return this.boxesEl;
29719         }
29720         
29721         this.boxesEl = this.el.select('.mas-boxes').first();
29722         
29723         return this.boxesEl;
29724     },
29725     
29726     
29727     initEvents : function()
29728     {
29729         var _this = this;
29730         
29731         if(this.isAutoInitial){
29732             Roo.log('hook children rendered');
29733             this.on('childrenrendered', function() {
29734                 Roo.log('children rendered');
29735                 _this.initial();
29736             } ,this);
29737         }
29738     },
29739     
29740     initial : function()
29741     {
29742         this.currentSize = this.el.getBox(true);
29743         
29744         Roo.EventManager.onWindowResize(this.resize, this); 
29745
29746         if(!this.isAutoInitial){
29747             this.layout();
29748             return;
29749         }
29750         
29751         this.layout();
29752         
29753         return;
29754         //this.layout.defer(500,this);
29755         
29756     },
29757     
29758     resize : function()
29759     {
29760         Roo.log('resize');
29761         
29762         var cs = this.el.getBox(true);
29763         
29764         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29765             Roo.log("no change in with or X");
29766             return;
29767         }
29768         
29769         this.currentSize = cs;
29770         
29771         this.layout();
29772         
29773     },
29774     
29775     layout : function()
29776     {   
29777         this._resetLayout();
29778         
29779         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29780         
29781         this.layoutItems( isInstant );
29782       
29783         this._isLayoutInited = true;
29784         
29785     },
29786     
29787     _resetLayout : function()
29788     {
29789         if(this.isHorizontal){
29790             this.horizontalMeasureColumns();
29791             return;
29792         }
29793         
29794         this.verticalMeasureColumns();
29795         
29796     },
29797     
29798     verticalMeasureColumns : function()
29799     {
29800         this.getContainerWidth();
29801         
29802 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29803 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29804 //            return;
29805 //        }
29806         
29807         var boxWidth = this.boxWidth + this.padWidth;
29808         
29809         if(this.containerWidth < this.boxWidth){
29810             boxWidth = this.containerWidth
29811         }
29812         
29813         var containerWidth = this.containerWidth;
29814         
29815         var cols = Math.floor(containerWidth / boxWidth);
29816         
29817         this.cols = Math.max( cols, 1 );
29818         
29819         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29820         
29821         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29822         
29823         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29824         
29825         this.colWidth = boxWidth + avail - this.padWidth;
29826         
29827         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29828         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29829     },
29830     
29831     horizontalMeasureColumns : function()
29832     {
29833         this.getContainerWidth();
29834         
29835         var boxWidth = this.boxWidth;
29836         
29837         if(this.containerWidth < boxWidth){
29838             boxWidth = this.containerWidth;
29839         }
29840         
29841         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29842         
29843         this.el.setHeight(boxWidth);
29844         
29845     },
29846     
29847     getContainerWidth : function()
29848     {
29849         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29850     },
29851     
29852     layoutItems : function( isInstant )
29853     {
29854         var items = Roo.apply([], this.bricks);
29855         
29856         if(this.isHorizontal){
29857             this._horizontalLayoutItems( items , isInstant );
29858             return;
29859         }
29860         
29861 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29862 //            this._verticalAlternativeLayoutItems( items , isInstant );
29863 //            return;
29864 //        }
29865         
29866         this._verticalLayoutItems( items , isInstant );
29867         
29868     },
29869     
29870     _verticalLayoutItems : function ( items , isInstant)
29871     {
29872         if ( !items || !items.length ) {
29873             return;
29874         }
29875         
29876         var standard = [
29877             ['xs', 'xs', 'xs', 'tall'],
29878             ['xs', 'xs', 'tall'],
29879             ['xs', 'xs', 'sm'],
29880             ['xs', 'xs', 'xs'],
29881             ['xs', 'tall'],
29882             ['xs', 'sm'],
29883             ['xs', 'xs'],
29884             ['xs'],
29885             
29886             ['sm', 'xs', 'xs'],
29887             ['sm', 'xs'],
29888             ['sm'],
29889             
29890             ['tall', 'xs', 'xs', 'xs'],
29891             ['tall', 'xs', 'xs'],
29892             ['tall', 'xs'],
29893             ['tall']
29894             
29895         ];
29896         
29897         var queue = [];
29898         
29899         var boxes = [];
29900         
29901         var box = [];
29902         
29903         Roo.each(items, function(item, k){
29904             
29905             switch (item.size) {
29906                 // these layouts take up a full box,
29907                 case 'md' :
29908                 case 'md-left' :
29909                 case 'md-right' :
29910                 case 'wide' :
29911                     
29912                     if(box.length){
29913                         boxes.push(box);
29914                         box = [];
29915                     }
29916                     
29917                     boxes.push([item]);
29918                     
29919                     break;
29920                     
29921                 case 'xs' :
29922                 case 'sm' :
29923                 case 'tall' :
29924                     
29925                     box.push(item);
29926                     
29927                     break;
29928                 default :
29929                     break;
29930                     
29931             }
29932             
29933         }, this);
29934         
29935         if(box.length){
29936             boxes.push(box);
29937             box = [];
29938         }
29939         
29940         var filterPattern = function(box, length)
29941         {
29942             if(!box.length){
29943                 return;
29944             }
29945             
29946             var match = false;
29947             
29948             var pattern = box.slice(0, length);
29949             
29950             var format = [];
29951             
29952             Roo.each(pattern, function(i){
29953                 format.push(i.size);
29954             }, this);
29955             
29956             Roo.each(standard, function(s){
29957                 
29958                 if(String(s) != String(format)){
29959                     return;
29960                 }
29961                 
29962                 match = true;
29963                 return false;
29964                 
29965             }, this);
29966             
29967             if(!match && length == 1){
29968                 return;
29969             }
29970             
29971             if(!match){
29972                 filterPattern(box, length - 1);
29973                 return;
29974             }
29975                 
29976             queue.push(pattern);
29977
29978             box = box.slice(length, box.length);
29979
29980             filterPattern(box, 4);
29981
29982             return;
29983             
29984         }
29985         
29986         Roo.each(boxes, function(box, k){
29987             
29988             if(!box.length){
29989                 return;
29990             }
29991             
29992             if(box.length == 1){
29993                 queue.push(box);
29994                 return;
29995             }
29996             
29997             filterPattern(box, 4);
29998             
29999         }, this);
30000         
30001         this._processVerticalLayoutQueue( queue, isInstant );
30002         
30003     },
30004     
30005 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30006 //    {
30007 //        if ( !items || !items.length ) {
30008 //            return;
30009 //        }
30010 //
30011 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30012 //        
30013 //    },
30014     
30015     _horizontalLayoutItems : function ( items , isInstant)
30016     {
30017         if ( !items || !items.length || items.length < 3) {
30018             return;
30019         }
30020         
30021         items.reverse();
30022         
30023         var eItems = items.slice(0, 3);
30024         
30025         items = items.slice(3, items.length);
30026         
30027         var standard = [
30028             ['xs', 'xs', 'xs', 'wide'],
30029             ['xs', 'xs', 'wide'],
30030             ['xs', 'xs', 'sm'],
30031             ['xs', 'xs', 'xs'],
30032             ['xs', 'wide'],
30033             ['xs', 'sm'],
30034             ['xs', 'xs'],
30035             ['xs'],
30036             
30037             ['sm', 'xs', 'xs'],
30038             ['sm', 'xs'],
30039             ['sm'],
30040             
30041             ['wide', 'xs', 'xs', 'xs'],
30042             ['wide', 'xs', 'xs'],
30043             ['wide', 'xs'],
30044             ['wide'],
30045             
30046             ['wide-thin']
30047         ];
30048         
30049         var queue = [];
30050         
30051         var boxes = [];
30052         
30053         var box = [];
30054         
30055         Roo.each(items, function(item, k){
30056             
30057             switch (item.size) {
30058                 case 'md' :
30059                 case 'md-left' :
30060                 case 'md-right' :
30061                 case 'tall' :
30062                     
30063                     if(box.length){
30064                         boxes.push(box);
30065                         box = [];
30066                     }
30067                     
30068                     boxes.push([item]);
30069                     
30070                     break;
30071                     
30072                 case 'xs' :
30073                 case 'sm' :
30074                 case 'wide' :
30075                 case 'wide-thin' :
30076                     
30077                     box.push(item);
30078                     
30079                     break;
30080                 default :
30081                     break;
30082                     
30083             }
30084             
30085         }, this);
30086         
30087         if(box.length){
30088             boxes.push(box);
30089             box = [];
30090         }
30091         
30092         var filterPattern = function(box, length)
30093         {
30094             if(!box.length){
30095                 return;
30096             }
30097             
30098             var match = false;
30099             
30100             var pattern = box.slice(0, length);
30101             
30102             var format = [];
30103             
30104             Roo.each(pattern, function(i){
30105                 format.push(i.size);
30106             }, this);
30107             
30108             Roo.each(standard, function(s){
30109                 
30110                 if(String(s) != String(format)){
30111                     return;
30112                 }
30113                 
30114                 match = true;
30115                 return false;
30116                 
30117             }, this);
30118             
30119             if(!match && length == 1){
30120                 return;
30121             }
30122             
30123             if(!match){
30124                 filterPattern(box, length - 1);
30125                 return;
30126             }
30127                 
30128             queue.push(pattern);
30129
30130             box = box.slice(length, box.length);
30131
30132             filterPattern(box, 4);
30133
30134             return;
30135             
30136         }
30137         
30138         Roo.each(boxes, function(box, k){
30139             
30140             if(!box.length){
30141                 return;
30142             }
30143             
30144             if(box.length == 1){
30145                 queue.push(box);
30146                 return;
30147             }
30148             
30149             filterPattern(box, 4);
30150             
30151         }, this);
30152         
30153         
30154         var prune = [];
30155         
30156         var pos = this.el.getBox(true);
30157         
30158         var minX = pos.x;
30159         
30160         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30161         
30162         var hit_end = false;
30163         
30164         Roo.each(queue, function(box){
30165             
30166             if(hit_end){
30167                 
30168                 Roo.each(box, function(b){
30169                 
30170                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30171                     b.el.hide();
30172
30173                 }, this);
30174
30175                 return;
30176             }
30177             
30178             var mx = 0;
30179             
30180             Roo.each(box, function(b){
30181                 
30182                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30183                 b.el.show();
30184
30185                 mx = Math.max(mx, b.x);
30186                 
30187             }, this);
30188             
30189             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30190             
30191             if(maxX < minX){
30192                 
30193                 Roo.each(box, function(b){
30194                 
30195                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30196                     b.el.hide();
30197                     
30198                 }, this);
30199                 
30200                 hit_end = true;
30201                 
30202                 return;
30203             }
30204             
30205             prune.push(box);
30206             
30207         }, this);
30208         
30209         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30210     },
30211     
30212     /** Sets position of item in DOM
30213     * @param {Element} item
30214     * @param {Number} x - horizontal position
30215     * @param {Number} y - vertical position
30216     * @param {Boolean} isInstant - disables transitions
30217     */
30218     _processVerticalLayoutQueue : function( queue, isInstant )
30219     {
30220         var pos = this.el.getBox(true);
30221         var x = pos.x;
30222         var y = pos.y;
30223         var maxY = [];
30224         
30225         for (var i = 0; i < this.cols; i++){
30226             maxY[i] = pos.y;
30227         }
30228         
30229         Roo.each(queue, function(box, k){
30230             
30231             var col = k % this.cols;
30232             
30233             Roo.each(box, function(b,kk){
30234                 
30235                 b.el.position('absolute');
30236                 
30237                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30238                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30239                 
30240                 if(b.size == 'md-left' || b.size == 'md-right'){
30241                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30242                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30243                 }
30244                 
30245                 b.el.setWidth(width);
30246                 b.el.setHeight(height);
30247                 // iframe?
30248                 b.el.select('iframe',true).setSize(width,height);
30249                 
30250             }, this);
30251             
30252             for (var i = 0; i < this.cols; i++){
30253                 
30254                 if(maxY[i] < maxY[col]){
30255                     col = i;
30256                     continue;
30257                 }
30258                 
30259                 col = Math.min(col, i);
30260                 
30261             }
30262             
30263             x = pos.x + col * (this.colWidth + this.padWidth);
30264             
30265             y = maxY[col];
30266             
30267             var positions = [];
30268             
30269             switch (box.length){
30270                 case 1 :
30271                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30272                     break;
30273                 case 2 :
30274                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30275                     break;
30276                 case 3 :
30277                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30278                     break;
30279                 case 4 :
30280                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30281                     break;
30282                 default :
30283                     break;
30284             }
30285             
30286             Roo.each(box, function(b,kk){
30287                 
30288                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30289                 
30290                 var sz = b.el.getSize();
30291                 
30292                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30293                 
30294             }, this);
30295             
30296         }, this);
30297         
30298         var mY = 0;
30299         
30300         for (var i = 0; i < this.cols; i++){
30301             mY = Math.max(mY, maxY[i]);
30302         }
30303         
30304         this.el.setHeight(mY - pos.y);
30305         
30306     },
30307     
30308 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30309 //    {
30310 //        var pos = this.el.getBox(true);
30311 //        var x = pos.x;
30312 //        var y = pos.y;
30313 //        var maxX = pos.right;
30314 //        
30315 //        var maxHeight = 0;
30316 //        
30317 //        Roo.each(items, function(item, k){
30318 //            
30319 //            var c = k % 2;
30320 //            
30321 //            item.el.position('absolute');
30322 //                
30323 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30324 //
30325 //            item.el.setWidth(width);
30326 //
30327 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30328 //
30329 //            item.el.setHeight(height);
30330 //            
30331 //            if(c == 0){
30332 //                item.el.setXY([x, y], isInstant ? false : true);
30333 //            } else {
30334 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30335 //            }
30336 //            
30337 //            y = y + height + this.alternativePadWidth;
30338 //            
30339 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30340 //            
30341 //        }, this);
30342 //        
30343 //        this.el.setHeight(maxHeight);
30344 //        
30345 //    },
30346     
30347     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30348     {
30349         var pos = this.el.getBox(true);
30350         
30351         var minX = pos.x;
30352         var minY = pos.y;
30353         
30354         var maxX = pos.right;
30355         
30356         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30357         
30358         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30359         
30360         Roo.each(queue, function(box, k){
30361             
30362             Roo.each(box, function(b, kk){
30363                 
30364                 b.el.position('absolute');
30365                 
30366                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30367                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30368                 
30369                 if(b.size == 'md-left' || b.size == 'md-right'){
30370                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30371                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30372                 }
30373                 
30374                 b.el.setWidth(width);
30375                 b.el.setHeight(height);
30376                 
30377             }, this);
30378             
30379             if(!box.length){
30380                 return;
30381             }
30382             
30383             var positions = [];
30384             
30385             switch (box.length){
30386                 case 1 :
30387                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30388                     break;
30389                 case 2 :
30390                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30391                     break;
30392                 case 3 :
30393                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30394                     break;
30395                 case 4 :
30396                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30397                     break;
30398                 default :
30399                     break;
30400             }
30401             
30402             Roo.each(box, function(b,kk){
30403                 
30404                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30405                 
30406                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30407                 
30408             }, this);
30409             
30410         }, this);
30411         
30412     },
30413     
30414     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30415     {
30416         Roo.each(eItems, function(b,k){
30417             
30418             b.size = (k == 0) ? 'sm' : 'xs';
30419             b.x = (k == 0) ? 2 : 1;
30420             b.y = (k == 0) ? 2 : 1;
30421             
30422             b.el.position('absolute');
30423             
30424             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30425                 
30426             b.el.setWidth(width);
30427             
30428             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30429             
30430             b.el.setHeight(height);
30431             
30432         }, this);
30433
30434         var positions = [];
30435         
30436         positions.push({
30437             x : maxX - this.unitWidth * 2 - this.gutter,
30438             y : minY
30439         });
30440         
30441         positions.push({
30442             x : maxX - this.unitWidth,
30443             y : minY + (this.unitWidth + this.gutter) * 2
30444         });
30445         
30446         positions.push({
30447             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30448             y : minY
30449         });
30450         
30451         Roo.each(eItems, function(b,k){
30452             
30453             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30454
30455         }, this);
30456         
30457     },
30458     
30459     getVerticalOneBoxColPositions : function(x, y, box)
30460     {
30461         var pos = [];
30462         
30463         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30464         
30465         if(box[0].size == 'md-left'){
30466             rand = 0;
30467         }
30468         
30469         if(box[0].size == 'md-right'){
30470             rand = 1;
30471         }
30472         
30473         pos.push({
30474             x : x + (this.unitWidth + this.gutter) * rand,
30475             y : y
30476         });
30477         
30478         return pos;
30479     },
30480     
30481     getVerticalTwoBoxColPositions : function(x, y, box)
30482     {
30483         var pos = [];
30484         
30485         if(box[0].size == 'xs'){
30486             
30487             pos.push({
30488                 x : x,
30489                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30490             });
30491
30492             pos.push({
30493                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30494                 y : y
30495             });
30496             
30497             return pos;
30498             
30499         }
30500         
30501         pos.push({
30502             x : x,
30503             y : y
30504         });
30505
30506         pos.push({
30507             x : x + (this.unitWidth + this.gutter) * 2,
30508             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30509         });
30510         
30511         return pos;
30512         
30513     },
30514     
30515     getVerticalThreeBoxColPositions : function(x, y, box)
30516     {
30517         var pos = [];
30518         
30519         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30520             
30521             pos.push({
30522                 x : x,
30523                 y : y
30524             });
30525
30526             pos.push({
30527                 x : x + (this.unitWidth + this.gutter) * 1,
30528                 y : y
30529             });
30530             
30531             pos.push({
30532                 x : x + (this.unitWidth + this.gutter) * 2,
30533                 y : y
30534             });
30535             
30536             return pos;
30537             
30538         }
30539         
30540         if(box[0].size == 'xs' && box[1].size == 'xs'){
30541             
30542             pos.push({
30543                 x : x,
30544                 y : y
30545             });
30546
30547             pos.push({
30548                 x : x,
30549                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30550             });
30551             
30552             pos.push({
30553                 x : x + (this.unitWidth + this.gutter) * 1,
30554                 y : y
30555             });
30556             
30557             return pos;
30558             
30559         }
30560         
30561         pos.push({
30562             x : x,
30563             y : y
30564         });
30565
30566         pos.push({
30567             x : x + (this.unitWidth + this.gutter) * 2,
30568             y : y
30569         });
30570
30571         pos.push({
30572             x : x + (this.unitWidth + this.gutter) * 2,
30573             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30574         });
30575             
30576         return pos;
30577         
30578     },
30579     
30580     getVerticalFourBoxColPositions : function(x, y, box)
30581     {
30582         var pos = [];
30583         
30584         if(box[0].size == 'xs'){
30585             
30586             pos.push({
30587                 x : x,
30588                 y : y
30589             });
30590
30591             pos.push({
30592                 x : x,
30593                 y : y + (this.unitHeight + this.gutter) * 1
30594             });
30595             
30596             pos.push({
30597                 x : x,
30598                 y : y + (this.unitHeight + this.gutter) * 2
30599             });
30600             
30601             pos.push({
30602                 x : x + (this.unitWidth + this.gutter) * 1,
30603                 y : y
30604             });
30605             
30606             return pos;
30607             
30608         }
30609         
30610         pos.push({
30611             x : x,
30612             y : y
30613         });
30614
30615         pos.push({
30616             x : x + (this.unitWidth + this.gutter) * 2,
30617             y : y
30618         });
30619
30620         pos.push({
30621             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30622             y : y + (this.unitHeight + this.gutter) * 1
30623         });
30624
30625         pos.push({
30626             x : x + (this.unitWidth + this.gutter) * 2,
30627             y : y + (this.unitWidth + this.gutter) * 2
30628         });
30629
30630         return pos;
30631         
30632     },
30633     
30634     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30635     {
30636         var pos = [];
30637         
30638         if(box[0].size == 'md-left'){
30639             pos.push({
30640                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30641                 y : minY
30642             });
30643             
30644             return pos;
30645         }
30646         
30647         if(box[0].size == 'md-right'){
30648             pos.push({
30649                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30650                 y : minY + (this.unitWidth + this.gutter) * 1
30651             });
30652             
30653             return pos;
30654         }
30655         
30656         var rand = Math.floor(Math.random() * (4 - box[0].y));
30657         
30658         pos.push({
30659             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30660             y : minY + (this.unitWidth + this.gutter) * rand
30661         });
30662         
30663         return pos;
30664         
30665     },
30666     
30667     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30668     {
30669         var pos = [];
30670         
30671         if(box[0].size == 'xs'){
30672             
30673             pos.push({
30674                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30675                 y : minY
30676             });
30677
30678             pos.push({
30679                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30680                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30681             });
30682             
30683             return pos;
30684             
30685         }
30686         
30687         pos.push({
30688             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30689             y : minY
30690         });
30691
30692         pos.push({
30693             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30694             y : minY + (this.unitWidth + this.gutter) * 2
30695         });
30696         
30697         return pos;
30698         
30699     },
30700     
30701     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30702     {
30703         var pos = [];
30704         
30705         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30706             
30707             pos.push({
30708                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30709                 y : minY
30710             });
30711
30712             pos.push({
30713                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30714                 y : minY + (this.unitWidth + this.gutter) * 1
30715             });
30716             
30717             pos.push({
30718                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30719                 y : minY + (this.unitWidth + this.gutter) * 2
30720             });
30721             
30722             return pos;
30723             
30724         }
30725         
30726         if(box[0].size == 'xs' && box[1].size == 'xs'){
30727             
30728             pos.push({
30729                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30730                 y : minY
30731             });
30732
30733             pos.push({
30734                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30735                 y : minY
30736             });
30737             
30738             pos.push({
30739                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30740                 y : minY + (this.unitWidth + this.gutter) * 1
30741             });
30742             
30743             return pos;
30744             
30745         }
30746         
30747         pos.push({
30748             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30749             y : minY
30750         });
30751
30752         pos.push({
30753             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30754             y : minY + (this.unitWidth + this.gutter) * 2
30755         });
30756
30757         pos.push({
30758             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30759             y : minY + (this.unitWidth + this.gutter) * 2
30760         });
30761             
30762         return pos;
30763         
30764     },
30765     
30766     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30767     {
30768         var pos = [];
30769         
30770         if(box[0].size == 'xs'){
30771             
30772             pos.push({
30773                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30774                 y : minY
30775             });
30776
30777             pos.push({
30778                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30779                 y : minY
30780             });
30781             
30782             pos.push({
30783                 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),
30784                 y : minY
30785             });
30786             
30787             pos.push({
30788                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30789                 y : minY + (this.unitWidth + this.gutter) * 1
30790             });
30791             
30792             return pos;
30793             
30794         }
30795         
30796         pos.push({
30797             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30798             y : minY
30799         });
30800         
30801         pos.push({
30802             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30803             y : minY + (this.unitWidth + this.gutter) * 2
30804         });
30805         
30806         pos.push({
30807             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30808             y : minY + (this.unitWidth + this.gutter) * 2
30809         });
30810         
30811         pos.push({
30812             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),
30813             y : minY + (this.unitWidth + this.gutter) * 2
30814         });
30815
30816         return pos;
30817         
30818     }
30819     
30820 });
30821
30822  
30823
30824  /**
30825  *
30826  * This is based on 
30827  * http://masonry.desandro.com
30828  *
30829  * The idea is to render all the bricks based on vertical width...
30830  *
30831  * The original code extends 'outlayer' - we might need to use that....
30832  * 
30833  */
30834
30835
30836 /**
30837  * @class Roo.bootstrap.LayoutMasonryAuto
30838  * @extends Roo.bootstrap.Component
30839  * Bootstrap Layout Masonry class
30840  * 
30841  * @constructor
30842  * Create a new Element
30843  * @param {Object} config The config object
30844  */
30845
30846 Roo.bootstrap.LayoutMasonryAuto = function(config){
30847     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30848 };
30849
30850 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30851     
30852       /**
30853      * @cfg {Boolean} isFitWidth  - resize the width..
30854      */   
30855     isFitWidth : false,  // options..
30856     /**
30857      * @cfg {Boolean} isOriginLeft = left align?
30858      */   
30859     isOriginLeft : true,
30860     /**
30861      * @cfg {Boolean} isOriginTop = top align?
30862      */   
30863     isOriginTop : false,
30864     /**
30865      * @cfg {Boolean} isLayoutInstant = no animation?
30866      */   
30867     isLayoutInstant : false, // needed?
30868     /**
30869      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30870      */   
30871     isResizingContainer : true,
30872     /**
30873      * @cfg {Number} columnWidth  width of the columns 
30874      */   
30875     
30876     columnWidth : 0,
30877     
30878     /**
30879      * @cfg {Number} maxCols maximum number of columns
30880      */   
30881     
30882     maxCols: 0,
30883     /**
30884      * @cfg {Number} padHeight padding below box..
30885      */   
30886     
30887     padHeight : 10, 
30888     
30889     /**
30890      * @cfg {Boolean} isAutoInitial defalut true
30891      */   
30892     
30893     isAutoInitial : true, 
30894     
30895     // private?
30896     gutter : 0,
30897     
30898     containerWidth: 0,
30899     initialColumnWidth : 0,
30900     currentSize : null,
30901     
30902     colYs : null, // array.
30903     maxY : 0,
30904     padWidth: 10,
30905     
30906     
30907     tag: 'div',
30908     cls: '',
30909     bricks: null, //CompositeElement
30910     cols : 0, // array?
30911     // element : null, // wrapped now this.el
30912     _isLayoutInited : null, 
30913     
30914     
30915     getAutoCreate : function(){
30916         
30917         var cfg = {
30918             tag: this.tag,
30919             cls: 'blog-masonary-wrapper ' + this.cls,
30920             cn : {
30921                 cls : 'mas-boxes masonary'
30922             }
30923         };
30924         
30925         return cfg;
30926     },
30927     
30928     getChildContainer: function( )
30929     {
30930         if (this.boxesEl) {
30931             return this.boxesEl;
30932         }
30933         
30934         this.boxesEl = this.el.select('.mas-boxes').first();
30935         
30936         return this.boxesEl;
30937     },
30938     
30939     
30940     initEvents : function()
30941     {
30942         var _this = this;
30943         
30944         if(this.isAutoInitial){
30945             Roo.log('hook children rendered');
30946             this.on('childrenrendered', function() {
30947                 Roo.log('children rendered');
30948                 _this.initial();
30949             } ,this);
30950         }
30951         
30952     },
30953     
30954     initial : function()
30955     {
30956         this.reloadItems();
30957
30958         this.currentSize = this.el.getBox(true);
30959
30960         /// was window resize... - let's see if this works..
30961         Roo.EventManager.onWindowResize(this.resize, this); 
30962
30963         if(!this.isAutoInitial){
30964             this.layout();
30965             return;
30966         }
30967         
30968         this.layout.defer(500,this);
30969     },
30970     
30971     reloadItems: function()
30972     {
30973         this.bricks = this.el.select('.masonry-brick', true);
30974         
30975         this.bricks.each(function(b) {
30976             //Roo.log(b.getSize());
30977             if (!b.attr('originalwidth')) {
30978                 b.attr('originalwidth',  b.getSize().width);
30979             }
30980             
30981         });
30982         
30983         Roo.log(this.bricks.elements.length);
30984     },
30985     
30986     resize : function()
30987     {
30988         Roo.log('resize');
30989         var cs = this.el.getBox(true);
30990         
30991         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30992             Roo.log("no change in with or X");
30993             return;
30994         }
30995         this.currentSize = cs;
30996         this.layout();
30997     },
30998     
30999     layout : function()
31000     {
31001          Roo.log('layout');
31002         this._resetLayout();
31003         //this._manageStamps();
31004       
31005         // don't animate first layout
31006         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31007         this.layoutItems( isInstant );
31008       
31009         // flag for initalized
31010         this._isLayoutInited = true;
31011     },
31012     
31013     layoutItems : function( isInstant )
31014     {
31015         //var items = this._getItemsForLayout( this.items );
31016         // original code supports filtering layout items.. we just ignore it..
31017         
31018         this._layoutItems( this.bricks , isInstant );
31019       
31020         this._postLayout();
31021     },
31022     _layoutItems : function ( items , isInstant)
31023     {
31024        //this.fireEvent( 'layout', this, items );
31025     
31026
31027         if ( !items || !items.elements.length ) {
31028           // no items, emit event with empty array
31029             return;
31030         }
31031
31032         var queue = [];
31033         items.each(function(item) {
31034             Roo.log("layout item");
31035             Roo.log(item);
31036             // get x/y object from method
31037             var position = this._getItemLayoutPosition( item );
31038             // enqueue
31039             position.item = item;
31040             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31041             queue.push( position );
31042         }, this);
31043       
31044         this._processLayoutQueue( queue );
31045     },
31046     /** Sets position of item in DOM
31047     * @param {Element} item
31048     * @param {Number} x - horizontal position
31049     * @param {Number} y - vertical position
31050     * @param {Boolean} isInstant - disables transitions
31051     */
31052     _processLayoutQueue : function( queue )
31053     {
31054         for ( var i=0, len = queue.length; i < len; i++ ) {
31055             var obj = queue[i];
31056             obj.item.position('absolute');
31057             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31058         }
31059     },
31060       
31061     
31062     /**
31063     * Any logic you want to do after each layout,
31064     * i.e. size the container
31065     */
31066     _postLayout : function()
31067     {
31068         this.resizeContainer();
31069     },
31070     
31071     resizeContainer : function()
31072     {
31073         if ( !this.isResizingContainer ) {
31074             return;
31075         }
31076         var size = this._getContainerSize();
31077         if ( size ) {
31078             this.el.setSize(size.width,size.height);
31079             this.boxesEl.setSize(size.width,size.height);
31080         }
31081     },
31082     
31083     
31084     
31085     _resetLayout : function()
31086     {
31087         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31088         this.colWidth = this.el.getWidth();
31089         //this.gutter = this.el.getWidth(); 
31090         
31091         this.measureColumns();
31092
31093         // reset column Y
31094         var i = this.cols;
31095         this.colYs = [];
31096         while (i--) {
31097             this.colYs.push( 0 );
31098         }
31099     
31100         this.maxY = 0;
31101     },
31102
31103     measureColumns : function()
31104     {
31105         this.getContainerWidth();
31106       // if columnWidth is 0, default to outerWidth of first item
31107         if ( !this.columnWidth ) {
31108             var firstItem = this.bricks.first();
31109             Roo.log(firstItem);
31110             this.columnWidth  = this.containerWidth;
31111             if (firstItem && firstItem.attr('originalwidth') ) {
31112                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31113             }
31114             // columnWidth fall back to item of first element
31115             Roo.log("set column width?");
31116                         this.initialColumnWidth = this.columnWidth  ;
31117
31118             // if first elem has no width, default to size of container
31119             
31120         }
31121         
31122         
31123         if (this.initialColumnWidth) {
31124             this.columnWidth = this.initialColumnWidth;
31125         }
31126         
31127         
31128             
31129         // column width is fixed at the top - however if container width get's smaller we should
31130         // reduce it...
31131         
31132         // this bit calcs how man columns..
31133             
31134         var columnWidth = this.columnWidth += this.gutter;
31135       
31136         // calculate columns
31137         var containerWidth = this.containerWidth + this.gutter;
31138         
31139         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31140         // fix rounding errors, typically with gutters
31141         var excess = columnWidth - containerWidth % columnWidth;
31142         
31143         
31144         // if overshoot is less than a pixel, round up, otherwise floor it
31145         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31146         cols = Math[ mathMethod ]( cols );
31147         this.cols = Math.max( cols, 1 );
31148         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31149         
31150          // padding positioning..
31151         var totalColWidth = this.cols * this.columnWidth;
31152         var padavail = this.containerWidth - totalColWidth;
31153         // so for 2 columns - we need 3 'pads'
31154         
31155         var padNeeded = (1+this.cols) * this.padWidth;
31156         
31157         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31158         
31159         this.columnWidth += padExtra
31160         //this.padWidth = Math.floor(padavail /  ( this.cols));
31161         
31162         // adjust colum width so that padding is fixed??
31163         
31164         // we have 3 columns ... total = width * 3
31165         // we have X left over... that should be used by 
31166         
31167         //if (this.expandC) {
31168             
31169         //}
31170         
31171         
31172         
31173     },
31174     
31175     getContainerWidth : function()
31176     {
31177        /* // container is parent if fit width
31178         var container = this.isFitWidth ? this.element.parentNode : this.element;
31179         // check that this.size and size are there
31180         // IE8 triggers resize on body size change, so they might not be
31181         
31182         var size = getSize( container );  //FIXME
31183         this.containerWidth = size && size.innerWidth; //FIXME
31184         */
31185          
31186         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31187         
31188     },
31189     
31190     _getItemLayoutPosition : function( item )  // what is item?
31191     {
31192         // we resize the item to our columnWidth..
31193       
31194         item.setWidth(this.columnWidth);
31195         item.autoBoxAdjust  = false;
31196         
31197         var sz = item.getSize();
31198  
31199         // how many columns does this brick span
31200         var remainder = this.containerWidth % this.columnWidth;
31201         
31202         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31203         // round if off by 1 pixel, otherwise use ceil
31204         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31205         colSpan = Math.min( colSpan, this.cols );
31206         
31207         // normally this should be '1' as we dont' currently allow multi width columns..
31208         
31209         var colGroup = this._getColGroup( colSpan );
31210         // get the minimum Y value from the columns
31211         var minimumY = Math.min.apply( Math, colGroup );
31212         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31213         
31214         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31215          
31216         // position the brick
31217         var position = {
31218             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31219             y: this.currentSize.y + minimumY + this.padHeight
31220         };
31221         
31222         Roo.log(position);
31223         // apply setHeight to necessary columns
31224         var setHeight = minimumY + sz.height + this.padHeight;
31225         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31226         
31227         var setSpan = this.cols + 1 - colGroup.length;
31228         for ( var i = 0; i < setSpan; i++ ) {
31229           this.colYs[ shortColIndex + i ] = setHeight ;
31230         }
31231       
31232         return position;
31233     },
31234     
31235     /**
31236      * @param {Number} colSpan - number of columns the element spans
31237      * @returns {Array} colGroup
31238      */
31239     _getColGroup : function( colSpan )
31240     {
31241         if ( colSpan < 2 ) {
31242           // if brick spans only one column, use all the column Ys
31243           return this.colYs;
31244         }
31245       
31246         var colGroup = [];
31247         // how many different places could this brick fit horizontally
31248         var groupCount = this.cols + 1 - colSpan;
31249         // for each group potential horizontal position
31250         for ( var i = 0; i < groupCount; i++ ) {
31251           // make an array of colY values for that one group
31252           var groupColYs = this.colYs.slice( i, i + colSpan );
31253           // and get the max value of the array
31254           colGroup[i] = Math.max.apply( Math, groupColYs );
31255         }
31256         return colGroup;
31257     },
31258     /*
31259     _manageStamp : function( stamp )
31260     {
31261         var stampSize =  stamp.getSize();
31262         var offset = stamp.getBox();
31263         // get the columns that this stamp affects
31264         var firstX = this.isOriginLeft ? offset.x : offset.right;
31265         var lastX = firstX + stampSize.width;
31266         var firstCol = Math.floor( firstX / this.columnWidth );
31267         firstCol = Math.max( 0, firstCol );
31268         
31269         var lastCol = Math.floor( lastX / this.columnWidth );
31270         // lastCol should not go over if multiple of columnWidth #425
31271         lastCol -= lastX % this.columnWidth ? 0 : 1;
31272         lastCol = Math.min( this.cols - 1, lastCol );
31273         
31274         // set colYs to bottom of the stamp
31275         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31276             stampSize.height;
31277             
31278         for ( var i = firstCol; i <= lastCol; i++ ) {
31279           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31280         }
31281     },
31282     */
31283     
31284     _getContainerSize : function()
31285     {
31286         this.maxY = Math.max.apply( Math, this.colYs );
31287         var size = {
31288             height: this.maxY
31289         };
31290       
31291         if ( this.isFitWidth ) {
31292             size.width = this._getContainerFitWidth();
31293         }
31294       
31295         return size;
31296     },
31297     
31298     _getContainerFitWidth : function()
31299     {
31300         var unusedCols = 0;
31301         // count unused columns
31302         var i = this.cols;
31303         while ( --i ) {
31304           if ( this.colYs[i] !== 0 ) {
31305             break;
31306           }
31307           unusedCols++;
31308         }
31309         // fit container to columns that have been used
31310         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31311     },
31312     
31313     needsResizeLayout : function()
31314     {
31315         var previousWidth = this.containerWidth;
31316         this.getContainerWidth();
31317         return previousWidth !== this.containerWidth;
31318     }
31319  
31320 });
31321
31322  
31323
31324  /*
31325  * - LGPL
31326  *
31327  * element
31328  * 
31329  */
31330
31331 /**
31332  * @class Roo.bootstrap.MasonryBrick
31333  * @extends Roo.bootstrap.Component
31334  * Bootstrap MasonryBrick class
31335  * 
31336  * @constructor
31337  * Create a new MasonryBrick
31338  * @param {Object} config The config object
31339  */
31340
31341 Roo.bootstrap.MasonryBrick = function(config){
31342     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31343     
31344     this.addEvents({
31345         // raw events
31346         /**
31347          * @event click
31348          * When a MasonryBrick is clcik
31349          * @param {Roo.bootstrap.MasonryBrick} this
31350          * @param {Roo.EventObject} e
31351          */
31352         "click" : true
31353     });
31354 };
31355
31356 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31357     
31358     /**
31359      * @cfg {String} title
31360      */   
31361     title : '',
31362     /**
31363      * @cfg {String} html
31364      */   
31365     html : '',
31366     /**
31367      * @cfg {String} bgimage
31368      */   
31369     bgimage : '',
31370     /**
31371      * @cfg {String} videourl
31372      */   
31373     videourl : '',
31374     /**
31375      * @cfg {String} cls
31376      */   
31377     cls : '',
31378     /**
31379      * @cfg {String} href
31380      */   
31381     href : '',
31382     /**
31383      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31384      */   
31385     size : 'xs',
31386     
31387     /**
31388      * @cfg {String} (center|bottom) placetitle
31389      */   
31390     placetitle : '',
31391     
31392     /**
31393      * @cfg {Boolean} isFitContainer defalut true
31394      */   
31395     isFitContainer : true, 
31396     
31397     /**
31398      * @cfg {Boolean} preventDefault defalut false
31399      */   
31400     preventDefault : false, 
31401     
31402     getAutoCreate : function()
31403     {
31404         if(!this.isFitContainer){
31405             return this.getSplitAutoCreate();
31406         }
31407         
31408         var cls = 'masonry-brick masonry-brick-full';
31409         
31410         if(this.href.length){
31411             cls += ' masonry-brick-link';
31412         }
31413         
31414         if(this.bgimage.length){
31415             cls += ' masonry-brick-image';
31416         }
31417         
31418         if(!this.html.length){
31419             cls += ' enable-mask';
31420         }
31421         
31422         if(this.size){
31423             cls += ' masonry-' + this.size + '-brick';
31424         }
31425         
31426         if(this.placetitle.length){
31427             
31428             switch (this.placetitle) {
31429                 case 'center' :
31430                     cls += ' masonry-center-title';
31431                     break;
31432                 case 'bottom' :
31433                     cls += ' masonry-bottom-title';
31434                     break;
31435                 default:
31436                     break;
31437             }
31438             
31439         } else {
31440             if(!this.html.length && !this.bgimage.length){
31441                 cls += ' masonry-center-title';
31442             }
31443
31444             if(!this.html.length && this.bgimage.length){
31445                 cls += ' masonry-bottom-title';
31446             }
31447         }
31448         
31449         if(this.cls){
31450             cls += ' ' + this.cls;
31451         }
31452         
31453         var cfg = {
31454             tag: (this.href.length) ? 'a' : 'div',
31455             cls: cls,
31456             cn: [
31457                 {
31458                     tag: 'div',
31459                     cls: 'masonry-brick-paragraph',
31460                     cn: []
31461                 }
31462             ]
31463         };
31464         
31465         if(this.href.length){
31466             cfg.href = this.href;
31467         }
31468         
31469         var cn = cfg.cn[0].cn;
31470         
31471         if(this.title.length){
31472             cn.push({
31473                 tag: 'h4',
31474                 cls: 'masonry-brick-title',
31475                 html: this.title
31476             });
31477         }
31478         
31479         if(this.html.length){
31480             cn.push({
31481                 tag: 'p',
31482                 cls: 'masonry-brick-text',
31483                 html: this.html
31484             });
31485         }  
31486         if (!this.title.length && !this.html.length) {
31487             cfg.cn[0].cls += ' hide';
31488         }
31489         
31490         if(this.bgimage.length){
31491             cfg.cn.push({
31492                 tag: 'img',
31493                 cls: 'masonry-brick-image-view',
31494                 src: this.bgimage
31495             });
31496         }
31497         
31498         if(this.videourl.length){
31499             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31500             // youtube support only?
31501             cfg.cn.push({
31502                 tag: 'iframe',
31503                 cls: 'masonry-brick-image-view',
31504                 src: vurl,
31505                 frameborder : 0,
31506                 allowfullscreen : true
31507             });
31508             
31509             
31510         }
31511         
31512         cfg.cn.push({
31513             tag: 'div',
31514             cls: 'masonry-brick-mask'
31515         });
31516         
31517         return cfg;
31518         
31519     },
31520     
31521     getSplitAutoCreate : function()
31522     {
31523         var cls = 'masonry-brick masonry-brick-split';
31524         
31525         if(this.href.length){
31526             cls += ' masonry-brick-link';
31527         }
31528         
31529         if(this.bgimage.length){
31530             cls += ' masonry-brick-image';
31531         }
31532         
31533         if(this.size){
31534             cls += ' masonry-' + this.size + '-brick';
31535         }
31536         
31537         switch (this.placetitle) {
31538             case 'center' :
31539                 cls += ' masonry-center-title';
31540                 break;
31541             case 'bottom' :
31542                 cls += ' masonry-bottom-title';
31543                 break;
31544             default:
31545                 if(!this.bgimage.length){
31546                     cls += ' masonry-center-title';
31547                 }
31548
31549                 if(this.bgimage.length){
31550                     cls += ' masonry-bottom-title';
31551                 }
31552                 break;
31553         }
31554         
31555         if(this.cls){
31556             cls += ' ' + this.cls;
31557         }
31558         
31559         var cfg = {
31560             tag: (this.href.length) ? 'a' : 'div',
31561             cls: cls,
31562             cn: [
31563                 {
31564                     tag: 'div',
31565                     cls: 'masonry-brick-split-head',
31566                     cn: [
31567                         {
31568                             tag: 'div',
31569                             cls: 'masonry-brick-paragraph',
31570                             cn: []
31571                         }
31572                     ]
31573                 },
31574                 {
31575                     tag: 'div',
31576                     cls: 'masonry-brick-split-body',
31577                     cn: []
31578                 }
31579             ]
31580         };
31581         
31582         if(this.href.length){
31583             cfg.href = this.href;
31584         }
31585         
31586         if(this.title.length){
31587             cfg.cn[0].cn[0].cn.push({
31588                 tag: 'h4',
31589                 cls: 'masonry-brick-title',
31590                 html: this.title
31591             });
31592         }
31593         
31594         if(this.html.length){
31595             cfg.cn[1].cn.push({
31596                 tag: 'p',
31597                 cls: 'masonry-brick-text',
31598                 html: this.html
31599             });
31600         }
31601
31602         if(this.bgimage.length){
31603             cfg.cn[0].cn.push({
31604                 tag: 'img',
31605                 cls: 'masonry-brick-image-view',
31606                 src: this.bgimage
31607             });
31608         }
31609         
31610         if(this.videourl.length){
31611             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31612             // youtube support only?
31613             cfg.cn[0].cn.cn.push({
31614                 tag: 'iframe',
31615                 cls: 'masonry-brick-image-view',
31616                 src: vurl,
31617                 frameborder : 0,
31618                 allowfullscreen : true
31619             });
31620         }
31621         
31622         return cfg;
31623     },
31624     
31625     initEvents: function() 
31626     {
31627         switch (this.size) {
31628             case 'xs' :
31629                 this.x = 1;
31630                 this.y = 1;
31631                 break;
31632             case 'sm' :
31633                 this.x = 2;
31634                 this.y = 2;
31635                 break;
31636             case 'md' :
31637             case 'md-left' :
31638             case 'md-right' :
31639                 this.x = 3;
31640                 this.y = 3;
31641                 break;
31642             case 'tall' :
31643                 this.x = 2;
31644                 this.y = 3;
31645                 break;
31646             case 'wide' :
31647                 this.x = 3;
31648                 this.y = 2;
31649                 break;
31650             case 'wide-thin' :
31651                 this.x = 3;
31652                 this.y = 1;
31653                 break;
31654                         
31655             default :
31656                 break;
31657         }
31658         
31659         if(Roo.isTouch){
31660             this.el.on('touchstart', this.onTouchStart, this);
31661             this.el.on('touchmove', this.onTouchMove, this);
31662             this.el.on('touchend', this.onTouchEnd, this);
31663             this.el.on('contextmenu', this.onContextMenu, this);
31664         } else {
31665             this.el.on('mouseenter'  ,this.enter, this);
31666             this.el.on('mouseleave', this.leave, this);
31667             this.el.on('click', this.onClick, this);
31668         }
31669         
31670         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31671             this.parent().bricks.push(this);   
31672         }
31673         
31674     },
31675     
31676     onClick: function(e, el)
31677     {
31678         var time = this.endTimer - this.startTimer;
31679         
31680         if(Roo.isTouch){
31681             if(time > 1000){
31682                 e.preventDefault();
31683                 return;
31684             }
31685         }
31686         
31687         if(!this.preventDefault){
31688             return;
31689         }
31690         
31691         e.preventDefault();
31692         this.fireEvent('click', this);
31693     },
31694     
31695     enter: function(e, el)
31696     {
31697         e.preventDefault();
31698         
31699         if(!this.isFitContainer){
31700             return;
31701         }
31702         
31703         if(this.bgimage.length && this.html.length){
31704             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31705         }
31706     },
31707     
31708     leave: function(e, el)
31709     {
31710         e.preventDefault();
31711         
31712         if(!this.isFitContainer){
31713             return;
31714         }
31715         
31716         if(this.bgimage.length && this.html.length){
31717             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31718         }
31719     },
31720     
31721     onTouchStart: function(e, el)
31722     {
31723 //        e.preventDefault();
31724         
31725         this.touchmoved = false;
31726         
31727         if(!this.isFitContainer){
31728             return;
31729         }
31730         
31731         if(!this.bgimage.length || !this.html.length){
31732             return;
31733         }
31734         
31735         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31736         
31737         this.timer = new Date().getTime();
31738         
31739     },
31740     
31741     onTouchMove: function(e, el)
31742     {
31743         this.touchmoved = true;
31744     },
31745     
31746     onContextMenu : function(e,el)
31747     {
31748         e.preventDefault();
31749         e.stopPropagation();
31750         return false;
31751     },
31752     
31753     onTouchEnd: function(e, el)
31754     {
31755 //        e.preventDefault();
31756         
31757         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31758         
31759             this.leave(e,el);
31760             
31761             return;
31762         }
31763         
31764         if(!this.bgimage.length || !this.html.length){
31765             
31766             if(this.href.length){
31767                 window.location.href = this.href;
31768             }
31769             
31770             return;
31771         }
31772         
31773         if(!this.isFitContainer){
31774             return;
31775         }
31776         
31777         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31778         
31779         window.location.href = this.href;
31780     }
31781     
31782 });
31783
31784  
31785
31786  /*
31787  * - LGPL
31788  *
31789  * element
31790  * 
31791  */
31792
31793 /**
31794  * @class Roo.bootstrap.Brick
31795  * @extends Roo.bootstrap.Component
31796  * Bootstrap Brick class
31797  * 
31798  * @constructor
31799  * Create a new Brick
31800  * @param {Object} config The config object
31801  */
31802
31803 Roo.bootstrap.Brick = function(config){
31804     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31805     
31806     this.addEvents({
31807         // raw events
31808         /**
31809          * @event click
31810          * When a Brick is click
31811          * @param {Roo.bootstrap.Brick} this
31812          * @param {Roo.EventObject} e
31813          */
31814         "click" : true
31815     });
31816 };
31817
31818 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31819     
31820     /**
31821      * @cfg {String} title
31822      */   
31823     title : '',
31824     /**
31825      * @cfg {String} html
31826      */   
31827     html : '',
31828     /**
31829      * @cfg {String} bgimage
31830      */   
31831     bgimage : '',
31832     /**
31833      * @cfg {String} cls
31834      */   
31835     cls : '',
31836     /**
31837      * @cfg {String} href
31838      */   
31839     href : '',
31840     /**
31841      * @cfg {String} video
31842      */   
31843     video : '',
31844     /**
31845      * @cfg {Boolean} square
31846      */   
31847     square : true,
31848     
31849     getAutoCreate : function()
31850     {
31851         var cls = 'roo-brick';
31852         
31853         if(this.href.length){
31854             cls += ' roo-brick-link';
31855         }
31856         
31857         if(this.bgimage.length){
31858             cls += ' roo-brick-image';
31859         }
31860         
31861         if(!this.html.length && !this.bgimage.length){
31862             cls += ' roo-brick-center-title';
31863         }
31864         
31865         if(!this.html.length && this.bgimage.length){
31866             cls += ' roo-brick-bottom-title';
31867         }
31868         
31869         if(this.cls){
31870             cls += ' ' + this.cls;
31871         }
31872         
31873         var cfg = {
31874             tag: (this.href.length) ? 'a' : 'div',
31875             cls: cls,
31876             cn: [
31877                 {
31878                     tag: 'div',
31879                     cls: 'roo-brick-paragraph',
31880                     cn: []
31881                 }
31882             ]
31883         };
31884         
31885         if(this.href.length){
31886             cfg.href = this.href;
31887         }
31888         
31889         var cn = cfg.cn[0].cn;
31890         
31891         if(this.title.length){
31892             cn.push({
31893                 tag: 'h4',
31894                 cls: 'roo-brick-title',
31895                 html: this.title
31896             });
31897         }
31898         
31899         if(this.html.length){
31900             cn.push({
31901                 tag: 'p',
31902                 cls: 'roo-brick-text',
31903                 html: this.html
31904             });
31905         } else {
31906             cn.cls += ' hide';
31907         }
31908         
31909         if(this.bgimage.length){
31910             cfg.cn.push({
31911                 tag: 'img',
31912                 cls: 'roo-brick-image-view',
31913                 src: this.bgimage
31914             });
31915         }
31916         
31917         return cfg;
31918     },
31919     
31920     initEvents: function() 
31921     {
31922         if(this.title.length || this.html.length){
31923             this.el.on('mouseenter'  ,this.enter, this);
31924             this.el.on('mouseleave', this.leave, this);
31925         }
31926         
31927         
31928         Roo.EventManager.onWindowResize(this.resize, this); 
31929         
31930         this.resize();
31931     },
31932     
31933     resize : function()
31934     {
31935         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31936         
31937         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31938         
31939         if(this.bgimage.length){
31940             var image = this.el.select('.roo-brick-image-view', true).first();
31941             image.setWidth(paragraph.getWidth());
31942             image.setHeight(paragraph.getWidth());
31943             
31944             this.el.setHeight(paragraph.getWidth());
31945             
31946         }
31947         
31948     },
31949     
31950     enter: function(e, el)
31951     {
31952         e.preventDefault();
31953         
31954         if(this.bgimage.length){
31955             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31956             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31957         }
31958     },
31959     
31960     leave: function(e, el)
31961     {
31962         e.preventDefault();
31963         
31964         if(this.bgimage.length){
31965             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31966             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31967         }
31968     }
31969     
31970 });
31971
31972  
31973
31974  /*
31975  * - LGPL
31976  *
31977  * Input
31978  * 
31979  */
31980
31981 /**
31982  * @class Roo.bootstrap.NumberField
31983  * @extends Roo.bootstrap.Input
31984  * Bootstrap NumberField class
31985  * 
31986  * 
31987  * 
31988  * 
31989  * @constructor
31990  * Create a new NumberField
31991  * @param {Object} config The config object
31992  */
31993
31994 Roo.bootstrap.NumberField = function(config){
31995     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31996 };
31997
31998 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31999     
32000     /**
32001      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32002      */
32003     allowDecimals : true,
32004     /**
32005      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32006      */
32007     decimalSeparator : ".",
32008     /**
32009      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32010      */
32011     decimalPrecision : 2,
32012     /**
32013      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32014      */
32015     allowNegative : true,
32016     /**
32017      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32018      */
32019     minValue : Number.NEGATIVE_INFINITY,
32020     /**
32021      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32022      */
32023     maxValue : Number.MAX_VALUE,
32024     /**
32025      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32026      */
32027     minText : "The minimum value for this field is {0}",
32028     /**
32029      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32030      */
32031     maxText : "The maximum value for this field is {0}",
32032     /**
32033      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32034      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32035      */
32036     nanText : "{0} is not a valid number",
32037     /**
32038      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32039      */
32040     castInt : true,
32041
32042     // private
32043     initEvents : function()
32044     {   
32045         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32046         
32047         var allowed = "0123456789";
32048         
32049         if(this.allowDecimals){
32050             allowed += this.decimalSeparator;
32051         }
32052         
32053         if(this.allowNegative){
32054             allowed += "-";
32055         }
32056         
32057         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32058         
32059         var keyPress = function(e){
32060             
32061             var k = e.getKey();
32062             
32063             var c = e.getCharCode();
32064             
32065             if(
32066                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32067                     allowed.indexOf(String.fromCharCode(c)) === -1
32068             ){
32069                 e.stopEvent();
32070                 return;
32071             }
32072             
32073             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32074                 return;
32075             }
32076             
32077             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32078                 e.stopEvent();
32079             }
32080         };
32081         
32082         this.el.on("keypress", keyPress, this);
32083     },
32084     
32085     validateValue : function(value)
32086     {
32087         
32088         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32089             return false;
32090         }
32091         
32092         var num = this.parseValue(value);
32093         
32094         if(isNaN(num)){
32095             this.markInvalid(String.format(this.nanText, value));
32096             return false;
32097         }
32098         
32099         if(num < this.minValue){
32100             this.markInvalid(String.format(this.minText, this.minValue));
32101             return false;
32102         }
32103         
32104         if(num > this.maxValue){
32105             this.markInvalid(String.format(this.maxText, this.maxValue));
32106             return false;
32107         }
32108         
32109         return true;
32110     },
32111
32112     getValue : function()
32113     {
32114         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32115     },
32116
32117     parseValue : function(value)
32118     {
32119         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32120         return isNaN(value) ? '' : value;
32121     },
32122
32123     fixPrecision : function(value)
32124     {
32125         var nan = isNaN(value);
32126         
32127         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32128             return nan ? '' : value;
32129         }
32130         return parseFloat(value).toFixed(this.decimalPrecision);
32131     },
32132
32133     setValue : function(v)
32134     {
32135         v = this.fixPrecision(v);
32136         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32137     },
32138
32139     decimalPrecisionFcn : function(v)
32140     {
32141         return Math.floor(v);
32142     },
32143
32144     beforeBlur : function()
32145     {
32146         if(!this.castInt){
32147             return;
32148         }
32149         
32150         var v = this.parseValue(this.getRawValue());
32151         if(v){
32152             this.setValue(v);
32153         }
32154     }
32155     
32156 });
32157
32158  
32159
32160 /*
32161 * Licence: LGPL
32162 */
32163
32164 /**
32165  * @class Roo.bootstrap.DocumentSlider
32166  * @extends Roo.bootstrap.Component
32167  * Bootstrap DocumentSlider class
32168  * 
32169  * @constructor
32170  * Create a new DocumentViewer
32171  * @param {Object} config The config object
32172  */
32173
32174 Roo.bootstrap.DocumentSlider = function(config){
32175     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32176     
32177     this.files = [];
32178     
32179     this.addEvents({
32180         /**
32181          * @event initial
32182          * Fire after initEvent
32183          * @param {Roo.bootstrap.DocumentSlider} this
32184          */
32185         "initial" : true,
32186         /**
32187          * @event update
32188          * Fire after update
32189          * @param {Roo.bootstrap.DocumentSlider} this
32190          */
32191         "update" : true,
32192         /**
32193          * @event click
32194          * Fire after click
32195          * @param {Roo.bootstrap.DocumentSlider} this
32196          */
32197         "click" : true
32198     });
32199 };
32200
32201 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32202     
32203     files : false,
32204     
32205     indicator : 0,
32206     
32207     getAutoCreate : function()
32208     {
32209         var cfg = {
32210             tag : 'div',
32211             cls : 'roo-document-slider',
32212             cn : [
32213                 {
32214                     tag : 'div',
32215                     cls : 'roo-document-slider-header',
32216                     cn : [
32217                         {
32218                             tag : 'div',
32219                             cls : 'roo-document-slider-header-title'
32220                         }
32221                     ]
32222                 },
32223                 {
32224                     tag : 'div',
32225                     cls : 'roo-document-slider-body',
32226                     cn : [
32227                         {
32228                             tag : 'div',
32229                             cls : 'roo-document-slider-prev',
32230                             cn : [
32231                                 {
32232                                     tag : 'i',
32233                                     cls : 'fa fa-chevron-left'
32234                                 }
32235                             ]
32236                         },
32237                         {
32238                             tag : 'div',
32239                             cls : 'roo-document-slider-thumb',
32240                             cn : [
32241                                 {
32242                                     tag : 'img',
32243                                     cls : 'roo-document-slider-image'
32244                                 }
32245                             ]
32246                         },
32247                         {
32248                             tag : 'div',
32249                             cls : 'roo-document-slider-next',
32250                             cn : [
32251                                 {
32252                                     tag : 'i',
32253                                     cls : 'fa fa-chevron-right'
32254                                 }
32255                             ]
32256                         }
32257                     ]
32258                 }
32259             ]
32260         };
32261         
32262         return cfg;
32263     },
32264     
32265     initEvents : function()
32266     {
32267         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32268         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32269         
32270         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32271         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32272         
32273         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32274         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32275         
32276         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32277         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32278         
32279         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32280         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32281         
32282         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32283         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32284         
32285         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32286         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32287         
32288         this.thumbEl.on('click', this.onClick, this);
32289         
32290         this.prevIndicator.on('click', this.prev, this);
32291         
32292         this.nextIndicator.on('click', this.next, this);
32293         
32294     },
32295     
32296     initial : function()
32297     {
32298         if(this.files.length){
32299             this.indicator = 1;
32300             this.update()
32301         }
32302         
32303         this.fireEvent('initial', this);
32304     },
32305     
32306     update : function()
32307     {
32308         this.imageEl.attr('src', this.files[this.indicator - 1]);
32309         
32310         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32311         
32312         this.prevIndicator.show();
32313         
32314         if(this.indicator == 1){
32315             this.prevIndicator.hide();
32316         }
32317         
32318         this.nextIndicator.show();
32319         
32320         if(this.indicator == this.files.length){
32321             this.nextIndicator.hide();
32322         }
32323         
32324         this.thumbEl.scrollTo('top');
32325         
32326         this.fireEvent('update', this);
32327     },
32328     
32329     onClick : function(e)
32330     {
32331         e.preventDefault();
32332         
32333         this.fireEvent('click', this);
32334     },
32335     
32336     prev : function(e)
32337     {
32338         e.preventDefault();
32339         
32340         this.indicator = Math.max(1, this.indicator - 1);
32341         
32342         this.update();
32343     },
32344     
32345     next : function(e)
32346     {
32347         e.preventDefault();
32348         
32349         this.indicator = Math.min(this.files.length, this.indicator + 1);
32350         
32351         this.update();
32352     }
32353 });
32354 /*
32355  * - LGPL
32356  *
32357  * RadioSet
32358  *
32359  *
32360  */
32361
32362 /**
32363  * @class Roo.bootstrap.RadioSet
32364  * @extends Roo.bootstrap.Input
32365  * Bootstrap RadioSet class
32366  * @cfg {String} indicatorpos (left|right) default left
32367  * @cfg {Boolean} inline (true|false) inline the element (default true)
32368  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32369  * @constructor
32370  * Create a new RadioSet
32371  * @param {Object} config The config object
32372  */
32373
32374 Roo.bootstrap.RadioSet = function(config){
32375     
32376     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
32377     
32378     this.radioes = [];
32379     
32380     Roo.bootstrap.RadioSet.register(this);
32381     
32382     this.addEvents({
32383         /**
32384         * @event check
32385         * Fires when the element is checked or unchecked.
32386         * @param {Roo.bootstrap.RadioSet} this This radio
32387         * @param {Roo.bootstrap.Radio} item The checked item
32388         */
32389        check : true
32390     });
32391     
32392 };
32393
32394 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32395
32396     radioes : false,
32397     
32398     inline : true,
32399     
32400     weight : '',
32401     
32402     indicatorpos : 'left',
32403     
32404     getAutoCreate : function()
32405     {
32406         var label = {
32407             tag : 'label',
32408             cls : 'roo-radio-set-label',
32409             cn : [
32410                 {
32411                     tag : 'span',
32412                     html : this.fieldLabel
32413                 }
32414             ]
32415         };
32416         
32417         if(this.indicatorpos == 'left'){
32418             label.cn.unshift({
32419                 tag : 'i',
32420                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32421                 tooltip : 'This field is required'
32422             });
32423         } else {
32424             label.cn.push({
32425                 tag : 'i',
32426                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32427                 tooltip : 'This field is required'
32428             });
32429         }
32430         
32431         var items = {
32432             tag : 'div',
32433             cls : 'roo-radio-set-items'
32434         };
32435         
32436         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32437         
32438         if (align === 'left' && this.fieldLabel.length) {
32439             
32440             items = {
32441                 cls : "roo-radio-set-right", 
32442                 cn: [
32443                     items
32444                 ]
32445             };
32446             
32447             if(this.labelWidth > 12){
32448                 label.style = "width: " + this.labelWidth + 'px';
32449             }
32450             
32451             if(this.labelWidth < 13 && this.labelmd == 0){
32452                 this.labelmd = this.labelWidth;
32453             }
32454             
32455             if(this.labellg > 0){
32456                 label.cls += ' col-lg-' + this.labellg;
32457                 items.cls += ' col-lg-' + (12 - this.labellg);
32458             }
32459             
32460             if(this.labelmd > 0){
32461                 label.cls += ' col-md-' + this.labelmd;
32462                 items.cls += ' col-md-' + (12 - this.labelmd);
32463             }
32464             
32465             if(this.labelsm > 0){
32466                 label.cls += ' col-sm-' + this.labelsm;
32467                 items.cls += ' col-sm-' + (12 - this.labelsm);
32468             }
32469             
32470             if(this.labelxs > 0){
32471                 label.cls += ' col-xs-' + this.labelxs;
32472                 items.cls += ' col-xs-' + (12 - this.labelxs);
32473             }
32474         }
32475         
32476         var cfg = {
32477             tag : 'div',
32478             cls : 'roo-radio-set',
32479             cn : [
32480                 {
32481                     tag : 'input',
32482                     cls : 'roo-radio-set-input',
32483                     type : 'text',
32484                     name : this.name,
32485                     value : this.value ? this.value :  ''
32486                 },
32487                 label,
32488                 items
32489             ]
32490         };
32491         
32492         if(this.weight.length){
32493             cfg.cls += ' roo-radio-' + this.weight;
32494         }
32495         
32496         if(this.inline) {
32497             cfg.cls += ' roo-radio-set-inline';
32498         }
32499         
32500         return cfg;
32501         
32502     },
32503
32504     initEvents : function()
32505     {
32506         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32507         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32508         
32509         if(!this.fieldLabel.length){
32510             this.labelEl.hide();
32511         }
32512         
32513         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32514         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32515         
32516         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
32517         this.indicatorEl().hide();
32518         
32519         this.originalValue = this.getValue();
32520         
32521     },
32522     
32523     inputEl: function ()
32524     {
32525         return this.el.select('.roo-radio-set-input', true).first();
32526     },
32527     
32528     getChildContainer : function()
32529     {
32530         return this.itemsEl;
32531     },
32532     
32533     register : function(item)
32534     {
32535         this.radioes.push(item);
32536         
32537     },
32538     
32539     validate : function()
32540     {   
32541         var valid = false;
32542         
32543         Roo.each(this.radioes, function(i){
32544             if(!i.checked){
32545                 return;
32546             }
32547             
32548             valid = true;
32549             return false;
32550         });
32551         
32552         if(this.disabled || this.allowBlank || valid){
32553             this.markValid();
32554             return true;
32555         }
32556         
32557         this.markInvalid();
32558         return false;
32559         
32560     },
32561     
32562     markValid : function()
32563     {
32564         if(this.labelEl.isVisible(true)){
32565             this.indicatorEl().hide();
32566         }
32567         
32568         this.el.removeClass([this.invalidClass, this.validClass]);
32569         this.el.addClass(this.validClass);
32570         
32571         this.fireEvent('valid', this);
32572     },
32573     
32574     markInvalid : function(msg)
32575     {
32576         if(this.allowBlank || this.disabled){
32577             return;
32578         }
32579         
32580         if(this.labelEl.isVisible(true)){
32581             this.indicatorEl().show();
32582         }
32583         
32584         this.el.removeClass([this.invalidClass, this.validClass]);
32585         this.el.addClass(this.invalidClass);
32586         
32587         this.fireEvent('invalid', this, msg);
32588         
32589     },
32590     
32591     setValue : function(v, suppressEvent)
32592     {   
32593         Roo.each(this.radioes, function(i){
32594             
32595             i.checked = false;
32596             i.el.removeClass('checked');
32597             
32598             if(i.value === v || i.value.toString() === v.toString()){
32599                 i.checked = true;
32600                 i.el.addClass('checked');
32601                 
32602                 if(suppressEvent !== true){
32603                     this.fireEvent('check', this, i);
32604                 }
32605             }
32606             
32607         }, this);
32608         
32609         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32610         
32611     },
32612     
32613     clearInvalid : function(){
32614         
32615         if(!this.el || this.preventMark){
32616             return;
32617         }
32618         
32619         if(this.labelEl.isVisible(true)){
32620             this.indicatorEl().hide();
32621         }
32622         
32623         this.el.removeClass([this.invalidClass]);
32624         
32625         this.fireEvent('valid', this);
32626     }
32627     
32628 });
32629
32630 Roo.apply(Roo.bootstrap.RadioSet, {
32631     
32632     groups: {},
32633     
32634     register : function(set)
32635     {
32636         this.groups[set.name] = set;
32637     },
32638     
32639     get: function(name) 
32640     {
32641         if (typeof(this.groups[name]) == 'undefined') {
32642             return false;
32643         }
32644         
32645         return this.groups[name] ;
32646     }
32647     
32648 });
32649 /*
32650  * Based on:
32651  * Ext JS Library 1.1.1
32652  * Copyright(c) 2006-2007, Ext JS, LLC.
32653  *
32654  * Originally Released Under LGPL - original licence link has changed is not relivant.
32655  *
32656  * Fork - LGPL
32657  * <script type="text/javascript">
32658  */
32659
32660
32661 /**
32662  * @class Roo.bootstrap.SplitBar
32663  * @extends Roo.util.Observable
32664  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32665  * <br><br>
32666  * Usage:
32667  * <pre><code>
32668 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32669                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32670 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32671 split.minSize = 100;
32672 split.maxSize = 600;
32673 split.animate = true;
32674 split.on('moved', splitterMoved);
32675 </code></pre>
32676  * @constructor
32677  * Create a new SplitBar
32678  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32679  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32680  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32681  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32682                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32683                         position of the SplitBar).
32684  */
32685 Roo.bootstrap.SplitBar = function(cfg){
32686     
32687     /** @private */
32688     
32689     //{
32690     //  dragElement : elm
32691     //  resizingElement: el,
32692         // optional..
32693     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32694     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32695         // existingProxy ???
32696     //}
32697     
32698     this.el = Roo.get(cfg.dragElement, true);
32699     this.el.dom.unselectable = "on";
32700     /** @private */
32701     this.resizingEl = Roo.get(cfg.resizingElement, true);
32702
32703     /**
32704      * @private
32705      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32706      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32707      * @type Number
32708      */
32709     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32710     
32711     /**
32712      * The minimum size of the resizing element. (Defaults to 0)
32713      * @type Number
32714      */
32715     this.minSize = 0;
32716     
32717     /**
32718      * The maximum size of the resizing element. (Defaults to 2000)
32719      * @type Number
32720      */
32721     this.maxSize = 2000;
32722     
32723     /**
32724      * Whether to animate the transition to the new size
32725      * @type Boolean
32726      */
32727     this.animate = false;
32728     
32729     /**
32730      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32731      * @type Boolean
32732      */
32733     this.useShim = false;
32734     
32735     /** @private */
32736     this.shim = null;
32737     
32738     if(!cfg.existingProxy){
32739         /** @private */
32740         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32741     }else{
32742         this.proxy = Roo.get(cfg.existingProxy).dom;
32743     }
32744     /** @private */
32745     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32746     
32747     /** @private */
32748     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32749     
32750     /** @private */
32751     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32752     
32753     /** @private */
32754     this.dragSpecs = {};
32755     
32756     /**
32757      * @private The adapter to use to positon and resize elements
32758      */
32759     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32760     this.adapter.init(this);
32761     
32762     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32763         /** @private */
32764         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32765         this.el.addClass("roo-splitbar-h");
32766     }else{
32767         /** @private */
32768         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32769         this.el.addClass("roo-splitbar-v");
32770     }
32771     
32772     this.addEvents({
32773         /**
32774          * @event resize
32775          * Fires when the splitter is moved (alias for {@link #event-moved})
32776          * @param {Roo.bootstrap.SplitBar} this
32777          * @param {Number} newSize the new width or height
32778          */
32779         "resize" : true,
32780         /**
32781          * @event moved
32782          * Fires when the splitter is moved
32783          * @param {Roo.bootstrap.SplitBar} this
32784          * @param {Number} newSize the new width or height
32785          */
32786         "moved" : true,
32787         /**
32788          * @event beforeresize
32789          * Fires before the splitter is dragged
32790          * @param {Roo.bootstrap.SplitBar} this
32791          */
32792         "beforeresize" : true,
32793
32794         "beforeapply" : true
32795     });
32796
32797     Roo.util.Observable.call(this);
32798 };
32799
32800 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32801     onStartProxyDrag : function(x, y){
32802         this.fireEvent("beforeresize", this);
32803         if(!this.overlay){
32804             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32805             o.unselectable();
32806             o.enableDisplayMode("block");
32807             // all splitbars share the same overlay
32808             Roo.bootstrap.SplitBar.prototype.overlay = o;
32809         }
32810         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32811         this.overlay.show();
32812         Roo.get(this.proxy).setDisplayed("block");
32813         var size = this.adapter.getElementSize(this);
32814         this.activeMinSize = this.getMinimumSize();;
32815         this.activeMaxSize = this.getMaximumSize();;
32816         var c1 = size - this.activeMinSize;
32817         var c2 = Math.max(this.activeMaxSize - size, 0);
32818         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32819             this.dd.resetConstraints();
32820             this.dd.setXConstraint(
32821                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32822                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32823             );
32824             this.dd.setYConstraint(0, 0);
32825         }else{
32826             this.dd.resetConstraints();
32827             this.dd.setXConstraint(0, 0);
32828             this.dd.setYConstraint(
32829                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32830                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32831             );
32832          }
32833         this.dragSpecs.startSize = size;
32834         this.dragSpecs.startPoint = [x, y];
32835         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32836     },
32837     
32838     /** 
32839      * @private Called after the drag operation by the DDProxy
32840      */
32841     onEndProxyDrag : function(e){
32842         Roo.get(this.proxy).setDisplayed(false);
32843         var endPoint = Roo.lib.Event.getXY(e);
32844         if(this.overlay){
32845             this.overlay.hide();
32846         }
32847         var newSize;
32848         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32849             newSize = this.dragSpecs.startSize + 
32850                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32851                     endPoint[0] - this.dragSpecs.startPoint[0] :
32852                     this.dragSpecs.startPoint[0] - endPoint[0]
32853                 );
32854         }else{
32855             newSize = this.dragSpecs.startSize + 
32856                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32857                     endPoint[1] - this.dragSpecs.startPoint[1] :
32858                     this.dragSpecs.startPoint[1] - endPoint[1]
32859                 );
32860         }
32861         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32862         if(newSize != this.dragSpecs.startSize){
32863             if(this.fireEvent('beforeapply', this, newSize) !== false){
32864                 this.adapter.setElementSize(this, newSize);
32865                 this.fireEvent("moved", this, newSize);
32866                 this.fireEvent("resize", this, newSize);
32867             }
32868         }
32869     },
32870     
32871     /**
32872      * Get the adapter this SplitBar uses
32873      * @return The adapter object
32874      */
32875     getAdapter : function(){
32876         return this.adapter;
32877     },
32878     
32879     /**
32880      * Set the adapter this SplitBar uses
32881      * @param {Object} adapter A SplitBar adapter object
32882      */
32883     setAdapter : function(adapter){
32884         this.adapter = adapter;
32885         this.adapter.init(this);
32886     },
32887     
32888     /**
32889      * Gets the minimum size for the resizing element
32890      * @return {Number} The minimum size
32891      */
32892     getMinimumSize : function(){
32893         return this.minSize;
32894     },
32895     
32896     /**
32897      * Sets the minimum size for the resizing element
32898      * @param {Number} minSize The minimum size
32899      */
32900     setMinimumSize : function(minSize){
32901         this.minSize = minSize;
32902     },
32903     
32904     /**
32905      * Gets the maximum size for the resizing element
32906      * @return {Number} The maximum size
32907      */
32908     getMaximumSize : function(){
32909         return this.maxSize;
32910     },
32911     
32912     /**
32913      * Sets the maximum size for the resizing element
32914      * @param {Number} maxSize The maximum size
32915      */
32916     setMaximumSize : function(maxSize){
32917         this.maxSize = maxSize;
32918     },
32919     
32920     /**
32921      * Sets the initialize size for the resizing element
32922      * @param {Number} size The initial size
32923      */
32924     setCurrentSize : function(size){
32925         var oldAnimate = this.animate;
32926         this.animate = false;
32927         this.adapter.setElementSize(this, size);
32928         this.animate = oldAnimate;
32929     },
32930     
32931     /**
32932      * Destroy this splitbar. 
32933      * @param {Boolean} removeEl True to remove the element
32934      */
32935     destroy : function(removeEl){
32936         if(this.shim){
32937             this.shim.remove();
32938         }
32939         this.dd.unreg();
32940         this.proxy.parentNode.removeChild(this.proxy);
32941         if(removeEl){
32942             this.el.remove();
32943         }
32944     }
32945 });
32946
32947 /**
32948  * @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.
32949  */
32950 Roo.bootstrap.SplitBar.createProxy = function(dir){
32951     var proxy = new Roo.Element(document.createElement("div"));
32952     proxy.unselectable();
32953     var cls = 'roo-splitbar-proxy';
32954     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32955     document.body.appendChild(proxy.dom);
32956     return proxy.dom;
32957 };
32958
32959 /** 
32960  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32961  * Default Adapter. It assumes the splitter and resizing element are not positioned
32962  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32963  */
32964 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32965 };
32966
32967 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32968     // do nothing for now
32969     init : function(s){
32970     
32971     },
32972     /**
32973      * Called before drag operations to get the current size of the resizing element. 
32974      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32975      */
32976      getElementSize : function(s){
32977         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32978             return s.resizingEl.getWidth();
32979         }else{
32980             return s.resizingEl.getHeight();
32981         }
32982     },
32983     
32984     /**
32985      * Called after drag operations to set the size of the resizing element.
32986      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32987      * @param {Number} newSize The new size to set
32988      * @param {Function} onComplete A function to be invoked when resizing is complete
32989      */
32990     setElementSize : function(s, newSize, onComplete){
32991         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32992             if(!s.animate){
32993                 s.resizingEl.setWidth(newSize);
32994                 if(onComplete){
32995                     onComplete(s, newSize);
32996                 }
32997             }else{
32998                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32999             }
33000         }else{
33001             
33002             if(!s.animate){
33003                 s.resizingEl.setHeight(newSize);
33004                 if(onComplete){
33005                     onComplete(s, newSize);
33006                 }
33007             }else{
33008                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33009             }
33010         }
33011     }
33012 };
33013
33014 /** 
33015  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33016  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33017  * Adapter that  moves the splitter element to align with the resized sizing element. 
33018  * Used with an absolute positioned SplitBar.
33019  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33020  * document.body, make sure you assign an id to the body element.
33021  */
33022 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33023     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33024     this.container = Roo.get(container);
33025 };
33026
33027 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33028     init : function(s){
33029         this.basic.init(s);
33030     },
33031     
33032     getElementSize : function(s){
33033         return this.basic.getElementSize(s);
33034     },
33035     
33036     setElementSize : function(s, newSize, onComplete){
33037         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33038     },
33039     
33040     moveSplitter : function(s){
33041         var yes = Roo.bootstrap.SplitBar;
33042         switch(s.placement){
33043             case yes.LEFT:
33044                 s.el.setX(s.resizingEl.getRight());
33045                 break;
33046             case yes.RIGHT:
33047                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33048                 break;
33049             case yes.TOP:
33050                 s.el.setY(s.resizingEl.getBottom());
33051                 break;
33052             case yes.BOTTOM:
33053                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33054                 break;
33055         }
33056     }
33057 };
33058
33059 /**
33060  * Orientation constant - Create a vertical SplitBar
33061  * @static
33062  * @type Number
33063  */
33064 Roo.bootstrap.SplitBar.VERTICAL = 1;
33065
33066 /**
33067  * Orientation constant - Create a horizontal SplitBar
33068  * @static
33069  * @type Number
33070  */
33071 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33072
33073 /**
33074  * Placement constant - The resizing element is to the left of the splitter element
33075  * @static
33076  * @type Number
33077  */
33078 Roo.bootstrap.SplitBar.LEFT = 1;
33079
33080 /**
33081  * Placement constant - The resizing element is to the right of the splitter element
33082  * @static
33083  * @type Number
33084  */
33085 Roo.bootstrap.SplitBar.RIGHT = 2;
33086
33087 /**
33088  * Placement constant - The resizing element is positioned above the splitter element
33089  * @static
33090  * @type Number
33091  */
33092 Roo.bootstrap.SplitBar.TOP = 3;
33093
33094 /**
33095  * Placement constant - The resizing element is positioned under splitter element
33096  * @static
33097  * @type Number
33098  */
33099 Roo.bootstrap.SplitBar.BOTTOM = 4;
33100 Roo.namespace("Roo.bootstrap.layout");/*
33101  * Based on:
33102  * Ext JS Library 1.1.1
33103  * Copyright(c) 2006-2007, Ext JS, LLC.
33104  *
33105  * Originally Released Under LGPL - original licence link has changed is not relivant.
33106  *
33107  * Fork - LGPL
33108  * <script type="text/javascript">
33109  */
33110
33111 /**
33112  * @class Roo.bootstrap.layout.Manager
33113  * @extends Roo.bootstrap.Component
33114  * Base class for layout managers.
33115  */
33116 Roo.bootstrap.layout.Manager = function(config)
33117 {
33118     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33119
33120
33121
33122
33123
33124     /** false to disable window resize monitoring @type Boolean */
33125     this.monitorWindowResize = true;
33126     this.regions = {};
33127     this.addEvents({
33128         /**
33129          * @event layout
33130          * Fires when a layout is performed.
33131          * @param {Roo.LayoutManager} this
33132          */
33133         "layout" : true,
33134         /**
33135          * @event regionresized
33136          * Fires when the user resizes a region.
33137          * @param {Roo.LayoutRegion} region The resized region
33138          * @param {Number} newSize The new size (width for east/west, height for north/south)
33139          */
33140         "regionresized" : true,
33141         /**
33142          * @event regioncollapsed
33143          * Fires when a region is collapsed.
33144          * @param {Roo.LayoutRegion} region The collapsed region
33145          */
33146         "regioncollapsed" : true,
33147         /**
33148          * @event regionexpanded
33149          * Fires when a region is expanded.
33150          * @param {Roo.LayoutRegion} region The expanded region
33151          */
33152         "regionexpanded" : true
33153     });
33154     this.updating = false;
33155
33156     if (config.el) {
33157         this.el = Roo.get(config.el);
33158         this.initEvents();
33159     }
33160
33161 };
33162
33163 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33164
33165
33166     regions : null,
33167
33168     monitorWindowResize : true,
33169
33170
33171     updating : false,
33172
33173
33174     onRender : function(ct, position)
33175     {
33176         if(!this.el){
33177             this.el = Roo.get(ct);
33178             this.initEvents();
33179         }
33180         //this.fireEvent('render',this);
33181     },
33182
33183
33184     initEvents: function()
33185     {
33186
33187
33188         // ie scrollbar fix
33189         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33190             document.body.scroll = "no";
33191         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33192             this.el.position('relative');
33193         }
33194         this.id = this.el.id;
33195         this.el.addClass("roo-layout-container");
33196         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33197         if(this.el.dom != document.body ) {
33198             this.el.on('resize', this.layout,this);
33199             this.el.on('show', this.layout,this);
33200         }
33201
33202     },
33203
33204     /**
33205      * Returns true if this layout is currently being updated
33206      * @return {Boolean}
33207      */
33208     isUpdating : function(){
33209         return this.updating;
33210     },
33211
33212     /**
33213      * Suspend the LayoutManager from doing auto-layouts while
33214      * making multiple add or remove calls
33215      */
33216     beginUpdate : function(){
33217         this.updating = true;
33218     },
33219
33220     /**
33221      * Restore auto-layouts and optionally disable the manager from performing a layout
33222      * @param {Boolean} noLayout true to disable a layout update
33223      */
33224     endUpdate : function(noLayout){
33225         this.updating = false;
33226         if(!noLayout){
33227             this.layout();
33228         }
33229     },
33230
33231     layout: function(){
33232         // abstract...
33233     },
33234
33235     onRegionResized : function(region, newSize){
33236         this.fireEvent("regionresized", region, newSize);
33237         this.layout();
33238     },
33239
33240     onRegionCollapsed : function(region){
33241         this.fireEvent("regioncollapsed", region);
33242     },
33243
33244     onRegionExpanded : function(region){
33245         this.fireEvent("regionexpanded", region);
33246     },
33247
33248     /**
33249      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33250      * performs box-model adjustments.
33251      * @return {Object} The size as an object {width: (the width), height: (the height)}
33252      */
33253     getViewSize : function()
33254     {
33255         var size;
33256         if(this.el.dom != document.body){
33257             size = this.el.getSize();
33258         }else{
33259             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33260         }
33261         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33262         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33263         return size;
33264     },
33265
33266     /**
33267      * Returns the Element this layout is bound to.
33268      * @return {Roo.Element}
33269      */
33270     getEl : function(){
33271         return this.el;
33272     },
33273
33274     /**
33275      * Returns the specified region.
33276      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33277      * @return {Roo.LayoutRegion}
33278      */
33279     getRegion : function(target){
33280         return this.regions[target.toLowerCase()];
33281     },
33282
33283     onWindowResize : function(){
33284         if(this.monitorWindowResize){
33285             this.layout();
33286         }
33287     }
33288 });
33289 /*
33290  * Based on:
33291  * Ext JS Library 1.1.1
33292  * Copyright(c) 2006-2007, Ext JS, LLC.
33293  *
33294  * Originally Released Under LGPL - original licence link has changed is not relivant.
33295  *
33296  * Fork - LGPL
33297  * <script type="text/javascript">
33298  */
33299 /**
33300  * @class Roo.bootstrap.layout.Border
33301  * @extends Roo.bootstrap.layout.Manager
33302  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33303  * please see: examples/bootstrap/nested.html<br><br>
33304  
33305 <b>The container the layout is rendered into can be either the body element or any other element.
33306 If it is not the body element, the container needs to either be an absolute positioned element,
33307 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33308 the container size if it is not the body element.</b>
33309
33310 * @constructor
33311 * Create a new Border
33312 * @param {Object} config Configuration options
33313  */
33314 Roo.bootstrap.layout.Border = function(config){
33315     config = config || {};
33316     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
33317     
33318     
33319     
33320     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33321         if(config[region]){
33322             config[region].region = region;
33323             this.addRegion(config[region]);
33324         }
33325     },this);
33326     
33327 };
33328
33329 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
33330
33331 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
33332     /**
33333      * Creates and adds a new region if it doesn't already exist.
33334      * @param {String} target The target region key (north, south, east, west or center).
33335      * @param {Object} config The regions config object
33336      * @return {BorderLayoutRegion} The new region
33337      */
33338     addRegion : function(config)
33339     {
33340         if(!this.regions[config.region]){
33341             var r = this.factory(config);
33342             this.bindRegion(r);
33343         }
33344         return this.regions[config.region];
33345     },
33346
33347     // private (kinda)
33348     bindRegion : function(r){
33349         this.regions[r.config.region] = r;
33350         
33351         r.on("visibilitychange",    this.layout, this);
33352         r.on("paneladded",          this.layout, this);
33353         r.on("panelremoved",        this.layout, this);
33354         r.on("invalidated",         this.layout, this);
33355         r.on("resized",             this.onRegionResized, this);
33356         r.on("collapsed",           this.onRegionCollapsed, this);
33357         r.on("expanded",            this.onRegionExpanded, this);
33358     },
33359
33360     /**
33361      * Performs a layout update.
33362      */
33363     layout : function()
33364     {
33365         if(this.updating) {
33366             return;
33367         }
33368         
33369         // render all the rebions if they have not been done alreayd?
33370         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33371             if(this.regions[region] && !this.regions[region].bodyEl){
33372                 this.regions[region].onRender(this.el)
33373             }
33374         },this);
33375         
33376         var size = this.getViewSize();
33377         var w = size.width;
33378         var h = size.height;
33379         var centerW = w;
33380         var centerH = h;
33381         var centerY = 0;
33382         var centerX = 0;
33383         //var x = 0, y = 0;
33384
33385         var rs = this.regions;
33386         var north = rs["north"];
33387         var south = rs["south"]; 
33388         var west = rs["west"];
33389         var east = rs["east"];
33390         var center = rs["center"];
33391         //if(this.hideOnLayout){ // not supported anymore
33392             //c.el.setStyle("display", "none");
33393         //}
33394         if(north && north.isVisible()){
33395             var b = north.getBox();
33396             var m = north.getMargins();
33397             b.width = w - (m.left+m.right);
33398             b.x = m.left;
33399             b.y = m.top;
33400             centerY = b.height + b.y + m.bottom;
33401             centerH -= centerY;
33402             north.updateBox(this.safeBox(b));
33403         }
33404         if(south && south.isVisible()){
33405             var b = south.getBox();
33406             var m = south.getMargins();
33407             b.width = w - (m.left+m.right);
33408             b.x = m.left;
33409             var totalHeight = (b.height + m.top + m.bottom);
33410             b.y = h - totalHeight + m.top;
33411             centerH -= totalHeight;
33412             south.updateBox(this.safeBox(b));
33413         }
33414         if(west && west.isVisible()){
33415             var b = west.getBox();
33416             var m = west.getMargins();
33417             b.height = centerH - (m.top+m.bottom);
33418             b.x = m.left;
33419             b.y = centerY + m.top;
33420             var totalWidth = (b.width + m.left + m.right);
33421             centerX += totalWidth;
33422             centerW -= totalWidth;
33423             west.updateBox(this.safeBox(b));
33424         }
33425         if(east && east.isVisible()){
33426             var b = east.getBox();
33427             var m = east.getMargins();
33428             b.height = centerH - (m.top+m.bottom);
33429             var totalWidth = (b.width + m.left + m.right);
33430             b.x = w - totalWidth + m.left;
33431             b.y = centerY + m.top;
33432             centerW -= totalWidth;
33433             east.updateBox(this.safeBox(b));
33434         }
33435         if(center){
33436             var m = center.getMargins();
33437             var centerBox = {
33438                 x: centerX + m.left,
33439                 y: centerY + m.top,
33440                 width: centerW - (m.left+m.right),
33441                 height: centerH - (m.top+m.bottom)
33442             };
33443             //if(this.hideOnLayout){
33444                 //center.el.setStyle("display", "block");
33445             //}
33446             center.updateBox(this.safeBox(centerBox));
33447         }
33448         this.el.repaint();
33449         this.fireEvent("layout", this);
33450     },
33451
33452     // private
33453     safeBox : function(box){
33454         box.width = Math.max(0, box.width);
33455         box.height = Math.max(0, box.height);
33456         return box;
33457     },
33458
33459     /**
33460      * Adds a ContentPanel (or subclass) to this layout.
33461      * @param {String} target The target region key (north, south, east, west or center).
33462      * @param {Roo.ContentPanel} panel The panel to add
33463      * @return {Roo.ContentPanel} The added panel
33464      */
33465     add : function(target, panel){
33466          
33467         target = target.toLowerCase();
33468         return this.regions[target].add(panel);
33469     },
33470
33471     /**
33472      * Remove a ContentPanel (or subclass) to this layout.
33473      * @param {String} target The target region key (north, south, east, west or center).
33474      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33475      * @return {Roo.ContentPanel} The removed panel
33476      */
33477     remove : function(target, panel){
33478         target = target.toLowerCase();
33479         return this.regions[target].remove(panel);
33480     },
33481
33482     /**
33483      * Searches all regions for a panel with the specified id
33484      * @param {String} panelId
33485      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33486      */
33487     findPanel : function(panelId){
33488         var rs = this.regions;
33489         for(var target in rs){
33490             if(typeof rs[target] != "function"){
33491                 var p = rs[target].getPanel(panelId);
33492                 if(p){
33493                     return p;
33494                 }
33495             }
33496         }
33497         return null;
33498     },
33499
33500     /**
33501      * Searches all regions for a panel with the specified id and activates (shows) it.
33502      * @param {String/ContentPanel} panelId The panels id or the panel itself
33503      * @return {Roo.ContentPanel} The shown panel or null
33504      */
33505     showPanel : function(panelId) {
33506       var rs = this.regions;
33507       for(var target in rs){
33508          var r = rs[target];
33509          if(typeof r != "function"){
33510             if(r.hasPanel(panelId)){
33511                return r.showPanel(panelId);
33512             }
33513          }
33514       }
33515       return null;
33516    },
33517
33518    /**
33519      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33520      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33521      */
33522    /*
33523     restoreState : function(provider){
33524         if(!provider){
33525             provider = Roo.state.Manager;
33526         }
33527         var sm = new Roo.LayoutStateManager();
33528         sm.init(this, provider);
33529     },
33530 */
33531  
33532  
33533     /**
33534      * Adds a xtype elements to the layout.
33535      * <pre><code>
33536
33537 layout.addxtype({
33538        xtype : 'ContentPanel',
33539        region: 'west',
33540        items: [ .... ]
33541    }
33542 );
33543
33544 layout.addxtype({
33545         xtype : 'NestedLayoutPanel',
33546         region: 'west',
33547         layout: {
33548            center: { },
33549            west: { }   
33550         },
33551         items : [ ... list of content panels or nested layout panels.. ]
33552    }
33553 );
33554 </code></pre>
33555      * @param {Object} cfg Xtype definition of item to add.
33556      */
33557     addxtype : function(cfg)
33558     {
33559         // basically accepts a pannel...
33560         // can accept a layout region..!?!?
33561         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33562         
33563         
33564         // theory?  children can only be panels??
33565         
33566         //if (!cfg.xtype.match(/Panel$/)) {
33567         //    return false;
33568         //}
33569         var ret = false;
33570         
33571         if (typeof(cfg.region) == 'undefined') {
33572             Roo.log("Failed to add Panel, region was not set");
33573             Roo.log(cfg);
33574             return false;
33575         }
33576         var region = cfg.region;
33577         delete cfg.region;
33578         
33579           
33580         var xitems = [];
33581         if (cfg.items) {
33582             xitems = cfg.items;
33583             delete cfg.items;
33584         }
33585         var nb = false;
33586         
33587         switch(cfg.xtype) 
33588         {
33589             case 'Content':  // ContentPanel (el, cfg)
33590             case 'Scroll':  // ContentPanel (el, cfg)
33591             case 'View': 
33592                 cfg.autoCreate = true;
33593                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33594                 //} else {
33595                 //    var el = this.el.createChild();
33596                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33597                 //}
33598                 
33599                 this.add(region, ret);
33600                 break;
33601             
33602             /*
33603             case 'TreePanel': // our new panel!
33604                 cfg.el = this.el.createChild();
33605                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33606                 this.add(region, ret);
33607                 break;
33608             */
33609             
33610             case 'Nest': 
33611                 // create a new Layout (which is  a Border Layout...
33612                 
33613                 var clayout = cfg.layout;
33614                 clayout.el  = this.el.createChild();
33615                 clayout.items   = clayout.items  || [];
33616                 
33617                 delete cfg.layout;
33618                 
33619                 // replace this exitems with the clayout ones..
33620                 xitems = clayout.items;
33621                  
33622                 // force background off if it's in center...
33623                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33624                     cfg.background = false;
33625                 }
33626                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33627                 
33628                 
33629                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33630                 //console.log('adding nested layout panel '  + cfg.toSource());
33631                 this.add(region, ret);
33632                 nb = {}; /// find first...
33633                 break;
33634             
33635             case 'Grid':
33636                 
33637                 // needs grid and region
33638                 
33639                 //var el = this.getRegion(region).el.createChild();
33640                 /*
33641                  *var el = this.el.createChild();
33642                 // create the grid first...
33643                 cfg.grid.container = el;
33644                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33645                 */
33646                 
33647                 if (region == 'center' && this.active ) {
33648                     cfg.background = false;
33649                 }
33650                 
33651                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33652                 
33653                 this.add(region, ret);
33654                 /*
33655                 if (cfg.background) {
33656                     // render grid on panel activation (if panel background)
33657                     ret.on('activate', function(gp) {
33658                         if (!gp.grid.rendered) {
33659                     //        gp.grid.render(el);
33660                         }
33661                     });
33662                 } else {
33663                   //  cfg.grid.render(el);
33664                 }
33665                 */
33666                 break;
33667            
33668            
33669             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33670                 // it was the old xcomponent building that caused this before.
33671                 // espeically if border is the top element in the tree.
33672                 ret = this;
33673                 break; 
33674                 
33675                     
33676                 
33677                 
33678                 
33679             default:
33680                 /*
33681                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33682                     
33683                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33684                     this.add(region, ret);
33685                 } else {
33686                 */
33687                     Roo.log(cfg);
33688                     throw "Can not add '" + cfg.xtype + "' to Border";
33689                     return null;
33690              
33691                                 
33692              
33693         }
33694         this.beginUpdate();
33695         // add children..
33696         var region = '';
33697         var abn = {};
33698         Roo.each(xitems, function(i)  {
33699             region = nb && i.region ? i.region : false;
33700             
33701             var add = ret.addxtype(i);
33702            
33703             if (region) {
33704                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33705                 if (!i.background) {
33706                     abn[region] = nb[region] ;
33707                 }
33708             }
33709             
33710         });
33711         this.endUpdate();
33712
33713         // make the last non-background panel active..
33714         //if (nb) { Roo.log(abn); }
33715         if (nb) {
33716             
33717             for(var r in abn) {
33718                 region = this.getRegion(r);
33719                 if (region) {
33720                     // tried using nb[r], but it does not work..
33721                      
33722                     region.showPanel(abn[r]);
33723                    
33724                 }
33725             }
33726         }
33727         return ret;
33728         
33729     },
33730     
33731     
33732 // private
33733     factory : function(cfg)
33734     {
33735         
33736         var validRegions = Roo.bootstrap.layout.Border.regions;
33737
33738         var target = cfg.region;
33739         cfg.mgr = this;
33740         
33741         var r = Roo.bootstrap.layout;
33742         Roo.log(target);
33743         switch(target){
33744             case "north":
33745                 return new r.North(cfg);
33746             case "south":
33747                 return new r.South(cfg);
33748             case "east":
33749                 return new r.East(cfg);
33750             case "west":
33751                 return new r.West(cfg);
33752             case "center":
33753                 return new r.Center(cfg);
33754         }
33755         throw 'Layout region "'+target+'" not supported.';
33756     }
33757     
33758     
33759 });
33760  /*
33761  * Based on:
33762  * Ext JS Library 1.1.1
33763  * Copyright(c) 2006-2007, Ext JS, LLC.
33764  *
33765  * Originally Released Under LGPL - original licence link has changed is not relivant.
33766  *
33767  * Fork - LGPL
33768  * <script type="text/javascript">
33769  */
33770  
33771 /**
33772  * @class Roo.bootstrap.layout.Basic
33773  * @extends Roo.util.Observable
33774  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33775  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33776  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33777  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33778  * @cfg {string}   region  the region that it inhabits..
33779  * @cfg {bool}   skipConfig skip config?
33780  * 
33781
33782  */
33783 Roo.bootstrap.layout.Basic = function(config){
33784     
33785     this.mgr = config.mgr;
33786     
33787     this.position = config.region;
33788     
33789     var skipConfig = config.skipConfig;
33790     
33791     this.events = {
33792         /**
33793          * @scope Roo.BasicLayoutRegion
33794          */
33795         
33796         /**
33797          * @event beforeremove
33798          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33799          * @param {Roo.LayoutRegion} this
33800          * @param {Roo.ContentPanel} panel The panel
33801          * @param {Object} e The cancel event object
33802          */
33803         "beforeremove" : true,
33804         /**
33805          * @event invalidated
33806          * Fires when the layout for this region is changed.
33807          * @param {Roo.LayoutRegion} this
33808          */
33809         "invalidated" : true,
33810         /**
33811          * @event visibilitychange
33812          * Fires when this region is shown or hidden 
33813          * @param {Roo.LayoutRegion} this
33814          * @param {Boolean} visibility true or false
33815          */
33816         "visibilitychange" : true,
33817         /**
33818          * @event paneladded
33819          * Fires when a panel is added. 
33820          * @param {Roo.LayoutRegion} this
33821          * @param {Roo.ContentPanel} panel The panel
33822          */
33823         "paneladded" : true,
33824         /**
33825          * @event panelremoved
33826          * Fires when a panel is removed. 
33827          * @param {Roo.LayoutRegion} this
33828          * @param {Roo.ContentPanel} panel The panel
33829          */
33830         "panelremoved" : true,
33831         /**
33832          * @event beforecollapse
33833          * Fires when this region before collapse.
33834          * @param {Roo.LayoutRegion} this
33835          */
33836         "beforecollapse" : true,
33837         /**
33838          * @event collapsed
33839          * Fires when this region is collapsed.
33840          * @param {Roo.LayoutRegion} this
33841          */
33842         "collapsed" : true,
33843         /**
33844          * @event expanded
33845          * Fires when this region is expanded.
33846          * @param {Roo.LayoutRegion} this
33847          */
33848         "expanded" : true,
33849         /**
33850          * @event slideshow
33851          * Fires when this region is slid into view.
33852          * @param {Roo.LayoutRegion} this
33853          */
33854         "slideshow" : true,
33855         /**
33856          * @event slidehide
33857          * Fires when this region slides out of view. 
33858          * @param {Roo.LayoutRegion} this
33859          */
33860         "slidehide" : true,
33861         /**
33862          * @event panelactivated
33863          * Fires when a panel is activated. 
33864          * @param {Roo.LayoutRegion} this
33865          * @param {Roo.ContentPanel} panel The activated panel
33866          */
33867         "panelactivated" : true,
33868         /**
33869          * @event resized
33870          * Fires when the user resizes this region. 
33871          * @param {Roo.LayoutRegion} this
33872          * @param {Number} newSize The new size (width for east/west, height for north/south)
33873          */
33874         "resized" : true
33875     };
33876     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33877     this.panels = new Roo.util.MixedCollection();
33878     this.panels.getKey = this.getPanelId.createDelegate(this);
33879     this.box = null;
33880     this.activePanel = null;
33881     // ensure listeners are added...
33882     
33883     if (config.listeners || config.events) {
33884         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33885             listeners : config.listeners || {},
33886             events : config.events || {}
33887         });
33888     }
33889     
33890     if(skipConfig !== true){
33891         this.applyConfig(config);
33892     }
33893 };
33894
33895 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33896 {
33897     getPanelId : function(p){
33898         return p.getId();
33899     },
33900     
33901     applyConfig : function(config){
33902         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33903         this.config = config;
33904         
33905     },
33906     
33907     /**
33908      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33909      * the width, for horizontal (north, south) the height.
33910      * @param {Number} newSize The new width or height
33911      */
33912     resizeTo : function(newSize){
33913         var el = this.el ? this.el :
33914                  (this.activePanel ? this.activePanel.getEl() : null);
33915         if(el){
33916             switch(this.position){
33917                 case "east":
33918                 case "west":
33919                     el.setWidth(newSize);
33920                     this.fireEvent("resized", this, newSize);
33921                 break;
33922                 case "north":
33923                 case "south":
33924                     el.setHeight(newSize);
33925                     this.fireEvent("resized", this, newSize);
33926                 break;                
33927             }
33928         }
33929     },
33930     
33931     getBox : function(){
33932         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33933     },
33934     
33935     getMargins : function(){
33936         return this.margins;
33937     },
33938     
33939     updateBox : function(box){
33940         this.box = box;
33941         var el = this.activePanel.getEl();
33942         el.dom.style.left = box.x + "px";
33943         el.dom.style.top = box.y + "px";
33944         this.activePanel.setSize(box.width, box.height);
33945     },
33946     
33947     /**
33948      * Returns the container element for this region.
33949      * @return {Roo.Element}
33950      */
33951     getEl : function(){
33952         return this.activePanel;
33953     },
33954     
33955     /**
33956      * Returns true if this region is currently visible.
33957      * @return {Boolean}
33958      */
33959     isVisible : function(){
33960         return this.activePanel ? true : false;
33961     },
33962     
33963     setActivePanel : function(panel){
33964         panel = this.getPanel(panel);
33965         if(this.activePanel && this.activePanel != panel){
33966             this.activePanel.setActiveState(false);
33967             this.activePanel.getEl().setLeftTop(-10000,-10000);
33968         }
33969         this.activePanel = panel;
33970         panel.setActiveState(true);
33971         if(this.box){
33972             panel.setSize(this.box.width, this.box.height);
33973         }
33974         this.fireEvent("panelactivated", this, panel);
33975         this.fireEvent("invalidated");
33976     },
33977     
33978     /**
33979      * Show the specified panel.
33980      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33981      * @return {Roo.ContentPanel} The shown panel or null
33982      */
33983     showPanel : function(panel){
33984         panel = this.getPanel(panel);
33985         if(panel){
33986             this.setActivePanel(panel);
33987         }
33988         return panel;
33989     },
33990     
33991     /**
33992      * Get the active panel for this region.
33993      * @return {Roo.ContentPanel} The active panel or null
33994      */
33995     getActivePanel : function(){
33996         return this.activePanel;
33997     },
33998     
33999     /**
34000      * Add the passed ContentPanel(s)
34001      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34002      * @return {Roo.ContentPanel} The panel added (if only one was added)
34003      */
34004     add : function(panel){
34005         if(arguments.length > 1){
34006             for(var i = 0, len = arguments.length; i < len; i++) {
34007                 this.add(arguments[i]);
34008             }
34009             return null;
34010         }
34011         if(this.hasPanel(panel)){
34012             this.showPanel(panel);
34013             return panel;
34014         }
34015         var el = panel.getEl();
34016         if(el.dom.parentNode != this.mgr.el.dom){
34017             this.mgr.el.dom.appendChild(el.dom);
34018         }
34019         if(panel.setRegion){
34020             panel.setRegion(this);
34021         }
34022         this.panels.add(panel);
34023         el.setStyle("position", "absolute");
34024         if(!panel.background){
34025             this.setActivePanel(panel);
34026             if(this.config.initialSize && this.panels.getCount()==1){
34027                 this.resizeTo(this.config.initialSize);
34028             }
34029         }
34030         this.fireEvent("paneladded", this, panel);
34031         return panel;
34032     },
34033     
34034     /**
34035      * Returns true if the panel is in this region.
34036      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34037      * @return {Boolean}
34038      */
34039     hasPanel : function(panel){
34040         if(typeof panel == "object"){ // must be panel obj
34041             panel = panel.getId();
34042         }
34043         return this.getPanel(panel) ? true : false;
34044     },
34045     
34046     /**
34047      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34048      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34049      * @param {Boolean} preservePanel Overrides the config preservePanel option
34050      * @return {Roo.ContentPanel} The panel that was removed
34051      */
34052     remove : function(panel, preservePanel){
34053         panel = this.getPanel(panel);
34054         if(!panel){
34055             return null;
34056         }
34057         var e = {};
34058         this.fireEvent("beforeremove", this, panel, e);
34059         if(e.cancel === true){
34060             return null;
34061         }
34062         var panelId = panel.getId();
34063         this.panels.removeKey(panelId);
34064         return panel;
34065     },
34066     
34067     /**
34068      * Returns the panel specified or null if it's not in this region.
34069      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34070      * @return {Roo.ContentPanel}
34071      */
34072     getPanel : function(id){
34073         if(typeof id == "object"){ // must be panel obj
34074             return id;
34075         }
34076         return this.panels.get(id);
34077     },
34078     
34079     /**
34080      * Returns this regions position (north/south/east/west/center).
34081      * @return {String} 
34082      */
34083     getPosition: function(){
34084         return this.position;    
34085     }
34086 });/*
34087  * Based on:
34088  * Ext JS Library 1.1.1
34089  * Copyright(c) 2006-2007, Ext JS, LLC.
34090  *
34091  * Originally Released Under LGPL - original licence link has changed is not relivant.
34092  *
34093  * Fork - LGPL
34094  * <script type="text/javascript">
34095  */
34096  
34097 /**
34098  * @class Roo.bootstrap.layout.Region
34099  * @extends Roo.bootstrap.layout.Basic
34100  * This class represents a region in a layout manager.
34101  
34102  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34103  * @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})
34104  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34105  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34106  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34107  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34108  * @cfg {String}    title           The title for the region (overrides panel titles)
34109  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34110  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34111  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34112  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34113  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34114  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34115  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34116  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34117  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34118  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34119
34120  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34121  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34122  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34123  * @cfg {Number}    width           For East/West panels
34124  * @cfg {Number}    height          For North/South panels
34125  * @cfg {Boolean}   split           To show the splitter
34126  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34127  * 
34128  * @cfg {string}   cls             Extra CSS classes to add to region
34129  * 
34130  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34131  * @cfg {string}   region  the region that it inhabits..
34132  *
34133
34134  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34135  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34136
34137  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34138  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34139  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34140  */
34141 Roo.bootstrap.layout.Region = function(config)
34142 {
34143     this.applyConfig(config);
34144
34145     var mgr = config.mgr;
34146     var pos = config.region;
34147     config.skipConfig = true;
34148     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34149     
34150     if (mgr.el) {
34151         this.onRender(mgr.el);   
34152     }
34153      
34154     this.visible = true;
34155     this.collapsed = false;
34156     this.unrendered_panels = [];
34157 };
34158
34159 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34160
34161     position: '', // set by wrapper (eg. north/south etc..)
34162     unrendered_panels : null,  // unrendered panels.
34163     createBody : function(){
34164         /** This region's body element 
34165         * @type Roo.Element */
34166         this.bodyEl = this.el.createChild({
34167                 tag: "div",
34168                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34169         });
34170     },
34171
34172     onRender: function(ctr, pos)
34173     {
34174         var dh = Roo.DomHelper;
34175         /** This region's container element 
34176         * @type Roo.Element */
34177         this.el = dh.append(ctr.dom, {
34178                 tag: "div",
34179                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34180             }, true);
34181         /** This region's title element 
34182         * @type Roo.Element */
34183     
34184         this.titleEl = dh.append(this.el.dom,
34185             {
34186                     tag: "div",
34187                     unselectable: "on",
34188                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34189                     children:[
34190                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34191                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34192                     ]}, true);
34193         
34194         this.titleEl.enableDisplayMode();
34195         /** This region's title text element 
34196         * @type HTMLElement */
34197         this.titleTextEl = this.titleEl.dom.firstChild;
34198         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34199         /*
34200         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34201         this.closeBtn.enableDisplayMode();
34202         this.closeBtn.on("click", this.closeClicked, this);
34203         this.closeBtn.hide();
34204     */
34205         this.createBody(this.config);
34206         if(this.config.hideWhenEmpty){
34207             this.hide();
34208             this.on("paneladded", this.validateVisibility, this);
34209             this.on("panelremoved", this.validateVisibility, this);
34210         }
34211         if(this.autoScroll){
34212             this.bodyEl.setStyle("overflow", "auto");
34213         }else{
34214             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34215         }
34216         //if(c.titlebar !== false){
34217             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34218                 this.titleEl.hide();
34219             }else{
34220                 this.titleEl.show();
34221                 if(this.config.title){
34222                     this.titleTextEl.innerHTML = this.config.title;
34223                 }
34224             }
34225         //}
34226         if(this.config.collapsed){
34227             this.collapse(true);
34228         }
34229         if(this.config.hidden){
34230             this.hide();
34231         }
34232         
34233         if (this.unrendered_panels && this.unrendered_panels.length) {
34234             for (var i =0;i< this.unrendered_panels.length; i++) {
34235                 this.add(this.unrendered_panels[i]);
34236             }
34237             this.unrendered_panels = null;
34238             
34239         }
34240         
34241     },
34242     
34243     applyConfig : function(c)
34244     {
34245         /*
34246          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34247             var dh = Roo.DomHelper;
34248             if(c.titlebar !== false){
34249                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34250                 this.collapseBtn.on("click", this.collapse, this);
34251                 this.collapseBtn.enableDisplayMode();
34252                 /*
34253                 if(c.showPin === true || this.showPin){
34254                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34255                     this.stickBtn.enableDisplayMode();
34256                     this.stickBtn.on("click", this.expand, this);
34257                     this.stickBtn.hide();
34258                 }
34259                 
34260             }
34261             */
34262             /** This region's collapsed element
34263             * @type Roo.Element */
34264             /*
34265              *
34266             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34267                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34268             ]}, true);
34269             
34270             if(c.floatable !== false){
34271                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34272                this.collapsedEl.on("click", this.collapseClick, this);
34273             }
34274
34275             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34276                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34277                    id: "message", unselectable: "on", style:{"float":"left"}});
34278                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34279              }
34280             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34281             this.expandBtn.on("click", this.expand, this);
34282             
34283         }
34284         
34285         if(this.collapseBtn){
34286             this.collapseBtn.setVisible(c.collapsible == true);
34287         }
34288         
34289         this.cmargins = c.cmargins || this.cmargins ||
34290                          (this.position == "west" || this.position == "east" ?
34291                              {top: 0, left: 2, right:2, bottom: 0} :
34292                              {top: 2, left: 0, right:0, bottom: 2});
34293         */
34294         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34295         
34296         
34297         this.bottomTabs = c.tabPosition != "top";
34298         
34299         this.autoScroll = c.autoScroll || false;
34300         
34301         
34302        
34303         
34304         this.duration = c.duration || .30;
34305         this.slideDuration = c.slideDuration || .45;
34306         this.config = c;
34307        
34308     },
34309     /**
34310      * Returns true if this region is currently visible.
34311      * @return {Boolean}
34312      */
34313     isVisible : function(){
34314         return this.visible;
34315     },
34316
34317     /**
34318      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34319      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34320      */
34321     //setCollapsedTitle : function(title){
34322     //    title = title || "&#160;";
34323      //   if(this.collapsedTitleTextEl){
34324       //      this.collapsedTitleTextEl.innerHTML = title;
34325        // }
34326     //},
34327
34328     getBox : function(){
34329         var b;
34330       //  if(!this.collapsed){
34331             b = this.el.getBox(false, true);
34332        // }else{
34333           //  b = this.collapsedEl.getBox(false, true);
34334         //}
34335         return b;
34336     },
34337
34338     getMargins : function(){
34339         return this.margins;
34340         //return this.collapsed ? this.cmargins : this.margins;
34341     },
34342 /*
34343     highlight : function(){
34344         this.el.addClass("x-layout-panel-dragover");
34345     },
34346
34347     unhighlight : function(){
34348         this.el.removeClass("x-layout-panel-dragover");
34349     },
34350 */
34351     updateBox : function(box)
34352     {
34353         if (!this.bodyEl) {
34354             return; // not rendered yet..
34355         }
34356         
34357         this.box = box;
34358         if(!this.collapsed){
34359             this.el.dom.style.left = box.x + "px";
34360             this.el.dom.style.top = box.y + "px";
34361             this.updateBody(box.width, box.height);
34362         }else{
34363             this.collapsedEl.dom.style.left = box.x + "px";
34364             this.collapsedEl.dom.style.top = box.y + "px";
34365             this.collapsedEl.setSize(box.width, box.height);
34366         }
34367         if(this.tabs){
34368             this.tabs.autoSizeTabs();
34369         }
34370     },
34371
34372     updateBody : function(w, h)
34373     {
34374         if(w !== null){
34375             this.el.setWidth(w);
34376             w -= this.el.getBorderWidth("rl");
34377             if(this.config.adjustments){
34378                 w += this.config.adjustments[0];
34379             }
34380         }
34381         if(h !== null && h > 0){
34382             this.el.setHeight(h);
34383             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34384             h -= this.el.getBorderWidth("tb");
34385             if(this.config.adjustments){
34386                 h += this.config.adjustments[1];
34387             }
34388             this.bodyEl.setHeight(h);
34389             if(this.tabs){
34390                 h = this.tabs.syncHeight(h);
34391             }
34392         }
34393         if(this.panelSize){
34394             w = w !== null ? w : this.panelSize.width;
34395             h = h !== null ? h : this.panelSize.height;
34396         }
34397         if(this.activePanel){
34398             var el = this.activePanel.getEl();
34399             w = w !== null ? w : el.getWidth();
34400             h = h !== null ? h : el.getHeight();
34401             this.panelSize = {width: w, height: h};
34402             this.activePanel.setSize(w, h);
34403         }
34404         if(Roo.isIE && this.tabs){
34405             this.tabs.el.repaint();
34406         }
34407     },
34408
34409     /**
34410      * Returns the container element for this region.
34411      * @return {Roo.Element}
34412      */
34413     getEl : function(){
34414         return this.el;
34415     },
34416
34417     /**
34418      * Hides this region.
34419      */
34420     hide : function(){
34421         //if(!this.collapsed){
34422             this.el.dom.style.left = "-2000px";
34423             this.el.hide();
34424         //}else{
34425          //   this.collapsedEl.dom.style.left = "-2000px";
34426          //   this.collapsedEl.hide();
34427        // }
34428         this.visible = false;
34429         this.fireEvent("visibilitychange", this, false);
34430     },
34431
34432     /**
34433      * Shows this region if it was previously hidden.
34434      */
34435     show : function(){
34436         //if(!this.collapsed){
34437             this.el.show();
34438         //}else{
34439         //    this.collapsedEl.show();
34440        // }
34441         this.visible = true;
34442         this.fireEvent("visibilitychange", this, true);
34443     },
34444 /*
34445     closeClicked : function(){
34446         if(this.activePanel){
34447             this.remove(this.activePanel);
34448         }
34449     },
34450
34451     collapseClick : function(e){
34452         if(this.isSlid){
34453            e.stopPropagation();
34454            this.slideIn();
34455         }else{
34456            e.stopPropagation();
34457            this.slideOut();
34458         }
34459     },
34460 */
34461     /**
34462      * Collapses this region.
34463      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34464      */
34465     /*
34466     collapse : function(skipAnim, skipCheck = false){
34467         if(this.collapsed) {
34468             return;
34469         }
34470         
34471         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34472             
34473             this.collapsed = true;
34474             if(this.split){
34475                 this.split.el.hide();
34476             }
34477             if(this.config.animate && skipAnim !== true){
34478                 this.fireEvent("invalidated", this);
34479                 this.animateCollapse();
34480             }else{
34481                 this.el.setLocation(-20000,-20000);
34482                 this.el.hide();
34483                 this.collapsedEl.show();
34484                 this.fireEvent("collapsed", this);
34485                 this.fireEvent("invalidated", this);
34486             }
34487         }
34488         
34489     },
34490 */
34491     animateCollapse : function(){
34492         // overridden
34493     },
34494
34495     /**
34496      * Expands this region if it was previously collapsed.
34497      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34498      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34499      */
34500     /*
34501     expand : function(e, skipAnim){
34502         if(e) {
34503             e.stopPropagation();
34504         }
34505         if(!this.collapsed || this.el.hasActiveFx()) {
34506             return;
34507         }
34508         if(this.isSlid){
34509             this.afterSlideIn();
34510             skipAnim = true;
34511         }
34512         this.collapsed = false;
34513         if(this.config.animate && skipAnim !== true){
34514             this.animateExpand();
34515         }else{
34516             this.el.show();
34517             if(this.split){
34518                 this.split.el.show();
34519             }
34520             this.collapsedEl.setLocation(-2000,-2000);
34521             this.collapsedEl.hide();
34522             this.fireEvent("invalidated", this);
34523             this.fireEvent("expanded", this);
34524         }
34525     },
34526 */
34527     animateExpand : function(){
34528         // overridden
34529     },
34530
34531     initTabs : function()
34532     {
34533         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34534         
34535         var ts = new Roo.bootstrap.panel.Tabs({
34536                 el: this.bodyEl.dom,
34537                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34538                 disableTooltips: this.config.disableTabTips,
34539                 toolbar : this.config.toolbar
34540             });
34541         
34542         if(this.config.hideTabs){
34543             ts.stripWrap.setDisplayed(false);
34544         }
34545         this.tabs = ts;
34546         ts.resizeTabs = this.config.resizeTabs === true;
34547         ts.minTabWidth = this.config.minTabWidth || 40;
34548         ts.maxTabWidth = this.config.maxTabWidth || 250;
34549         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34550         ts.monitorResize = false;
34551         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34552         ts.bodyEl.addClass('roo-layout-tabs-body');
34553         this.panels.each(this.initPanelAsTab, this);
34554     },
34555
34556     initPanelAsTab : function(panel){
34557         var ti = this.tabs.addTab(
34558             panel.getEl().id,
34559             panel.getTitle(),
34560             null,
34561             this.config.closeOnTab && panel.isClosable(),
34562             panel.tpl
34563         );
34564         if(panel.tabTip !== undefined){
34565             ti.setTooltip(panel.tabTip);
34566         }
34567         ti.on("activate", function(){
34568               this.setActivePanel(panel);
34569         }, this);
34570         
34571         if(this.config.closeOnTab){
34572             ti.on("beforeclose", function(t, e){
34573                 e.cancel = true;
34574                 this.remove(panel);
34575             }, this);
34576         }
34577         
34578         panel.tabItem = ti;
34579         
34580         return ti;
34581     },
34582
34583     updatePanelTitle : function(panel, title)
34584     {
34585         if(this.activePanel == panel){
34586             this.updateTitle(title);
34587         }
34588         if(this.tabs){
34589             var ti = this.tabs.getTab(panel.getEl().id);
34590             ti.setText(title);
34591             if(panel.tabTip !== undefined){
34592                 ti.setTooltip(panel.tabTip);
34593             }
34594         }
34595     },
34596
34597     updateTitle : function(title){
34598         if(this.titleTextEl && !this.config.title){
34599             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34600         }
34601     },
34602
34603     setActivePanel : function(panel)
34604     {
34605         panel = this.getPanel(panel);
34606         if(this.activePanel && this.activePanel != panel){
34607             this.activePanel.setActiveState(false);
34608         }
34609         this.activePanel = panel;
34610         panel.setActiveState(true);
34611         if(this.panelSize){
34612             panel.setSize(this.panelSize.width, this.panelSize.height);
34613         }
34614         if(this.closeBtn){
34615             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34616         }
34617         this.updateTitle(panel.getTitle());
34618         if(this.tabs){
34619             this.fireEvent("invalidated", this);
34620         }
34621         this.fireEvent("panelactivated", this, panel);
34622     },
34623
34624     /**
34625      * Shows the specified panel.
34626      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34627      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34628      */
34629     showPanel : function(panel)
34630     {
34631         panel = this.getPanel(panel);
34632         if(panel){
34633             if(this.tabs){
34634                 var tab = this.tabs.getTab(panel.getEl().id);
34635                 if(tab.isHidden()){
34636                     this.tabs.unhideTab(tab.id);
34637                 }
34638                 tab.activate();
34639             }else{
34640                 this.setActivePanel(panel);
34641             }
34642         }
34643         return panel;
34644     },
34645
34646     /**
34647      * Get the active panel for this region.
34648      * @return {Roo.ContentPanel} The active panel or null
34649      */
34650     getActivePanel : function(){
34651         return this.activePanel;
34652     },
34653
34654     validateVisibility : function(){
34655         if(this.panels.getCount() < 1){
34656             this.updateTitle("&#160;");
34657             this.closeBtn.hide();
34658             this.hide();
34659         }else{
34660             if(!this.isVisible()){
34661                 this.show();
34662             }
34663         }
34664     },
34665
34666     /**
34667      * Adds the passed ContentPanel(s) to this region.
34668      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34669      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34670      */
34671     add : function(panel)
34672     {
34673         if(arguments.length > 1){
34674             for(var i = 0, len = arguments.length; i < len; i++) {
34675                 this.add(arguments[i]);
34676             }
34677             return null;
34678         }
34679         
34680         // if we have not been rendered yet, then we can not really do much of this..
34681         if (!this.bodyEl) {
34682             this.unrendered_panels.push(panel);
34683             return panel;
34684         }
34685         
34686         
34687         
34688         
34689         if(this.hasPanel(panel)){
34690             this.showPanel(panel);
34691             return panel;
34692         }
34693         panel.setRegion(this);
34694         this.panels.add(panel);
34695        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34696             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34697             // and hide them... ???
34698             this.bodyEl.dom.appendChild(panel.getEl().dom);
34699             if(panel.background !== true){
34700                 this.setActivePanel(panel);
34701             }
34702             this.fireEvent("paneladded", this, panel);
34703             return panel;
34704         }
34705         */
34706         if(!this.tabs){
34707             this.initTabs();
34708         }else{
34709             this.initPanelAsTab(panel);
34710         }
34711         
34712         
34713         if(panel.background !== true){
34714             this.tabs.activate(panel.getEl().id);
34715         }
34716         this.fireEvent("paneladded", this, panel);
34717         return panel;
34718     },
34719
34720     /**
34721      * Hides the tab for the specified panel.
34722      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34723      */
34724     hidePanel : function(panel){
34725         if(this.tabs && (panel = this.getPanel(panel))){
34726             this.tabs.hideTab(panel.getEl().id);
34727         }
34728     },
34729
34730     /**
34731      * Unhides the tab for a previously hidden panel.
34732      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34733      */
34734     unhidePanel : function(panel){
34735         if(this.tabs && (panel = this.getPanel(panel))){
34736             this.tabs.unhideTab(panel.getEl().id);
34737         }
34738     },
34739
34740     clearPanels : function(){
34741         while(this.panels.getCount() > 0){
34742              this.remove(this.panels.first());
34743         }
34744     },
34745
34746     /**
34747      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34748      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34749      * @param {Boolean} preservePanel Overrides the config preservePanel option
34750      * @return {Roo.ContentPanel} The panel that was removed
34751      */
34752     remove : function(panel, preservePanel)
34753     {
34754         panel = this.getPanel(panel);
34755         if(!panel){
34756             return null;
34757         }
34758         var e = {};
34759         this.fireEvent("beforeremove", this, panel, e);
34760         if(e.cancel === true){
34761             return null;
34762         }
34763         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34764         var panelId = panel.getId();
34765         this.panels.removeKey(panelId);
34766         if(preservePanel){
34767             document.body.appendChild(panel.getEl().dom);
34768         }
34769         if(this.tabs){
34770             this.tabs.removeTab(panel.getEl().id);
34771         }else if (!preservePanel){
34772             this.bodyEl.dom.removeChild(panel.getEl().dom);
34773         }
34774         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34775             var p = this.panels.first();
34776             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34777             tempEl.appendChild(p.getEl().dom);
34778             this.bodyEl.update("");
34779             this.bodyEl.dom.appendChild(p.getEl().dom);
34780             tempEl = null;
34781             this.updateTitle(p.getTitle());
34782             this.tabs = null;
34783             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34784             this.setActivePanel(p);
34785         }
34786         panel.setRegion(null);
34787         if(this.activePanel == panel){
34788             this.activePanel = null;
34789         }
34790         if(this.config.autoDestroy !== false && preservePanel !== true){
34791             try{panel.destroy();}catch(e){}
34792         }
34793         this.fireEvent("panelremoved", this, panel);
34794         return panel;
34795     },
34796
34797     /**
34798      * Returns the TabPanel component used by this region
34799      * @return {Roo.TabPanel}
34800      */
34801     getTabs : function(){
34802         return this.tabs;
34803     },
34804
34805     createTool : function(parentEl, className){
34806         var btn = Roo.DomHelper.append(parentEl, {
34807             tag: "div",
34808             cls: "x-layout-tools-button",
34809             children: [ {
34810                 tag: "div",
34811                 cls: "roo-layout-tools-button-inner " + className,
34812                 html: "&#160;"
34813             }]
34814         }, true);
34815         btn.addClassOnOver("roo-layout-tools-button-over");
34816         return btn;
34817     }
34818 });/*
34819  * Based on:
34820  * Ext JS Library 1.1.1
34821  * Copyright(c) 2006-2007, Ext JS, LLC.
34822  *
34823  * Originally Released Under LGPL - original licence link has changed is not relivant.
34824  *
34825  * Fork - LGPL
34826  * <script type="text/javascript">
34827  */
34828  
34829
34830
34831 /**
34832  * @class Roo.SplitLayoutRegion
34833  * @extends Roo.LayoutRegion
34834  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34835  */
34836 Roo.bootstrap.layout.Split = function(config){
34837     this.cursor = config.cursor;
34838     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34839 };
34840
34841 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34842 {
34843     splitTip : "Drag to resize.",
34844     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34845     useSplitTips : false,
34846
34847     applyConfig : function(config){
34848         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34849     },
34850     
34851     onRender : function(ctr,pos) {
34852         
34853         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34854         if(!this.config.split){
34855             return;
34856         }
34857         if(!this.split){
34858             
34859             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34860                             tag: "div",
34861                             id: this.el.id + "-split",
34862                             cls: "roo-layout-split roo-layout-split-"+this.position,
34863                             html: "&#160;"
34864             });
34865             /** The SplitBar for this region 
34866             * @type Roo.SplitBar */
34867             // does not exist yet...
34868             Roo.log([this.position, this.orientation]);
34869             
34870             this.split = new Roo.bootstrap.SplitBar({
34871                 dragElement : splitEl,
34872                 resizingElement: this.el,
34873                 orientation : this.orientation
34874             });
34875             
34876             this.split.on("moved", this.onSplitMove, this);
34877             this.split.useShim = this.config.useShim === true;
34878             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34879             if(this.useSplitTips){
34880                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34881             }
34882             //if(config.collapsible){
34883             //    this.split.el.on("dblclick", this.collapse,  this);
34884             //}
34885         }
34886         if(typeof this.config.minSize != "undefined"){
34887             this.split.minSize = this.config.minSize;
34888         }
34889         if(typeof this.config.maxSize != "undefined"){
34890             this.split.maxSize = this.config.maxSize;
34891         }
34892         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34893             this.hideSplitter();
34894         }
34895         
34896     },
34897
34898     getHMaxSize : function(){
34899          var cmax = this.config.maxSize || 10000;
34900          var center = this.mgr.getRegion("center");
34901          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34902     },
34903
34904     getVMaxSize : function(){
34905          var cmax = this.config.maxSize || 10000;
34906          var center = this.mgr.getRegion("center");
34907          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34908     },
34909
34910     onSplitMove : function(split, newSize){
34911         this.fireEvent("resized", this, newSize);
34912     },
34913     
34914     /** 
34915      * Returns the {@link Roo.SplitBar} for this region.
34916      * @return {Roo.SplitBar}
34917      */
34918     getSplitBar : function(){
34919         return this.split;
34920     },
34921     
34922     hide : function(){
34923         this.hideSplitter();
34924         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34925     },
34926
34927     hideSplitter : function(){
34928         if(this.split){
34929             this.split.el.setLocation(-2000,-2000);
34930             this.split.el.hide();
34931         }
34932     },
34933
34934     show : function(){
34935         if(this.split){
34936             this.split.el.show();
34937         }
34938         Roo.bootstrap.layout.Split.superclass.show.call(this);
34939     },
34940     
34941     beforeSlide: function(){
34942         if(Roo.isGecko){// firefox overflow auto bug workaround
34943             this.bodyEl.clip();
34944             if(this.tabs) {
34945                 this.tabs.bodyEl.clip();
34946             }
34947             if(this.activePanel){
34948                 this.activePanel.getEl().clip();
34949                 
34950                 if(this.activePanel.beforeSlide){
34951                     this.activePanel.beforeSlide();
34952                 }
34953             }
34954         }
34955     },
34956     
34957     afterSlide : function(){
34958         if(Roo.isGecko){// firefox overflow auto bug workaround
34959             this.bodyEl.unclip();
34960             if(this.tabs) {
34961                 this.tabs.bodyEl.unclip();
34962             }
34963             if(this.activePanel){
34964                 this.activePanel.getEl().unclip();
34965                 if(this.activePanel.afterSlide){
34966                     this.activePanel.afterSlide();
34967                 }
34968             }
34969         }
34970     },
34971
34972     initAutoHide : function(){
34973         if(this.autoHide !== false){
34974             if(!this.autoHideHd){
34975                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34976                 this.autoHideHd = {
34977                     "mouseout": function(e){
34978                         if(!e.within(this.el, true)){
34979                             st.delay(500);
34980                         }
34981                     },
34982                     "mouseover" : function(e){
34983                         st.cancel();
34984                     },
34985                     scope : this
34986                 };
34987             }
34988             this.el.on(this.autoHideHd);
34989         }
34990     },
34991
34992     clearAutoHide : function(){
34993         if(this.autoHide !== false){
34994             this.el.un("mouseout", this.autoHideHd.mouseout);
34995             this.el.un("mouseover", this.autoHideHd.mouseover);
34996         }
34997     },
34998
34999     clearMonitor : function(){
35000         Roo.get(document).un("click", this.slideInIf, this);
35001     },
35002
35003     // these names are backwards but not changed for compat
35004     slideOut : function(){
35005         if(this.isSlid || this.el.hasActiveFx()){
35006             return;
35007         }
35008         this.isSlid = true;
35009         if(this.collapseBtn){
35010             this.collapseBtn.hide();
35011         }
35012         this.closeBtnState = this.closeBtn.getStyle('display');
35013         this.closeBtn.hide();
35014         if(this.stickBtn){
35015             this.stickBtn.show();
35016         }
35017         this.el.show();
35018         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35019         this.beforeSlide();
35020         this.el.setStyle("z-index", 10001);
35021         this.el.slideIn(this.getSlideAnchor(), {
35022             callback: function(){
35023                 this.afterSlide();
35024                 this.initAutoHide();
35025                 Roo.get(document).on("click", this.slideInIf, this);
35026                 this.fireEvent("slideshow", this);
35027             },
35028             scope: this,
35029             block: true
35030         });
35031     },
35032
35033     afterSlideIn : function(){
35034         this.clearAutoHide();
35035         this.isSlid = false;
35036         this.clearMonitor();
35037         this.el.setStyle("z-index", "");
35038         if(this.collapseBtn){
35039             this.collapseBtn.show();
35040         }
35041         this.closeBtn.setStyle('display', this.closeBtnState);
35042         if(this.stickBtn){
35043             this.stickBtn.hide();
35044         }
35045         this.fireEvent("slidehide", this);
35046     },
35047
35048     slideIn : function(cb){
35049         if(!this.isSlid || this.el.hasActiveFx()){
35050             Roo.callback(cb);
35051             return;
35052         }
35053         this.isSlid = false;
35054         this.beforeSlide();
35055         this.el.slideOut(this.getSlideAnchor(), {
35056             callback: function(){
35057                 this.el.setLeftTop(-10000, -10000);
35058                 this.afterSlide();
35059                 this.afterSlideIn();
35060                 Roo.callback(cb);
35061             },
35062             scope: this,
35063             block: true
35064         });
35065     },
35066     
35067     slideInIf : function(e){
35068         if(!e.within(this.el)){
35069             this.slideIn();
35070         }
35071     },
35072
35073     animateCollapse : function(){
35074         this.beforeSlide();
35075         this.el.setStyle("z-index", 20000);
35076         var anchor = this.getSlideAnchor();
35077         this.el.slideOut(anchor, {
35078             callback : function(){
35079                 this.el.setStyle("z-index", "");
35080                 this.collapsedEl.slideIn(anchor, {duration:.3});
35081                 this.afterSlide();
35082                 this.el.setLocation(-10000,-10000);
35083                 this.el.hide();
35084                 this.fireEvent("collapsed", this);
35085             },
35086             scope: this,
35087             block: true
35088         });
35089     },
35090
35091     animateExpand : function(){
35092         this.beforeSlide();
35093         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35094         this.el.setStyle("z-index", 20000);
35095         this.collapsedEl.hide({
35096             duration:.1
35097         });
35098         this.el.slideIn(this.getSlideAnchor(), {
35099             callback : function(){
35100                 this.el.setStyle("z-index", "");
35101                 this.afterSlide();
35102                 if(this.split){
35103                     this.split.el.show();
35104                 }
35105                 this.fireEvent("invalidated", this);
35106                 this.fireEvent("expanded", this);
35107             },
35108             scope: this,
35109             block: true
35110         });
35111     },
35112
35113     anchors : {
35114         "west" : "left",
35115         "east" : "right",
35116         "north" : "top",
35117         "south" : "bottom"
35118     },
35119
35120     sanchors : {
35121         "west" : "l",
35122         "east" : "r",
35123         "north" : "t",
35124         "south" : "b"
35125     },
35126
35127     canchors : {
35128         "west" : "tl-tr",
35129         "east" : "tr-tl",
35130         "north" : "tl-bl",
35131         "south" : "bl-tl"
35132     },
35133
35134     getAnchor : function(){
35135         return this.anchors[this.position];
35136     },
35137
35138     getCollapseAnchor : function(){
35139         return this.canchors[this.position];
35140     },
35141
35142     getSlideAnchor : function(){
35143         return this.sanchors[this.position];
35144     },
35145
35146     getAlignAdj : function(){
35147         var cm = this.cmargins;
35148         switch(this.position){
35149             case "west":
35150                 return [0, 0];
35151             break;
35152             case "east":
35153                 return [0, 0];
35154             break;
35155             case "north":
35156                 return [0, 0];
35157             break;
35158             case "south":
35159                 return [0, 0];
35160             break;
35161         }
35162     },
35163
35164     getExpandAdj : function(){
35165         var c = this.collapsedEl, cm = this.cmargins;
35166         switch(this.position){
35167             case "west":
35168                 return [-(cm.right+c.getWidth()+cm.left), 0];
35169             break;
35170             case "east":
35171                 return [cm.right+c.getWidth()+cm.left, 0];
35172             break;
35173             case "north":
35174                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35175             break;
35176             case "south":
35177                 return [0, cm.top+cm.bottom+c.getHeight()];
35178             break;
35179         }
35180     }
35181 });/*
35182  * Based on:
35183  * Ext JS Library 1.1.1
35184  * Copyright(c) 2006-2007, Ext JS, LLC.
35185  *
35186  * Originally Released Under LGPL - original licence link has changed is not relivant.
35187  *
35188  * Fork - LGPL
35189  * <script type="text/javascript">
35190  */
35191 /*
35192  * These classes are private internal classes
35193  */
35194 Roo.bootstrap.layout.Center = function(config){
35195     config.region = "center";
35196     Roo.bootstrap.layout.Region.call(this, config);
35197     this.visible = true;
35198     this.minWidth = config.minWidth || 20;
35199     this.minHeight = config.minHeight || 20;
35200 };
35201
35202 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35203     hide : function(){
35204         // center panel can't be hidden
35205     },
35206     
35207     show : function(){
35208         // center panel can't be hidden
35209     },
35210     
35211     getMinWidth: function(){
35212         return this.minWidth;
35213     },
35214     
35215     getMinHeight: function(){
35216         return this.minHeight;
35217     }
35218 });
35219
35220
35221
35222
35223  
35224
35225
35226
35227
35228
35229 Roo.bootstrap.layout.North = function(config)
35230 {
35231     config.region = 'north';
35232     config.cursor = 'n-resize';
35233     
35234     Roo.bootstrap.layout.Split.call(this, config);
35235     
35236     
35237     if(this.split){
35238         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35239         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35240         this.split.el.addClass("roo-layout-split-v");
35241     }
35242     var size = config.initialSize || config.height;
35243     if(typeof size != "undefined"){
35244         this.el.setHeight(size);
35245     }
35246 };
35247 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35248 {
35249     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35250     
35251     
35252     
35253     getBox : function(){
35254         if(this.collapsed){
35255             return this.collapsedEl.getBox();
35256         }
35257         var box = this.el.getBox();
35258         if(this.split){
35259             box.height += this.split.el.getHeight();
35260         }
35261         return box;
35262     },
35263     
35264     updateBox : function(box){
35265         if(this.split && !this.collapsed){
35266             box.height -= this.split.el.getHeight();
35267             this.split.el.setLeft(box.x);
35268             this.split.el.setTop(box.y+box.height);
35269             this.split.el.setWidth(box.width);
35270         }
35271         if(this.collapsed){
35272             this.updateBody(box.width, null);
35273         }
35274         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35275     }
35276 });
35277
35278
35279
35280
35281
35282 Roo.bootstrap.layout.South = function(config){
35283     config.region = 'south';
35284     config.cursor = 's-resize';
35285     Roo.bootstrap.layout.Split.call(this, config);
35286     if(this.split){
35287         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35288         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35289         this.split.el.addClass("roo-layout-split-v");
35290     }
35291     var size = config.initialSize || config.height;
35292     if(typeof size != "undefined"){
35293         this.el.setHeight(size);
35294     }
35295 };
35296
35297 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35298     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35299     getBox : function(){
35300         if(this.collapsed){
35301             return this.collapsedEl.getBox();
35302         }
35303         var box = this.el.getBox();
35304         if(this.split){
35305             var sh = this.split.el.getHeight();
35306             box.height += sh;
35307             box.y -= sh;
35308         }
35309         return box;
35310     },
35311     
35312     updateBox : function(box){
35313         if(this.split && !this.collapsed){
35314             var sh = this.split.el.getHeight();
35315             box.height -= sh;
35316             box.y += sh;
35317             this.split.el.setLeft(box.x);
35318             this.split.el.setTop(box.y-sh);
35319             this.split.el.setWidth(box.width);
35320         }
35321         if(this.collapsed){
35322             this.updateBody(box.width, null);
35323         }
35324         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35325     }
35326 });
35327
35328 Roo.bootstrap.layout.East = function(config){
35329     config.region = "east";
35330     config.cursor = "e-resize";
35331     Roo.bootstrap.layout.Split.call(this, config);
35332     if(this.split){
35333         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
35334         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35335         this.split.el.addClass("roo-layout-split-h");
35336     }
35337     var size = config.initialSize || config.width;
35338     if(typeof size != "undefined"){
35339         this.el.setWidth(size);
35340     }
35341 };
35342 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35343     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35344     getBox : function(){
35345         if(this.collapsed){
35346             return this.collapsedEl.getBox();
35347         }
35348         var box = this.el.getBox();
35349         if(this.split){
35350             var sw = this.split.el.getWidth();
35351             box.width += sw;
35352             box.x -= sw;
35353         }
35354         return box;
35355     },
35356
35357     updateBox : function(box){
35358         if(this.split && !this.collapsed){
35359             var sw = this.split.el.getWidth();
35360             box.width -= sw;
35361             this.split.el.setLeft(box.x);
35362             this.split.el.setTop(box.y);
35363             this.split.el.setHeight(box.height);
35364             box.x += sw;
35365         }
35366         if(this.collapsed){
35367             this.updateBody(null, box.height);
35368         }
35369         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35370     }
35371 });
35372
35373 Roo.bootstrap.layout.West = function(config){
35374     config.region = "west";
35375     config.cursor = "w-resize";
35376     
35377     Roo.bootstrap.layout.Split.call(this, config);
35378     if(this.split){
35379         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
35380         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35381         this.split.el.addClass("roo-layout-split-h");
35382     }
35383     
35384 };
35385 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
35386     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35387     
35388     onRender: function(ctr, pos)
35389     {
35390         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
35391         var size = this.config.initialSize || this.config.width;
35392         if(typeof size != "undefined"){
35393             this.el.setWidth(size);
35394         }
35395     },
35396     
35397     getBox : function(){
35398         if(this.collapsed){
35399             return this.collapsedEl.getBox();
35400         }
35401         var box = this.el.getBox();
35402         if(this.split){
35403             box.width += this.split.el.getWidth();
35404         }
35405         return box;
35406     },
35407     
35408     updateBox : function(box){
35409         if(this.split && !this.collapsed){
35410             var sw = this.split.el.getWidth();
35411             box.width -= sw;
35412             this.split.el.setLeft(box.x+box.width);
35413             this.split.el.setTop(box.y);
35414             this.split.el.setHeight(box.height);
35415         }
35416         if(this.collapsed){
35417             this.updateBody(null, box.height);
35418         }
35419         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35420     }
35421 });
35422 Roo.namespace("Roo.bootstrap.panel");/*
35423  * Based on:
35424  * Ext JS Library 1.1.1
35425  * Copyright(c) 2006-2007, Ext JS, LLC.
35426  *
35427  * Originally Released Under LGPL - original licence link has changed is not relivant.
35428  *
35429  * Fork - LGPL
35430  * <script type="text/javascript">
35431  */
35432 /**
35433  * @class Roo.ContentPanel
35434  * @extends Roo.util.Observable
35435  * A basic ContentPanel element.
35436  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35437  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35438  * @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
35439  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35440  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35441  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35442  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35443  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35444  * @cfg {String} title          The title for this panel
35445  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35446  * @cfg {String} url            Calls {@link #setUrl} with this value
35447  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35448  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35449  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35450  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35451  * @cfg {Boolean} badges render the badges
35452
35453  * @constructor
35454  * Create a new ContentPanel.
35455  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35456  * @param {String/Object} config A string to set only the title or a config object
35457  * @param {String} content (optional) Set the HTML content for this panel
35458  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35459  */
35460 Roo.bootstrap.panel.Content = function( config){
35461     
35462     this.tpl = config.tpl || false;
35463     
35464     var el = config.el;
35465     var content = config.content;
35466
35467     if(config.autoCreate){ // xtype is available if this is called from factory
35468         el = Roo.id();
35469     }
35470     this.el = Roo.get(el);
35471     if(!this.el && config && config.autoCreate){
35472         if(typeof config.autoCreate == "object"){
35473             if(!config.autoCreate.id){
35474                 config.autoCreate.id = config.id||el;
35475             }
35476             this.el = Roo.DomHelper.append(document.body,
35477                         config.autoCreate, true);
35478         }else{
35479             var elcfg =  {   tag: "div",
35480                             cls: "roo-layout-inactive-content",
35481                             id: config.id||el
35482                             };
35483             if (config.html) {
35484                 elcfg.html = config.html;
35485                 
35486             }
35487                         
35488             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35489         }
35490     } 
35491     this.closable = false;
35492     this.loaded = false;
35493     this.active = false;
35494    
35495       
35496     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35497         
35498         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35499         
35500         this.wrapEl = this.el; //this.el.wrap();
35501         var ti = [];
35502         if (config.toolbar.items) {
35503             ti = config.toolbar.items ;
35504             delete config.toolbar.items ;
35505         }
35506         
35507         var nitems = [];
35508         this.toolbar.render(this.wrapEl, 'before');
35509         for(var i =0;i < ti.length;i++) {
35510           //  Roo.log(['add child', items[i]]);
35511             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35512         }
35513         this.toolbar.items = nitems;
35514         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35515         delete config.toolbar;
35516         
35517     }
35518     /*
35519     // xtype created footer. - not sure if will work as we normally have to render first..
35520     if (this.footer && !this.footer.el && this.footer.xtype) {
35521         if (!this.wrapEl) {
35522             this.wrapEl = this.el.wrap();
35523         }
35524     
35525         this.footer.container = this.wrapEl.createChild();
35526          
35527         this.footer = Roo.factory(this.footer, Roo);
35528         
35529     }
35530     */
35531     
35532      if(typeof config == "string"){
35533         this.title = config;
35534     }else{
35535         Roo.apply(this, config);
35536     }
35537     
35538     if(this.resizeEl){
35539         this.resizeEl = Roo.get(this.resizeEl, true);
35540     }else{
35541         this.resizeEl = this.el;
35542     }
35543     // handle view.xtype
35544     
35545  
35546     
35547     
35548     this.addEvents({
35549         /**
35550          * @event activate
35551          * Fires when this panel is activated. 
35552          * @param {Roo.ContentPanel} this
35553          */
35554         "activate" : true,
35555         /**
35556          * @event deactivate
35557          * Fires when this panel is activated. 
35558          * @param {Roo.ContentPanel} this
35559          */
35560         "deactivate" : true,
35561
35562         /**
35563          * @event resize
35564          * Fires when this panel is resized if fitToFrame is true.
35565          * @param {Roo.ContentPanel} this
35566          * @param {Number} width The width after any component adjustments
35567          * @param {Number} height The height after any component adjustments
35568          */
35569         "resize" : true,
35570         
35571          /**
35572          * @event render
35573          * Fires when this tab is created
35574          * @param {Roo.ContentPanel} this
35575          */
35576         "render" : true
35577         
35578         
35579         
35580     });
35581     
35582
35583     
35584     
35585     if(this.autoScroll){
35586         this.resizeEl.setStyle("overflow", "auto");
35587     } else {
35588         // fix randome scrolling
35589         //this.el.on('scroll', function() {
35590         //    Roo.log('fix random scolling');
35591         //    this.scrollTo('top',0); 
35592         //});
35593     }
35594     content = content || this.content;
35595     if(content){
35596         this.setContent(content);
35597     }
35598     if(config && config.url){
35599         this.setUrl(this.url, this.params, this.loadOnce);
35600     }
35601     
35602     
35603     
35604     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35605     
35606     if (this.view && typeof(this.view.xtype) != 'undefined') {
35607         this.view.el = this.el.appendChild(document.createElement("div"));
35608         this.view = Roo.factory(this.view); 
35609         this.view.render  &&  this.view.render(false, '');  
35610     }
35611     
35612     
35613     this.fireEvent('render', this);
35614 };
35615
35616 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35617     
35618     tabTip : '',
35619     
35620     setRegion : function(region){
35621         this.region = region;
35622         this.setActiveClass(region && !this.background);
35623     },
35624     
35625     
35626     setActiveClass: function(state)
35627     {
35628         if(state){
35629            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35630            this.el.setStyle('position','relative');
35631         }else{
35632            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35633            this.el.setStyle('position', 'absolute');
35634         } 
35635     },
35636     
35637     /**
35638      * Returns the toolbar for this Panel if one was configured. 
35639      * @return {Roo.Toolbar} 
35640      */
35641     getToolbar : function(){
35642         return this.toolbar;
35643     },
35644     
35645     setActiveState : function(active)
35646     {
35647         this.active = active;
35648         this.setActiveClass(active);
35649         if(!active){
35650             this.fireEvent("deactivate", this);
35651         }else{
35652             this.fireEvent("activate", this);
35653         }
35654     },
35655     /**
35656      * Updates this panel's element
35657      * @param {String} content The new content
35658      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35659     */
35660     setContent : function(content, loadScripts){
35661         this.el.update(content, loadScripts);
35662     },
35663
35664     ignoreResize : function(w, h){
35665         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35666             return true;
35667         }else{
35668             this.lastSize = {width: w, height: h};
35669             return false;
35670         }
35671     },
35672     /**
35673      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35674      * @return {Roo.UpdateManager} The UpdateManager
35675      */
35676     getUpdateManager : function(){
35677         return this.el.getUpdateManager();
35678     },
35679      /**
35680      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35681      * @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:
35682 <pre><code>
35683 panel.load({
35684     url: "your-url.php",
35685     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35686     callback: yourFunction,
35687     scope: yourObject, //(optional scope)
35688     discardUrl: false,
35689     nocache: false,
35690     text: "Loading...",
35691     timeout: 30,
35692     scripts: false
35693 });
35694 </code></pre>
35695      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35696      * 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.
35697      * @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}
35698      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35699      * @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.
35700      * @return {Roo.ContentPanel} this
35701      */
35702     load : function(){
35703         var um = this.el.getUpdateManager();
35704         um.update.apply(um, arguments);
35705         return this;
35706     },
35707
35708
35709     /**
35710      * 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.
35711      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35712      * @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)
35713      * @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)
35714      * @return {Roo.UpdateManager} The UpdateManager
35715      */
35716     setUrl : function(url, params, loadOnce){
35717         if(this.refreshDelegate){
35718             this.removeListener("activate", this.refreshDelegate);
35719         }
35720         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35721         this.on("activate", this.refreshDelegate);
35722         return this.el.getUpdateManager();
35723     },
35724     
35725     _handleRefresh : function(url, params, loadOnce){
35726         if(!loadOnce || !this.loaded){
35727             var updater = this.el.getUpdateManager();
35728             updater.update(url, params, this._setLoaded.createDelegate(this));
35729         }
35730     },
35731     
35732     _setLoaded : function(){
35733         this.loaded = true;
35734     }, 
35735     
35736     /**
35737      * Returns this panel's id
35738      * @return {String} 
35739      */
35740     getId : function(){
35741         return this.el.id;
35742     },
35743     
35744     /** 
35745      * Returns this panel's element - used by regiosn to add.
35746      * @return {Roo.Element} 
35747      */
35748     getEl : function(){
35749         return this.wrapEl || this.el;
35750     },
35751     
35752    
35753     
35754     adjustForComponents : function(width, height)
35755     {
35756         //Roo.log('adjustForComponents ');
35757         if(this.resizeEl != this.el){
35758             width -= this.el.getFrameWidth('lr');
35759             height -= this.el.getFrameWidth('tb');
35760         }
35761         if(this.toolbar){
35762             var te = this.toolbar.getEl();
35763             height -= te.getHeight();
35764             te.setWidth(width);
35765         }
35766         if(this.footer){
35767             var te = this.footer.getEl();
35768             Roo.log("footer:" + te.getHeight());
35769             
35770             height -= te.getHeight();
35771             te.setWidth(width);
35772         }
35773         
35774         
35775         if(this.adjustments){
35776             width += this.adjustments[0];
35777             height += this.adjustments[1];
35778         }
35779         return {"width": width, "height": height};
35780     },
35781     
35782     setSize : function(width, height){
35783         if(this.fitToFrame && !this.ignoreResize(width, height)){
35784             if(this.fitContainer && this.resizeEl != this.el){
35785                 this.el.setSize(width, height);
35786             }
35787             var size = this.adjustForComponents(width, height);
35788             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35789             this.fireEvent('resize', this, size.width, size.height);
35790         }
35791     },
35792     
35793     /**
35794      * Returns this panel's title
35795      * @return {String} 
35796      */
35797     getTitle : function(){
35798         return this.title;
35799     },
35800     
35801     /**
35802      * Set this panel's title
35803      * @param {String} title
35804      */
35805     setTitle : function(title){
35806         this.title = title;
35807         if(this.region){
35808             this.region.updatePanelTitle(this, title);
35809         }
35810     },
35811     
35812     /**
35813      * Returns true is this panel was configured to be closable
35814      * @return {Boolean} 
35815      */
35816     isClosable : function(){
35817         return this.closable;
35818     },
35819     
35820     beforeSlide : function(){
35821         this.el.clip();
35822         this.resizeEl.clip();
35823     },
35824     
35825     afterSlide : function(){
35826         this.el.unclip();
35827         this.resizeEl.unclip();
35828     },
35829     
35830     /**
35831      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35832      *   Will fail silently if the {@link #setUrl} method has not been called.
35833      *   This does not activate the panel, just updates its content.
35834      */
35835     refresh : function(){
35836         if(this.refreshDelegate){
35837            this.loaded = false;
35838            this.refreshDelegate();
35839         }
35840     },
35841     
35842     /**
35843      * Destroys this panel
35844      */
35845     destroy : function(){
35846         this.el.removeAllListeners();
35847         var tempEl = document.createElement("span");
35848         tempEl.appendChild(this.el.dom);
35849         tempEl.innerHTML = "";
35850         this.el.remove();
35851         this.el = null;
35852     },
35853     
35854     /**
35855      * form - if the content panel contains a form - this is a reference to it.
35856      * @type {Roo.form.Form}
35857      */
35858     form : false,
35859     /**
35860      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35861      *    This contains a reference to it.
35862      * @type {Roo.View}
35863      */
35864     view : false,
35865     
35866       /**
35867      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35868      * <pre><code>
35869
35870 layout.addxtype({
35871        xtype : 'Form',
35872        items: [ .... ]
35873    }
35874 );
35875
35876 </code></pre>
35877      * @param {Object} cfg Xtype definition of item to add.
35878      */
35879     
35880     
35881     getChildContainer: function () {
35882         return this.getEl();
35883     }
35884     
35885     
35886     /*
35887         var  ret = new Roo.factory(cfg);
35888         return ret;
35889         
35890         
35891         // add form..
35892         if (cfg.xtype.match(/^Form$/)) {
35893             
35894             var el;
35895             //if (this.footer) {
35896             //    el = this.footer.container.insertSibling(false, 'before');
35897             //} else {
35898                 el = this.el.createChild();
35899             //}
35900
35901             this.form = new  Roo.form.Form(cfg);
35902             
35903             
35904             if ( this.form.allItems.length) {
35905                 this.form.render(el.dom);
35906             }
35907             return this.form;
35908         }
35909         // should only have one of theses..
35910         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35911             // views.. should not be just added - used named prop 'view''
35912             
35913             cfg.el = this.el.appendChild(document.createElement("div"));
35914             // factory?
35915             
35916             var ret = new Roo.factory(cfg);
35917              
35918              ret.render && ret.render(false, ''); // render blank..
35919             this.view = ret;
35920             return ret;
35921         }
35922         return false;
35923     }
35924     \*/
35925 });
35926  
35927 /**
35928  * @class Roo.bootstrap.panel.Grid
35929  * @extends Roo.bootstrap.panel.Content
35930  * @constructor
35931  * Create a new GridPanel.
35932  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35933  * @param {Object} config A the config object
35934   
35935  */
35936
35937
35938
35939 Roo.bootstrap.panel.Grid = function(config)
35940 {
35941     
35942       
35943     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35944         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35945
35946     config.el = this.wrapper;
35947     //this.el = this.wrapper;
35948     
35949       if (config.container) {
35950         // ctor'ed from a Border/panel.grid
35951         
35952         
35953         this.wrapper.setStyle("overflow", "hidden");
35954         this.wrapper.addClass('roo-grid-container');
35955
35956     }
35957     
35958     
35959     if(config.toolbar){
35960         var tool_el = this.wrapper.createChild();    
35961         this.toolbar = Roo.factory(config.toolbar);
35962         var ti = [];
35963         if (config.toolbar.items) {
35964             ti = config.toolbar.items ;
35965             delete config.toolbar.items ;
35966         }
35967         
35968         var nitems = [];
35969         this.toolbar.render(tool_el);
35970         for(var i =0;i < ti.length;i++) {
35971           //  Roo.log(['add child', items[i]]);
35972             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35973         }
35974         this.toolbar.items = nitems;
35975         
35976         delete config.toolbar;
35977     }
35978     
35979     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35980     config.grid.scrollBody = true;;
35981     config.grid.monitorWindowResize = false; // turn off autosizing
35982     config.grid.autoHeight = false;
35983     config.grid.autoWidth = false;
35984     
35985     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35986     
35987     if (config.background) {
35988         // render grid on panel activation (if panel background)
35989         this.on('activate', function(gp) {
35990             if (!gp.grid.rendered) {
35991                 gp.grid.render(this.wrapper);
35992                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35993             }
35994         });
35995             
35996     } else {
35997         this.grid.render(this.wrapper);
35998         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35999
36000     }
36001     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36002     // ??? needed ??? config.el = this.wrapper;
36003     
36004     
36005     
36006   
36007     // xtype created footer. - not sure if will work as we normally have to render first..
36008     if (this.footer && !this.footer.el && this.footer.xtype) {
36009         
36010         var ctr = this.grid.getView().getFooterPanel(true);
36011         this.footer.dataSource = this.grid.dataSource;
36012         this.footer = Roo.factory(this.footer, Roo);
36013         this.footer.render(ctr);
36014         
36015     }
36016     
36017     
36018     
36019     
36020      
36021 };
36022
36023 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36024     getId : function(){
36025         return this.grid.id;
36026     },
36027     
36028     /**
36029      * Returns the grid for this panel
36030      * @return {Roo.bootstrap.Table} 
36031      */
36032     getGrid : function(){
36033         return this.grid;    
36034     },
36035     
36036     setSize : function(width, height){
36037         if(!this.ignoreResize(width, height)){
36038             var grid = this.grid;
36039             var size = this.adjustForComponents(width, height);
36040             var gridel = grid.getGridEl();
36041             gridel.setSize(size.width, size.height);
36042             /*
36043             var thd = grid.getGridEl().select('thead',true).first();
36044             var tbd = grid.getGridEl().select('tbody', true).first();
36045             if (tbd) {
36046                 tbd.setSize(width, height - thd.getHeight());
36047             }
36048             */
36049             grid.autoSize();
36050         }
36051     },
36052      
36053     
36054     
36055     beforeSlide : function(){
36056         this.grid.getView().scroller.clip();
36057     },
36058     
36059     afterSlide : function(){
36060         this.grid.getView().scroller.unclip();
36061     },
36062     
36063     destroy : function(){
36064         this.grid.destroy();
36065         delete this.grid;
36066         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36067     }
36068 });
36069
36070 /**
36071  * @class Roo.bootstrap.panel.Nest
36072  * @extends Roo.bootstrap.panel.Content
36073  * @constructor
36074  * Create a new Panel, that can contain a layout.Border.
36075  * 
36076  * 
36077  * @param {Roo.BorderLayout} layout The layout for this panel
36078  * @param {String/Object} config A string to set only the title or a config object
36079  */
36080 Roo.bootstrap.panel.Nest = function(config)
36081 {
36082     // construct with only one argument..
36083     /* FIXME - implement nicer consturctors
36084     if (layout.layout) {
36085         config = layout;
36086         layout = config.layout;
36087         delete config.layout;
36088     }
36089     if (layout.xtype && !layout.getEl) {
36090         // then layout needs constructing..
36091         layout = Roo.factory(layout, Roo);
36092     }
36093     */
36094     
36095     config.el =  config.layout.getEl();
36096     
36097     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36098     
36099     config.layout.monitorWindowResize = false; // turn off autosizing
36100     this.layout = config.layout;
36101     this.layout.getEl().addClass("roo-layout-nested-layout");
36102     
36103     
36104     
36105     
36106 };
36107
36108 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36109
36110     setSize : function(width, height){
36111         if(!this.ignoreResize(width, height)){
36112             var size = this.adjustForComponents(width, height);
36113             var el = this.layout.getEl();
36114             if (size.height < 1) {
36115                 el.setWidth(size.width);   
36116             } else {
36117                 el.setSize(size.width, size.height);
36118             }
36119             var touch = el.dom.offsetWidth;
36120             this.layout.layout();
36121             // ie requires a double layout on the first pass
36122             if(Roo.isIE && !this.initialized){
36123                 this.initialized = true;
36124                 this.layout.layout();
36125             }
36126         }
36127     },
36128     
36129     // activate all subpanels if not currently active..
36130     
36131     setActiveState : function(active){
36132         this.active = active;
36133         this.setActiveClass(active);
36134         
36135         if(!active){
36136             this.fireEvent("deactivate", this);
36137             return;
36138         }
36139         
36140         this.fireEvent("activate", this);
36141         // not sure if this should happen before or after..
36142         if (!this.layout) {
36143             return; // should not happen..
36144         }
36145         var reg = false;
36146         for (var r in this.layout.regions) {
36147             reg = this.layout.getRegion(r);
36148             if (reg.getActivePanel()) {
36149                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36150                 reg.setActivePanel(reg.getActivePanel());
36151                 continue;
36152             }
36153             if (!reg.panels.length) {
36154                 continue;
36155             }
36156             reg.showPanel(reg.getPanel(0));
36157         }
36158         
36159         
36160         
36161         
36162     },
36163     
36164     /**
36165      * Returns the nested BorderLayout for this panel
36166      * @return {Roo.BorderLayout} 
36167      */
36168     getLayout : function(){
36169         return this.layout;
36170     },
36171     
36172      /**
36173      * Adds a xtype elements to the layout of the nested panel
36174      * <pre><code>
36175
36176 panel.addxtype({
36177        xtype : 'ContentPanel',
36178        region: 'west',
36179        items: [ .... ]
36180    }
36181 );
36182
36183 panel.addxtype({
36184         xtype : 'NestedLayoutPanel',
36185         region: 'west',
36186         layout: {
36187            center: { },
36188            west: { }   
36189         },
36190         items : [ ... list of content panels or nested layout panels.. ]
36191    }
36192 );
36193 </code></pre>
36194      * @param {Object} cfg Xtype definition of item to add.
36195      */
36196     addxtype : function(cfg) {
36197         return this.layout.addxtype(cfg);
36198     
36199     }
36200 });        /*
36201  * Based on:
36202  * Ext JS Library 1.1.1
36203  * Copyright(c) 2006-2007, Ext JS, LLC.
36204  *
36205  * Originally Released Under LGPL - original licence link has changed is not relivant.
36206  *
36207  * Fork - LGPL
36208  * <script type="text/javascript">
36209  */
36210 /**
36211  * @class Roo.TabPanel
36212  * @extends Roo.util.Observable
36213  * A lightweight tab container.
36214  * <br><br>
36215  * Usage:
36216  * <pre><code>
36217 // basic tabs 1, built from existing content
36218 var tabs = new Roo.TabPanel("tabs1");
36219 tabs.addTab("script", "View Script");
36220 tabs.addTab("markup", "View Markup");
36221 tabs.activate("script");
36222
36223 // more advanced tabs, built from javascript
36224 var jtabs = new Roo.TabPanel("jtabs");
36225 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36226
36227 // set up the UpdateManager
36228 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36229 var updater = tab2.getUpdateManager();
36230 updater.setDefaultUrl("ajax1.htm");
36231 tab2.on('activate', updater.refresh, updater, true);
36232
36233 // Use setUrl for Ajax loading
36234 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36235 tab3.setUrl("ajax2.htm", null, true);
36236
36237 // Disabled tab
36238 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36239 tab4.disable();
36240
36241 jtabs.activate("jtabs-1");
36242  * </code></pre>
36243  * @constructor
36244  * Create a new TabPanel.
36245  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36246  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36247  */
36248 Roo.bootstrap.panel.Tabs = function(config){
36249     /**
36250     * The container element for this TabPanel.
36251     * @type Roo.Element
36252     */
36253     this.el = Roo.get(config.el);
36254     delete config.el;
36255     if(config){
36256         if(typeof config == "boolean"){
36257             this.tabPosition = config ? "bottom" : "top";
36258         }else{
36259             Roo.apply(this, config);
36260         }
36261     }
36262     
36263     if(this.tabPosition == "bottom"){
36264         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36265         this.el.addClass("roo-tabs-bottom");
36266     }
36267     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36268     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36269     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36270     if(Roo.isIE){
36271         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36272     }
36273     if(this.tabPosition != "bottom"){
36274         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36275          * @type Roo.Element
36276          */
36277         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36278         this.el.addClass("roo-tabs-top");
36279     }
36280     this.items = [];
36281
36282     this.bodyEl.setStyle("position", "relative");
36283
36284     this.active = null;
36285     this.activateDelegate = this.activate.createDelegate(this);
36286
36287     this.addEvents({
36288         /**
36289          * @event tabchange
36290          * Fires when the active tab changes
36291          * @param {Roo.TabPanel} this
36292          * @param {Roo.TabPanelItem} activePanel The new active tab
36293          */
36294         "tabchange": true,
36295         /**
36296          * @event beforetabchange
36297          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
36298          * @param {Roo.TabPanel} this
36299          * @param {Object} e Set cancel to true on this object to cancel the tab change
36300          * @param {Roo.TabPanelItem} tab The tab being changed to
36301          */
36302         "beforetabchange" : true
36303     });
36304
36305     Roo.EventManager.onWindowResize(this.onResize, this);
36306     this.cpad = this.el.getPadding("lr");
36307     this.hiddenCount = 0;
36308
36309
36310     // toolbar on the tabbar support...
36311     if (this.toolbar) {
36312         alert("no toolbar support yet");
36313         this.toolbar  = false;
36314         /*
36315         var tcfg = this.toolbar;
36316         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
36317         this.toolbar = new Roo.Toolbar(tcfg);
36318         if (Roo.isSafari) {
36319             var tbl = tcfg.container.child('table', true);
36320             tbl.setAttribute('width', '100%');
36321         }
36322         */
36323         
36324     }
36325    
36326
36327
36328     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
36329 };
36330
36331 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
36332     /*
36333      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
36334      */
36335     tabPosition : "top",
36336     /*
36337      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
36338      */
36339     currentTabWidth : 0,
36340     /*
36341      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
36342      */
36343     minTabWidth : 40,
36344     /*
36345      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36346      */
36347     maxTabWidth : 250,
36348     /*
36349      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
36350      */
36351     preferredTabWidth : 175,
36352     /*
36353      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
36354      */
36355     resizeTabs : false,
36356     /*
36357      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
36358      */
36359     monitorResize : true,
36360     /*
36361      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
36362      */
36363     toolbar : false,
36364
36365     /**
36366      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
36367      * @param {String} id The id of the div to use <b>or create</b>
36368      * @param {String} text The text for the tab
36369      * @param {String} content (optional) Content to put in the TabPanelItem body
36370      * @param {Boolean} closable (optional) True to create a close icon on the tab
36371      * @return {Roo.TabPanelItem} The created TabPanelItem
36372      */
36373     addTab : function(id, text, content, closable, tpl)
36374     {
36375         var item = new Roo.bootstrap.panel.TabItem({
36376             panel: this,
36377             id : id,
36378             text : text,
36379             closable : closable,
36380             tpl : tpl
36381         });
36382         this.addTabItem(item);
36383         if(content){
36384             item.setContent(content);
36385         }
36386         return item;
36387     },
36388
36389     /**
36390      * Returns the {@link Roo.TabPanelItem} with the specified id/index
36391      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
36392      * @return {Roo.TabPanelItem}
36393      */
36394     getTab : function(id){
36395         return this.items[id];
36396     },
36397
36398     /**
36399      * Hides the {@link Roo.TabPanelItem} with the specified id/index
36400      * @param {String/Number} id The id or index of the TabPanelItem to hide.
36401      */
36402     hideTab : function(id){
36403         var t = this.items[id];
36404         if(!t.isHidden()){
36405            t.setHidden(true);
36406            this.hiddenCount++;
36407            this.autoSizeTabs();
36408         }
36409     },
36410
36411     /**
36412      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36413      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36414      */
36415     unhideTab : function(id){
36416         var t = this.items[id];
36417         if(t.isHidden()){
36418            t.setHidden(false);
36419            this.hiddenCount--;
36420            this.autoSizeTabs();
36421         }
36422     },
36423
36424     /**
36425      * Adds an existing {@link Roo.TabPanelItem}.
36426      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36427      */
36428     addTabItem : function(item){
36429         this.items[item.id] = item;
36430         this.items.push(item);
36431       //  if(this.resizeTabs){
36432     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36433   //         this.autoSizeTabs();
36434 //        }else{
36435 //            item.autoSize();
36436        // }
36437     },
36438
36439     /**
36440      * Removes a {@link Roo.TabPanelItem}.
36441      * @param {String/Number} id The id or index of the TabPanelItem to remove.
36442      */
36443     removeTab : function(id){
36444         var items = this.items;
36445         var tab = items[id];
36446         if(!tab) { return; }
36447         var index = items.indexOf(tab);
36448         if(this.active == tab && items.length > 1){
36449             var newTab = this.getNextAvailable(index);
36450             if(newTab) {
36451                 newTab.activate();
36452             }
36453         }
36454         this.stripEl.dom.removeChild(tab.pnode.dom);
36455         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36456             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36457         }
36458         items.splice(index, 1);
36459         delete this.items[tab.id];
36460         tab.fireEvent("close", tab);
36461         tab.purgeListeners();
36462         this.autoSizeTabs();
36463     },
36464
36465     getNextAvailable : function(start){
36466         var items = this.items;
36467         var index = start;
36468         // look for a next tab that will slide over to
36469         // replace the one being removed
36470         while(index < items.length){
36471             var item = items[++index];
36472             if(item && !item.isHidden()){
36473                 return item;
36474             }
36475         }
36476         // if one isn't found select the previous tab (on the left)
36477         index = start;
36478         while(index >= 0){
36479             var item = items[--index];
36480             if(item && !item.isHidden()){
36481                 return item;
36482             }
36483         }
36484         return null;
36485     },
36486
36487     /**
36488      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36489      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36490      */
36491     disableTab : function(id){
36492         var tab = this.items[id];
36493         if(tab && this.active != tab){
36494             tab.disable();
36495         }
36496     },
36497
36498     /**
36499      * Enables a {@link Roo.TabPanelItem} that is disabled.
36500      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36501      */
36502     enableTab : function(id){
36503         var tab = this.items[id];
36504         tab.enable();
36505     },
36506
36507     /**
36508      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36509      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36510      * @return {Roo.TabPanelItem} The TabPanelItem.
36511      */
36512     activate : function(id){
36513         var tab = this.items[id];
36514         if(!tab){
36515             return null;
36516         }
36517         if(tab == this.active || tab.disabled){
36518             return tab;
36519         }
36520         var e = {};
36521         this.fireEvent("beforetabchange", this, e, tab);
36522         if(e.cancel !== true && !tab.disabled){
36523             if(this.active){
36524                 this.active.hide();
36525             }
36526             this.active = this.items[id];
36527             this.active.show();
36528             this.fireEvent("tabchange", this, this.active);
36529         }
36530         return tab;
36531     },
36532
36533     /**
36534      * Gets the active {@link Roo.TabPanelItem}.
36535      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36536      */
36537     getActiveTab : function(){
36538         return this.active;
36539     },
36540
36541     /**
36542      * Updates the tab body element to fit the height of the container element
36543      * for overflow scrolling
36544      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36545      */
36546     syncHeight : function(targetHeight){
36547         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36548         var bm = this.bodyEl.getMargins();
36549         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36550         this.bodyEl.setHeight(newHeight);
36551         return newHeight;
36552     },
36553
36554     onResize : function(){
36555         if(this.monitorResize){
36556             this.autoSizeTabs();
36557         }
36558     },
36559
36560     /**
36561      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36562      */
36563     beginUpdate : function(){
36564         this.updating = true;
36565     },
36566
36567     /**
36568      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36569      */
36570     endUpdate : function(){
36571         this.updating = false;
36572         this.autoSizeTabs();
36573     },
36574
36575     /**
36576      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36577      */
36578     autoSizeTabs : function(){
36579         var count = this.items.length;
36580         var vcount = count - this.hiddenCount;
36581         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36582             return;
36583         }
36584         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36585         var availWidth = Math.floor(w / vcount);
36586         var b = this.stripBody;
36587         if(b.getWidth() > w){
36588             var tabs = this.items;
36589             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36590             if(availWidth < this.minTabWidth){
36591                 /*if(!this.sleft){    // incomplete scrolling code
36592                     this.createScrollButtons();
36593                 }
36594                 this.showScroll();
36595                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36596             }
36597         }else{
36598             if(this.currentTabWidth < this.preferredTabWidth){
36599                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36600             }
36601         }
36602     },
36603
36604     /**
36605      * Returns the number of tabs in this TabPanel.
36606      * @return {Number}
36607      */
36608      getCount : function(){
36609          return this.items.length;
36610      },
36611
36612     /**
36613      * Resizes all the tabs to the passed width
36614      * @param {Number} The new width
36615      */
36616     setTabWidth : function(width){
36617         this.currentTabWidth = width;
36618         for(var i = 0, len = this.items.length; i < len; i++) {
36619                 if(!this.items[i].isHidden()) {
36620                 this.items[i].setWidth(width);
36621             }
36622         }
36623     },
36624
36625     /**
36626      * Destroys this TabPanel
36627      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36628      */
36629     destroy : function(removeEl){
36630         Roo.EventManager.removeResizeListener(this.onResize, this);
36631         for(var i = 0, len = this.items.length; i < len; i++){
36632             this.items[i].purgeListeners();
36633         }
36634         if(removeEl === true){
36635             this.el.update("");
36636             this.el.remove();
36637         }
36638     },
36639     
36640     createStrip : function(container)
36641     {
36642         var strip = document.createElement("nav");
36643         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36644         container.appendChild(strip);
36645         return strip;
36646     },
36647     
36648     createStripList : function(strip)
36649     {
36650         // div wrapper for retard IE
36651         // returns the "tr" element.
36652         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36653         //'<div class="x-tabs-strip-wrap">'+
36654           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36655           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36656         return strip.firstChild; //.firstChild.firstChild.firstChild;
36657     },
36658     createBody : function(container)
36659     {
36660         var body = document.createElement("div");
36661         Roo.id(body, "tab-body");
36662         //Roo.fly(body).addClass("x-tabs-body");
36663         Roo.fly(body).addClass("tab-content");
36664         container.appendChild(body);
36665         return body;
36666     },
36667     createItemBody :function(bodyEl, id){
36668         var body = Roo.getDom(id);
36669         if(!body){
36670             body = document.createElement("div");
36671             body.id = id;
36672         }
36673         //Roo.fly(body).addClass("x-tabs-item-body");
36674         Roo.fly(body).addClass("tab-pane");
36675          bodyEl.insertBefore(body, bodyEl.firstChild);
36676         return body;
36677     },
36678     /** @private */
36679     createStripElements :  function(stripEl, text, closable, tpl)
36680     {
36681         var td = document.createElement("li"); // was td..
36682         
36683         
36684         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36685         
36686         
36687         stripEl.appendChild(td);
36688         /*if(closable){
36689             td.className = "x-tabs-closable";
36690             if(!this.closeTpl){
36691                 this.closeTpl = new Roo.Template(
36692                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36693                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36694                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36695                 );
36696             }
36697             var el = this.closeTpl.overwrite(td, {"text": text});
36698             var close = el.getElementsByTagName("div")[0];
36699             var inner = el.getElementsByTagName("em")[0];
36700             return {"el": el, "close": close, "inner": inner};
36701         } else {
36702         */
36703         // not sure what this is..
36704 //            if(!this.tabTpl){
36705                 //this.tabTpl = new Roo.Template(
36706                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36707                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36708                 //);
36709 //                this.tabTpl = new Roo.Template(
36710 //                   '<a href="#">' +
36711 //                   '<span unselectable="on"' +
36712 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36713 //                            ' >{text}</span></a>'
36714 //                );
36715 //                
36716 //            }
36717
36718
36719             var template = tpl || this.tabTpl || false;
36720             
36721             if(!template){
36722                 
36723                 template = new Roo.Template(
36724                    '<a href="#">' +
36725                    '<span unselectable="on"' +
36726                             (this.disableTooltips ? '' : ' title="{text}"') +
36727                             ' >{text}</span></a>'
36728                 );
36729             }
36730             
36731             switch (typeof(template)) {
36732                 case 'object' :
36733                     break;
36734                 case 'string' :
36735                     template = new Roo.Template(template);
36736                     break;
36737                 default :
36738                     break;
36739             }
36740             
36741             var el = template.overwrite(td, {"text": text});
36742             
36743             var inner = el.getElementsByTagName("span")[0];
36744             
36745             return {"el": el, "inner": inner};
36746             
36747     }
36748         
36749     
36750 });
36751
36752 /**
36753  * @class Roo.TabPanelItem
36754  * @extends Roo.util.Observable
36755  * Represents an individual item (tab plus body) in a TabPanel.
36756  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36757  * @param {String} id The id of this TabPanelItem
36758  * @param {String} text The text for the tab of this TabPanelItem
36759  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36760  */
36761 Roo.bootstrap.panel.TabItem = function(config){
36762     /**
36763      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36764      * @type Roo.TabPanel
36765      */
36766     this.tabPanel = config.panel;
36767     /**
36768      * The id for this TabPanelItem
36769      * @type String
36770      */
36771     this.id = config.id;
36772     /** @private */
36773     this.disabled = false;
36774     /** @private */
36775     this.text = config.text;
36776     /** @private */
36777     this.loaded = false;
36778     this.closable = config.closable;
36779
36780     /**
36781      * The body element for this TabPanelItem.
36782      * @type Roo.Element
36783      */
36784     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36785     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36786     this.bodyEl.setStyle("display", "block");
36787     this.bodyEl.setStyle("zoom", "1");
36788     //this.hideAction();
36789
36790     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36791     /** @private */
36792     this.el = Roo.get(els.el);
36793     this.inner = Roo.get(els.inner, true);
36794     this.textEl = Roo.get(this.el.dom.firstChild, true);
36795     this.pnode = Roo.get(els.el.parentNode, true);
36796     this.el.on("mousedown", this.onTabMouseDown, this);
36797     this.el.on("click", this.onTabClick, this);
36798     /** @private */
36799     if(config.closable){
36800         var c = Roo.get(els.close, true);
36801         c.dom.title = this.closeText;
36802         c.addClassOnOver("close-over");
36803         c.on("click", this.closeClick, this);
36804      }
36805
36806     this.addEvents({
36807          /**
36808          * @event activate
36809          * Fires when this tab becomes the active tab.
36810          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36811          * @param {Roo.TabPanelItem} this
36812          */
36813         "activate": true,
36814         /**
36815          * @event beforeclose
36816          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36817          * @param {Roo.TabPanelItem} this
36818          * @param {Object} e Set cancel to true on this object to cancel the close.
36819          */
36820         "beforeclose": true,
36821         /**
36822          * @event close
36823          * Fires when this tab is closed.
36824          * @param {Roo.TabPanelItem} this
36825          */
36826          "close": true,
36827         /**
36828          * @event deactivate
36829          * Fires when this tab is no longer the active tab.
36830          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36831          * @param {Roo.TabPanelItem} this
36832          */
36833          "deactivate" : true
36834     });
36835     this.hidden = false;
36836
36837     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36838 };
36839
36840 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36841            {
36842     purgeListeners : function(){
36843        Roo.util.Observable.prototype.purgeListeners.call(this);
36844        this.el.removeAllListeners();
36845     },
36846     /**
36847      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36848      */
36849     show : function(){
36850         this.pnode.addClass("active");
36851         this.showAction();
36852         if(Roo.isOpera){
36853             this.tabPanel.stripWrap.repaint();
36854         }
36855         this.fireEvent("activate", this.tabPanel, this);
36856     },
36857
36858     /**
36859      * Returns true if this tab is the active tab.
36860      * @return {Boolean}
36861      */
36862     isActive : function(){
36863         return this.tabPanel.getActiveTab() == this;
36864     },
36865
36866     /**
36867      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36868      */
36869     hide : function(){
36870         this.pnode.removeClass("active");
36871         this.hideAction();
36872         this.fireEvent("deactivate", this.tabPanel, this);
36873     },
36874
36875     hideAction : function(){
36876         this.bodyEl.hide();
36877         this.bodyEl.setStyle("position", "absolute");
36878         this.bodyEl.setLeft("-20000px");
36879         this.bodyEl.setTop("-20000px");
36880     },
36881
36882     showAction : function(){
36883         this.bodyEl.setStyle("position", "relative");
36884         this.bodyEl.setTop("");
36885         this.bodyEl.setLeft("");
36886         this.bodyEl.show();
36887     },
36888
36889     /**
36890      * Set the tooltip for the tab.
36891      * @param {String} tooltip The tab's tooltip
36892      */
36893     setTooltip : function(text){
36894         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36895             this.textEl.dom.qtip = text;
36896             this.textEl.dom.removeAttribute('title');
36897         }else{
36898             this.textEl.dom.title = text;
36899         }
36900     },
36901
36902     onTabClick : function(e){
36903         e.preventDefault();
36904         this.tabPanel.activate(this.id);
36905     },
36906
36907     onTabMouseDown : function(e){
36908         e.preventDefault();
36909         this.tabPanel.activate(this.id);
36910     },
36911 /*
36912     getWidth : function(){
36913         return this.inner.getWidth();
36914     },
36915
36916     setWidth : function(width){
36917         var iwidth = width - this.pnode.getPadding("lr");
36918         this.inner.setWidth(iwidth);
36919         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36920         this.pnode.setWidth(width);
36921     },
36922 */
36923     /**
36924      * Show or hide the tab
36925      * @param {Boolean} hidden True to hide or false to show.
36926      */
36927     setHidden : function(hidden){
36928         this.hidden = hidden;
36929         this.pnode.setStyle("display", hidden ? "none" : "");
36930     },
36931
36932     /**
36933      * Returns true if this tab is "hidden"
36934      * @return {Boolean}
36935      */
36936     isHidden : function(){
36937         return this.hidden;
36938     },
36939
36940     /**
36941      * Returns the text for this tab
36942      * @return {String}
36943      */
36944     getText : function(){
36945         return this.text;
36946     },
36947     /*
36948     autoSize : function(){
36949         //this.el.beginMeasure();
36950         this.textEl.setWidth(1);
36951         /*
36952          *  #2804 [new] Tabs in Roojs
36953          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36954          */
36955         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36956         //this.el.endMeasure();
36957     //},
36958
36959     /**
36960      * Sets the text for the tab (Note: this also sets the tooltip text)
36961      * @param {String} text The tab's text and tooltip
36962      */
36963     setText : function(text){
36964         this.text = text;
36965         this.textEl.update(text);
36966         this.setTooltip(text);
36967         //if(!this.tabPanel.resizeTabs){
36968         //    this.autoSize();
36969         //}
36970     },
36971     /**
36972      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36973      */
36974     activate : function(){
36975         this.tabPanel.activate(this.id);
36976     },
36977
36978     /**
36979      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36980      */
36981     disable : function(){
36982         if(this.tabPanel.active != this){
36983             this.disabled = true;
36984             this.pnode.addClass("disabled");
36985         }
36986     },
36987
36988     /**
36989      * Enables this TabPanelItem if it was previously disabled.
36990      */
36991     enable : function(){
36992         this.disabled = false;
36993         this.pnode.removeClass("disabled");
36994     },
36995
36996     /**
36997      * Sets the content for this TabPanelItem.
36998      * @param {String} content The content
36999      * @param {Boolean} loadScripts true to look for and load scripts
37000      */
37001     setContent : function(content, loadScripts){
37002         this.bodyEl.update(content, loadScripts);
37003     },
37004
37005     /**
37006      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37007      * @return {Roo.UpdateManager} The UpdateManager
37008      */
37009     getUpdateManager : function(){
37010         return this.bodyEl.getUpdateManager();
37011     },
37012
37013     /**
37014      * Set a URL to be used to load the content for this TabPanelItem.
37015      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37016      * @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)
37017      * @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)
37018      * @return {Roo.UpdateManager} The UpdateManager
37019      */
37020     setUrl : function(url, params, loadOnce){
37021         if(this.refreshDelegate){
37022             this.un('activate', this.refreshDelegate);
37023         }
37024         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37025         this.on("activate", this.refreshDelegate);
37026         return this.bodyEl.getUpdateManager();
37027     },
37028
37029     /** @private */
37030     _handleRefresh : function(url, params, loadOnce){
37031         if(!loadOnce || !this.loaded){
37032             var updater = this.bodyEl.getUpdateManager();
37033             updater.update(url, params, this._setLoaded.createDelegate(this));
37034         }
37035     },
37036
37037     /**
37038      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37039      *   Will fail silently if the setUrl method has not been called.
37040      *   This does not activate the panel, just updates its content.
37041      */
37042     refresh : function(){
37043         if(this.refreshDelegate){
37044            this.loaded = false;
37045            this.refreshDelegate();
37046         }
37047     },
37048
37049     /** @private */
37050     _setLoaded : function(){
37051         this.loaded = true;
37052     },
37053
37054     /** @private */
37055     closeClick : function(e){
37056         var o = {};
37057         e.stopEvent();
37058         this.fireEvent("beforeclose", this, o);
37059         if(o.cancel !== true){
37060             this.tabPanel.removeTab(this.id);
37061         }
37062     },
37063     /**
37064      * The text displayed in the tooltip for the close icon.
37065      * @type String
37066      */
37067     closeText : "Close this tab"
37068 });