roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         
1528         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529             this.el.dom.src =  url;
1530             return;
1531         }
1532         
1533         this.el.select('img', true).first().dom.src =  url;
1534     }
1535     
1536     
1537    
1538 });
1539
1540  /*
1541  * - LGPL
1542  *
1543  * image
1544  * 
1545  */
1546
1547
1548 /**
1549  * @class Roo.bootstrap.Link
1550  * @extends Roo.bootstrap.Component
1551  * Bootstrap Link Class
1552  * @cfg {String} alt image alternative text
1553  * @cfg {String} href a tag href
1554  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555  * @cfg {String} html the content of the link.
1556  * @cfg {String} anchor name for the anchor link
1557  * @cfg {String} fa - favicon
1558
1559  * @cfg {Boolean} preventDefault (true | false) default false
1560
1561  * 
1562  * @constructor
1563  * Create a new Input
1564  * @param {Object} config The config object
1565  */
1566
1567 Roo.bootstrap.Link = function(config){
1568     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1569     
1570     this.addEvents({
1571         // img events
1572         /**
1573          * @event click
1574          * The img click event for the img.
1575          * @param {Roo.EventObject} e
1576          */
1577         "click" : true
1578     });
1579 };
1580
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1582     
1583     href: false,
1584     target: false,
1585     preventDefault: false,
1586     anchor : false,
1587     alt : false,
1588     fa: false,
1589
1590
1591     getAutoCreate : function()
1592     {
1593         var html = this.html || '';
1594         
1595         if (this.fa !== false) {
1596             html = '<i class="fa fa-' + this.fa + '"></i>';
1597         }
1598         var cfg = {
1599             tag: 'a'
1600         };
1601         // anchor's do not require html/href...
1602         if (this.anchor === false) {
1603             cfg.html = html;
1604             cfg.href = this.href || '#';
1605         } else {
1606             cfg.name = this.anchor;
1607             if (this.html !== false || this.fa !== false) {
1608                 cfg.html = html;
1609             }
1610             if (this.href !== false) {
1611                 cfg.href = this.href;
1612             }
1613         }
1614         
1615         if(this.alt !== false){
1616             cfg.alt = this.alt;
1617         }
1618         
1619         
1620         if(this.target !== false) {
1621             cfg.target = this.target;
1622         }
1623         
1624         return cfg;
1625     },
1626     
1627     initEvents: function() {
1628         
1629         if(!this.href || this.preventDefault){
1630             this.el.on('click', this.onClick, this);
1631         }
1632     },
1633     
1634     onClick : function(e)
1635     {
1636         if(this.preventDefault){
1637             e.preventDefault();
1638         }
1639         //Roo.log('img onclick');
1640         this.fireEvent('click', this, e);
1641     }
1642    
1643 });
1644
1645  /*
1646  * - LGPL
1647  *
1648  * header
1649  * 
1650  */
1651
1652 /**
1653  * @class Roo.bootstrap.Header
1654  * @extends Roo.bootstrap.Component
1655  * Bootstrap Header class
1656  * @cfg {String} html content of header
1657  * @cfg {Number} level (1|2|3|4|5|6) default 1
1658  * 
1659  * @constructor
1660  * Create a new Header
1661  * @param {Object} config The config object
1662  */
1663
1664
1665 Roo.bootstrap.Header  = function(config){
1666     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1667 };
1668
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1670     
1671     //href : false,
1672     html : false,
1673     level : 1,
1674     
1675     
1676     
1677     getAutoCreate : function(){
1678         
1679         
1680         
1681         var cfg = {
1682             tag: 'h' + (1 *this.level),
1683             html: this.html || ''
1684         } ;
1685         
1686         return cfg;
1687     }
1688    
1689 });
1690
1691  
1692
1693  /*
1694  * Based on:
1695  * Ext JS Library 1.1.1
1696  * Copyright(c) 2006-2007, Ext JS, LLC.
1697  *
1698  * Originally Released Under LGPL - original licence link has changed is not relivant.
1699  *
1700  * Fork - LGPL
1701  * <script type="text/javascript">
1702  */
1703  
1704 /**
1705  * @class Roo.bootstrap.MenuMgr
1706  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1707  * @singleton
1708  */
1709 Roo.bootstrap.MenuMgr = function(){
1710    var menus, active, groups = {}, attached = false, lastShow = new Date();
1711
1712    // private - called when first menu is created
1713    function init(){
1714        menus = {};
1715        active = new Roo.util.MixedCollection();
1716        Roo.get(document).addKeyListener(27, function(){
1717            if(active.length > 0){
1718                hideAll();
1719            }
1720        });
1721    }
1722
1723    // private
1724    function hideAll(){
1725        if(active && active.length > 0){
1726            var c = active.clone();
1727            c.each(function(m){
1728                m.hide();
1729            });
1730        }
1731    }
1732
1733    // private
1734    function onHide(m){
1735        active.remove(m);
1736        if(active.length < 1){
1737            Roo.get(document).un("mouseup", onMouseDown);
1738             
1739            attached = false;
1740        }
1741    }
1742
1743    // private
1744    function onShow(m){
1745        var last = active.last();
1746        lastShow = new Date();
1747        active.add(m);
1748        if(!attached){
1749           Roo.get(document).on("mouseup", onMouseDown);
1750            
1751            attached = true;
1752        }
1753        if(m.parentMenu){
1754           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755           m.parentMenu.activeChild = m;
1756        }else if(last && last.isVisible()){
1757           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1758        }
1759    }
1760
1761    // private
1762    function onBeforeHide(m){
1763        if(m.activeChild){
1764            m.activeChild.hide();
1765        }
1766        if(m.autoHideTimer){
1767            clearTimeout(m.autoHideTimer);
1768            delete m.autoHideTimer;
1769        }
1770    }
1771
1772    // private
1773    function onBeforeShow(m){
1774        var pm = m.parentMenu;
1775        if(!pm && !m.allowOtherMenus){
1776            hideAll();
1777        }else if(pm && pm.activeChild && active != m){
1778            pm.activeChild.hide();
1779        }
1780    }
1781
1782    // private this should really trigger on mouseup..
1783    function onMouseDown(e){
1784         Roo.log("on Mouse Up");
1785         
1786         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787             Roo.log("MenuManager hideAll");
1788             hideAll();
1789             e.stopEvent();
1790         }
1791         
1792         
1793    }
1794
1795    // private
1796    function onBeforeCheck(mi, state){
1797        if(state){
1798            var g = groups[mi.group];
1799            for(var i = 0, l = g.length; i < l; i++){
1800                if(g[i] != mi){
1801                    g[i].setChecked(false);
1802                }
1803            }
1804        }
1805    }
1806
1807    return {
1808
1809        /**
1810         * Hides all menus that are currently visible
1811         */
1812        hideAll : function(){
1813             hideAll();  
1814        },
1815
1816        // private
1817        register : function(menu){
1818            if(!menus){
1819                init();
1820            }
1821            menus[menu.id] = menu;
1822            menu.on("beforehide", onBeforeHide);
1823            menu.on("hide", onHide);
1824            menu.on("beforeshow", onBeforeShow);
1825            menu.on("show", onShow);
1826            var g = menu.group;
1827            if(g && menu.events["checkchange"]){
1828                if(!groups[g]){
1829                    groups[g] = [];
1830                }
1831                groups[g].push(menu);
1832                menu.on("checkchange", onCheck);
1833            }
1834        },
1835
1836         /**
1837          * Returns a {@link Roo.menu.Menu} object
1838          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839          * be used to generate and return a new Menu instance.
1840          */
1841        get : function(menu){
1842            if(typeof menu == "string"){ // menu id
1843                return menus[menu];
1844            }else if(menu.events){  // menu instance
1845                return menu;
1846            }
1847            /*else if(typeof menu.length == 'number'){ // array of menu items?
1848                return new Roo.bootstrap.Menu({items:menu});
1849            }else{ // otherwise, must be a config
1850                return new Roo.bootstrap.Menu(menu);
1851            }
1852            */
1853            return false;
1854        },
1855
1856        // private
1857        unregister : function(menu){
1858            delete menus[menu.id];
1859            menu.un("beforehide", onBeforeHide);
1860            menu.un("hide", onHide);
1861            menu.un("beforeshow", onBeforeShow);
1862            menu.un("show", onShow);
1863            var g = menu.group;
1864            if(g && menu.events["checkchange"]){
1865                groups[g].remove(menu);
1866                menu.un("checkchange", onCheck);
1867            }
1868        },
1869
1870        // private
1871        registerCheckable : function(menuItem){
1872            var g = menuItem.group;
1873            if(g){
1874                if(!groups[g]){
1875                    groups[g] = [];
1876                }
1877                groups[g].push(menuItem);
1878                menuItem.on("beforecheckchange", onBeforeCheck);
1879            }
1880        },
1881
1882        // private
1883        unregisterCheckable : function(menuItem){
1884            var g = menuItem.group;
1885            if(g){
1886                groups[g].remove(menuItem);
1887                menuItem.un("beforecheckchange", onBeforeCheck);
1888            }
1889        }
1890    };
1891 }();/*
1892  * - LGPL
1893  *
1894  * menu
1895  * 
1896  */
1897
1898 /**
1899  * @class Roo.bootstrap.Menu
1900  * @extends Roo.bootstrap.Component
1901  * Bootstrap Menu class - container for MenuItems
1902  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1904  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1905  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1906  * 
1907  * @constructor
1908  * Create a new Menu
1909  * @param {Object} config The config object
1910  */
1911
1912
1913 Roo.bootstrap.Menu = function(config){
1914     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915     if (this.registerMenu && this.type != 'treeview')  {
1916         Roo.bootstrap.MenuMgr.register(this);
1917     }
1918     this.addEvents({
1919         /**
1920          * @event beforeshow
1921          * Fires before this menu is displayed
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforeshow : true,
1925         /**
1926          * @event beforehide
1927          * Fires before this menu is hidden
1928          * @param {Roo.menu.Menu} this
1929          */
1930         beforehide : true,
1931         /**
1932          * @event show
1933          * Fires after this menu is displayed
1934          * @param {Roo.menu.Menu} this
1935          */
1936         show : true,
1937         /**
1938          * @event hide
1939          * Fires after this menu is hidden
1940          * @param {Roo.menu.Menu} this
1941          */
1942         hide : true,
1943         /**
1944          * @event click
1945          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946          * @param {Roo.menu.Menu} this
1947          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948          * @param {Roo.EventObject} e
1949          */
1950         click : true,
1951         /**
1952          * @event mouseover
1953          * Fires when the mouse is hovering over this menu
1954          * @param {Roo.menu.Menu} this
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          */
1958         mouseover : true,
1959         /**
1960          * @event mouseout
1961          * Fires when the mouse exits this menu
1962          * @param {Roo.menu.Menu} this
1963          * @param {Roo.EventObject} e
1964          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1965          */
1966         mouseout : true,
1967         /**
1968          * @event itemclick
1969          * Fires when a menu item contained in this menu is clicked
1970          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971          * @param {Roo.EventObject} e
1972          */
1973         itemclick: true
1974     });
1975     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1976 };
1977
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1979     
1980    /// html : false,
1981     //align : '',
1982     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1983     type: false,
1984     /**
1985      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1986      */
1987     registerMenu : true,
1988     
1989     menuItems :false, // stores the menu items..
1990     
1991     hidden:true,
1992         
1993     parentMenu : false,
1994     
1995     stopEvent : true,
1996     
1997     isLink : false,
1998     
1999     getChildContainer : function() {
2000         return this.el;  
2001     },
2002     
2003     getAutoCreate : function(){
2004          
2005         //if (['right'].indexOf(this.align)!==-1) {
2006         //    cfg.cn[1].cls += ' pull-right'
2007         //}
2008         
2009         
2010         var cfg = {
2011             tag : 'ul',
2012             cls : 'dropdown-menu' ,
2013             style : 'z-index:1000'
2014             
2015         };
2016         
2017         if (this.type === 'submenu') {
2018             cfg.cls = 'submenu active';
2019         }
2020         if (this.type === 'treeview') {
2021             cfg.cls = 'treeview-menu';
2022         }
2023         
2024         return cfg;
2025     },
2026     initEvents : function() {
2027         
2028        // Roo.log("ADD event");
2029        // Roo.log(this.triggerEl.dom);
2030         
2031         this.triggerEl.on('click', this.onTriggerClick, this);
2032         
2033         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2034         
2035         this.triggerEl.addClass('dropdown-toggle');
2036         
2037         if (Roo.isTouch) {
2038             this.el.on('touchstart'  , this.onTouch, this);
2039         }
2040         this.el.on('click' , this.onClick, this);
2041
2042         this.el.on("mouseover", this.onMouseOver, this);
2043         this.el.on("mouseout", this.onMouseOut, this);
2044         
2045     },
2046     
2047     findTargetItem : function(e)
2048     {
2049         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2050         if(!t){
2051             return false;
2052         }
2053         //Roo.log(t);         Roo.log(t.id);
2054         if(t && t.id){
2055             //Roo.log(this.menuitems);
2056             return this.menuitems.get(t.id);
2057             
2058             //return this.items.get(t.menuItemId);
2059         }
2060         
2061         return false;
2062     },
2063     
2064     onTouch : function(e) 
2065     {
2066         Roo.log("menu.onTouch");
2067         //e.stopEvent(); this make the user popdown broken
2068         this.onClick(e);
2069     },
2070     
2071     onClick : function(e)
2072     {
2073         Roo.log("menu.onClick");
2074         
2075         var t = this.findTargetItem(e);
2076         if(!t || t.isContainer){
2077             return;
2078         }
2079         Roo.log(e);
2080         /*
2081         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2082             if(t == this.activeItem && t.shouldDeactivate(e)){
2083                 this.activeItem.deactivate();
2084                 delete this.activeItem;
2085                 return;
2086             }
2087             if(t.canActivate){
2088                 this.setActiveItem(t, true);
2089             }
2090             return;
2091             
2092             
2093         }
2094         */
2095        
2096         Roo.log('pass click event');
2097         
2098         t.onClick(e);
2099         
2100         this.fireEvent("click", this, t, e);
2101         
2102         var _this = this;
2103         
2104         (function() { _this.hide(); }).defer(100);
2105     },
2106     
2107     onMouseOver : function(e){
2108         var t  = this.findTargetItem(e);
2109         //Roo.log(t);
2110         //if(t){
2111         //    if(t.canActivate && !t.disabled){
2112         //        this.setActiveItem(t, true);
2113         //    }
2114         //}
2115         
2116         this.fireEvent("mouseover", this, e, t);
2117     },
2118     isVisible : function(){
2119         return !this.hidden;
2120     },
2121      onMouseOut : function(e){
2122         var t  = this.findTargetItem(e);
2123         
2124         //if(t ){
2125         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2126         //        this.activeItem.deactivate();
2127         //        delete this.activeItem;
2128         //    }
2129         //}
2130         this.fireEvent("mouseout", this, e, t);
2131     },
2132     
2133     
2134     /**
2135      * Displays this menu relative to another element
2136      * @param {String/HTMLElement/Roo.Element} element The element to align to
2137      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138      * the element (defaults to this.defaultAlign)
2139      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2140      */
2141     show : function(el, pos, parentMenu){
2142         this.parentMenu = parentMenu;
2143         if(!this.el){
2144             this.render();
2145         }
2146         this.fireEvent("beforeshow", this);
2147         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2148     },
2149      /**
2150      * Displays this menu at a specific xy position
2151      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2153      */
2154     showAt : function(xy, parentMenu, /* private: */_e){
2155         this.parentMenu = parentMenu;
2156         if(!this.el){
2157             this.render();
2158         }
2159         if(_e !== false){
2160             this.fireEvent("beforeshow", this);
2161             //xy = this.el.adjustForConstraints(xy);
2162         }
2163         
2164         //this.el.show();
2165         this.hideMenuItems();
2166         this.hidden = false;
2167         this.triggerEl.addClass('open');
2168         
2169         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2171         }
2172         
2173         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2174             this.el.setXY(xy);
2175         }
2176         
2177         this.focus();
2178         this.fireEvent("show", this);
2179     },
2180     
2181     focus : function(){
2182         return;
2183         if(!this.hidden){
2184             this.doFocus.defer(50, this);
2185         }
2186     },
2187
2188     doFocus : function(){
2189         if(!this.hidden){
2190             this.focusEl.focus();
2191         }
2192     },
2193
2194     /**
2195      * Hides this menu and optionally all parent menus
2196      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2197      */
2198     hide : function(deep)
2199     {
2200         
2201         this.hideMenuItems();
2202         if(this.el && this.isVisible()){
2203             this.fireEvent("beforehide", this);
2204             if(this.activeItem){
2205                 this.activeItem.deactivate();
2206                 this.activeItem = null;
2207             }
2208             this.triggerEl.removeClass('open');;
2209             this.hidden = true;
2210             this.fireEvent("hide", this);
2211         }
2212         if(deep === true && this.parentMenu){
2213             this.parentMenu.hide(true);
2214         }
2215     },
2216     
2217     onTriggerClick : function(e)
2218     {
2219         Roo.log('trigger click');
2220         
2221         var target = e.getTarget();
2222         
2223         Roo.log(target.nodeName.toLowerCase());
2224         
2225         if(target.nodeName.toLowerCase() === 'i'){
2226             e.preventDefault();
2227         }
2228         
2229     },
2230     
2231     onTriggerPress  : function(e)
2232     {
2233         Roo.log('trigger press');
2234         //Roo.log(e.getTarget());
2235        // Roo.log(this.triggerEl.dom);
2236        
2237         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238         var pel = Roo.get(e.getTarget());
2239         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240             Roo.log('is treeview or dropdown?');
2241             return;
2242         }
2243         
2244         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2245             return;
2246         }
2247         
2248         if (this.isVisible()) {
2249             Roo.log('hide');
2250             this.hide();
2251         } else {
2252             Roo.log('show');
2253             this.show(this.triggerEl, false, false);
2254         }
2255         
2256         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2257             e.stopEvent();
2258         }
2259         
2260     },
2261        
2262     
2263     hideMenuItems : function()
2264     {
2265         Roo.log("hide Menu Items");
2266         if (!this.el) { 
2267             return;
2268         }
2269         //$(backdrop).remove()
2270         this.el.select('.open',true).each(function(aa) {
2271             
2272             aa.removeClass('open');
2273           //var parent = getParent($(this))
2274           //var relatedTarget = { relatedTarget: this }
2275           
2276            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277           //if (e.isDefaultPrevented()) return
2278            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2279         });
2280     },
2281     addxtypeChild : function (tree, cntr) {
2282         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2283           
2284         this.menuitems.add(comp);
2285         return comp;
2286
2287     },
2288     getEl : function()
2289     {
2290         Roo.log(this.el);
2291         return this.el;
2292     }
2293 });
2294
2295  
2296  /*
2297  * - LGPL
2298  *
2299  * menu item
2300  * 
2301  */
2302
2303
2304 /**
2305  * @class Roo.bootstrap.MenuItem
2306  * @extends Roo.bootstrap.Component
2307  * Bootstrap MenuItem class
2308  * @cfg {String} html the menu label
2309  * @cfg {String} href the link
2310  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2313  * @cfg {String} fa favicon to show on left of menu item.
2314  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2315  * 
2316  * 
2317  * @constructor
2318  * Create a new MenuItem
2319  * @param {Object} config The config object
2320  */
2321
2322
2323 Roo.bootstrap.MenuItem = function(config){
2324     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2325     this.addEvents({
2326         // raw events
2327         /**
2328          * @event click
2329          * The raw click event for the entire grid.
2330          * @param {Roo.bootstrap.MenuItem} this
2331          * @param {Roo.EventObject} e
2332          */
2333         "click" : true
2334     });
2335 };
2336
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2338     
2339     href : false,
2340     html : false,
2341     preventDefault: false,
2342     isContainer : false,
2343     active : false,
2344     fa: false,
2345     
2346     getAutoCreate : function(){
2347         
2348         if(this.isContainer){
2349             return {
2350                 tag: 'li',
2351                 cls: 'dropdown-menu-item'
2352             };
2353         }
2354         var ctag = {
2355             tag: 'span',
2356             html: 'Link'
2357         };
2358         
2359         var anc = {
2360             tag : 'a',
2361             href : '#',
2362             cn : [  ]
2363         };
2364         
2365         if (this.fa !== false) {
2366             anc.cn.push({
2367                 tag : 'i',
2368                 cls : 'fa fa-' + this.fa
2369             });
2370         }
2371         
2372         anc.cn.push(ctag);
2373         
2374         
2375         var cfg= {
2376             tag: 'li',
2377             cls: 'dropdown-menu-item',
2378             cn: [ anc ]
2379         };
2380         if (this.parent().type == 'treeview') {
2381             cfg.cls = 'treeview-menu';
2382         }
2383         if (this.active) {
2384             cfg.cls += ' active';
2385         }
2386         
2387         
2388         
2389         anc.href = this.href || cfg.cn[0].href ;
2390         ctag.html = this.html || cfg.cn[0].html ;
2391         return cfg;
2392     },
2393     
2394     initEvents: function()
2395     {
2396         if (this.parent().type == 'treeview') {
2397             this.el.select('a').on('click', this.onClick, this);
2398         }
2399         if (this.menu) {
2400             this.menu.parentType = this.xtype;
2401             this.menu.triggerEl = this.el;
2402             this.menu = this.addxtype(Roo.apply({}, this.menu));
2403         }
2404         
2405     },
2406     onClick : function(e)
2407     {
2408         Roo.log('item on click ');
2409         
2410         if(this.preventDefault){
2411             e.preventDefault();
2412         }
2413         //this.parent().hideMenuItems();
2414         
2415         this.fireEvent('click', this, e);
2416     },
2417     getEl : function()
2418     {
2419         return this.el;
2420     } 
2421 });
2422
2423  
2424
2425  /*
2426  * - LGPL
2427  *
2428  * menu separator
2429  * 
2430  */
2431
2432
2433 /**
2434  * @class Roo.bootstrap.MenuSeparator
2435  * @extends Roo.bootstrap.Component
2436  * Bootstrap MenuSeparator class
2437  * 
2438  * @constructor
2439  * Create a new MenuItem
2440  * @param {Object} config The config object
2441  */
2442
2443
2444 Roo.bootstrap.MenuSeparator = function(config){
2445     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2446 };
2447
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2449     
2450     getAutoCreate : function(){
2451         var cfg = {
2452             cls: 'divider',
2453             tag : 'li'
2454         };
2455         
2456         return cfg;
2457     }
2458    
2459 });
2460
2461  
2462
2463  
2464 /*
2465 * Licence: LGPL
2466 */
2467
2468 /**
2469  * @class Roo.bootstrap.Modal
2470  * @extends Roo.bootstrap.Component
2471  * Bootstrap Modal class
2472  * @cfg {String} title Title of dialog
2473  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2475  * @cfg {Boolean} specificTitle default false
2476  * @cfg {Array} buttons Array of buttons or standard button set..
2477  * @cfg {String} buttonPosition (left|right|center) default right
2478  * @cfg {Boolean} animate default true
2479  * @cfg {Boolean} allow_close default true
2480  * @cfg {Boolean} fitwindow default false
2481  * @cfg {String} size (sm|lg) default empty
2482  *
2483  *
2484  * @constructor
2485  * Create a new Modal Dialog
2486  * @param {Object} config The config object
2487  */
2488
2489 Roo.bootstrap.Modal = function(config){
2490     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event btnclick
2495          * The raw btnclick event for the button
2496          * @param {Roo.EventObject} e
2497          */
2498         "btnclick" : true
2499     });
2500     this.buttons = this.buttons || [];
2501
2502     if (this.tmpl) {
2503         this.tmpl = Roo.factory(this.tmpl);
2504     }
2505
2506 };
2507
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2509
2510     title : 'test dialog',
2511
2512     buttons : false,
2513
2514     // set on load...
2515
2516     html: false,
2517
2518     tmp: false,
2519
2520     specificTitle: false,
2521
2522     buttonPosition: 'right',
2523
2524     allow_close : true,
2525
2526     animate : true,
2527
2528     fitwindow: false,
2529
2530
2531      // private
2532     dialogEl: false,
2533     bodyEl:  false,
2534     footerEl:  false,
2535     titleEl:  false,
2536     closeEl:  false,
2537
2538     size: '',
2539
2540
2541     onRender : function(ct, position)
2542     {
2543         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2544
2545         if(!this.el){
2546             var cfg = Roo.apply({},  this.getAutoCreate());
2547             cfg.id = Roo.id();
2548             //if(!cfg.name){
2549             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2550             //}
2551             //if (!cfg.name.length) {
2552             //    delete cfg.name;
2553            // }
2554             if (this.cls) {
2555                 cfg.cls += ' ' + this.cls;
2556             }
2557             if (this.style) {
2558                 cfg.style = this.style;
2559             }
2560             this.el = Roo.get(document.body).createChild(cfg, position);
2561         }
2562         //var type = this.el.dom.type;
2563
2564
2565         if(this.tabIndex !== undefined){
2566             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2567         }
2568
2569         this.dialogEl = this.el.select('.modal-dialog',true).first();
2570         this.bodyEl = this.el.select('.modal-body',true).first();
2571         this.closeEl = this.el.select('.modal-header .close', true).first();
2572         this.headerEl = this.el.select('.modal-header',true).first();
2573         this.titleEl = this.el.select('.modal-title',true).first();
2574         this.footerEl = this.el.select('.modal-footer',true).first();
2575
2576         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577         this.maskEl.enableDisplayMode("block");
2578         this.maskEl.hide();
2579         //this.el.addClass("x-dlg-modal");
2580
2581         if (this.buttons.length) {
2582             Roo.each(this.buttons, function(bb) {
2583                 var b = Roo.apply({}, bb);
2584                 b.xns = b.xns || Roo.bootstrap;
2585                 b.xtype = b.xtype || 'Button';
2586                 if (typeof(b.listeners) == 'undefined') {
2587                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2588                 }
2589
2590                 var btn = Roo.factory(b);
2591
2592                 btn.render(this.el.select('.modal-footer div').first());
2593
2594             },this);
2595         }
2596         // render the children.
2597         var nitems = [];
2598
2599         if(typeof(this.items) != 'undefined'){
2600             var items = this.items;
2601             delete this.items;
2602
2603             for(var i =0;i < items.length;i++) {
2604                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2605             }
2606         }
2607
2608         this.items = nitems;
2609
2610         // where are these used - they used to be body/close/footer
2611
2612
2613         this.initEvents();
2614         //this.el.addClass([this.fieldClass, this.cls]);
2615
2616     },
2617
2618     getAutoCreate : function(){
2619
2620
2621         var bdy = {
2622                 cls : 'modal-body',
2623                 html : this.html || ''
2624         };
2625
2626         var title = {
2627             tag: 'h4',
2628             cls : 'modal-title',
2629             html : this.title
2630         };
2631
2632         if(this.specificTitle){
2633             title = this.title;
2634
2635         };
2636
2637         var header = [];
2638         if (this.allow_close) {
2639             header.push({
2640                 tag: 'button',
2641                 cls : 'close',
2642                 html : '&times'
2643             });
2644         }
2645
2646         header.push(title);
2647
2648         var size = '';
2649
2650         if(this.size.length){
2651             size = 'modal-' + this.size;
2652         }
2653
2654         var modal = {
2655             cls: "modal",
2656             style : 'display: none',
2657             cn : [
2658                 {
2659                     cls: "modal-dialog " + size,
2660                     cn : [
2661                         {
2662                             cls : "modal-content",
2663                             cn : [
2664                                 {
2665                                     cls : 'modal-header',
2666                                     cn : header
2667                                 },
2668                                 bdy,
2669                                 {
2670                                     cls : 'modal-footer',
2671                                     cn : [
2672                                         {
2673                                             tag: 'div',
2674                                             cls: 'btn-' + this.buttonPosition
2675                                         }
2676                                     ]
2677
2678                                 }
2679
2680
2681                             ]
2682
2683                         }
2684                     ]
2685
2686                 }
2687             ]
2688         };
2689
2690         if(this.animate){
2691             modal.cls += ' fade';
2692         }
2693
2694         return modal;
2695
2696     },
2697     getChildContainer : function() {
2698
2699          return this.bodyEl;
2700
2701     },
2702     getButtonContainer : function() {
2703          return this.el.select('.modal-footer div',true).first();
2704
2705     },
2706     initEvents : function()
2707     {
2708         if (this.allow_close) {
2709             this.closeEl.on('click', this.hide, this);
2710         }
2711         Roo.EventManager.onWindowResize(this.resize, this, true);
2712
2713
2714     },
2715
2716     resize : function()
2717     {
2718         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2719         if (this.fitwindow) {
2720             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2722             this.setSize(w,h);
2723         }
2724     },
2725
2726     setSize : function(w,h)
2727     {
2728         if (!w && !h) {
2729             return;
2730         }
2731         this.resizeTo(w,h);
2732     },
2733
2734     show : function() {
2735
2736         if (!this.rendered) {
2737             this.render();
2738         }
2739
2740         this.el.setStyle('display', 'block');
2741
2742         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2743             var _this = this;
2744             (function(){
2745                 this.el.addClass('in');
2746             }).defer(50, this);
2747         }else{
2748             this.el.addClass('in');
2749
2750         }
2751
2752         // not sure how we can show data in here..
2753         //if (this.tmpl) {
2754         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2755         //}
2756
2757         Roo.get(document.body).addClass("x-body-masked");
2758         
2759         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2760         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2761         this.maskEl.show();
2762         
2763         this.resize();
2764         
2765         this.fireEvent('show', this);
2766
2767         // set zindex here - otherwise it appears to be ignored...
2768         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2769
2770         (function () {
2771             this.items.forEach( function(e) {
2772                 e.layout ? e.layout() : false;
2773
2774             });
2775         }).defer(100,this);
2776
2777     },
2778     hide : function()
2779     {
2780         if(this.fireEvent("beforehide", this) !== false){
2781             this.maskEl.hide();
2782             Roo.get(document.body).removeClass("x-body-masked");
2783             this.el.removeClass('in');
2784             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2785
2786             if(this.animate){ // why
2787                 var _this = this;
2788                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2789             }else{
2790                 this.el.setStyle('display', 'none');
2791             }
2792             this.fireEvent('hide', this);
2793         }
2794     },
2795
2796     addButton : function(str, cb)
2797     {
2798
2799
2800         var b = Roo.apply({}, { html : str } );
2801         b.xns = b.xns || Roo.bootstrap;
2802         b.xtype = b.xtype || 'Button';
2803         if (typeof(b.listeners) == 'undefined') {
2804             b.listeners = { click : cb.createDelegate(this)  };
2805         }
2806
2807         var btn = Roo.factory(b);
2808
2809         btn.render(this.el.select('.modal-footer div').first());
2810
2811         return btn;
2812
2813     },
2814
2815     setDefaultButton : function(btn)
2816     {
2817         //this.el.select('.modal-footer').()
2818     },
2819     diff : false,
2820
2821     resizeTo: function(w,h)
2822     {
2823         // skip.. ?? why??
2824
2825         this.dialogEl.setWidth(w);
2826         if (this.diff === false) {
2827             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2828         }
2829
2830         this.bodyEl.setHeight(h-this.diff);
2831
2832
2833     },
2834     setContentSize  : function(w, h)
2835     {
2836
2837     },
2838     onButtonClick: function(btn,e)
2839     {
2840         //Roo.log([a,b,c]);
2841         this.fireEvent('btnclick', btn.name, e);
2842     },
2843      /**
2844      * Set the title of the Dialog
2845      * @param {String} str new Title
2846      */
2847     setTitle: function(str) {
2848         this.titleEl.dom.innerHTML = str;
2849     },
2850     /**
2851      * Set the body of the Dialog
2852      * @param {String} str new Title
2853      */
2854     setBody: function(str) {
2855         this.bodyEl.dom.innerHTML = str;
2856     },
2857     /**
2858      * Set the body of the Dialog using the template
2859      * @param {Obj} data - apply this data to the template and replace the body contents.
2860      */
2861     applyBody: function(obj)
2862     {
2863         if (!this.tmpl) {
2864             Roo.log("Error - using apply Body without a template");
2865             //code
2866         }
2867         this.tmpl.overwrite(this.bodyEl, obj);
2868     }
2869
2870 });
2871
2872
2873 Roo.apply(Roo.bootstrap.Modal,  {
2874     /**
2875          * Button config that displays a single OK button
2876          * @type Object
2877          */
2878         OK :  [{
2879             name : 'ok',
2880             weight : 'primary',
2881             html : 'OK'
2882         }],
2883         /**
2884          * Button config that displays Yes and No buttons
2885          * @type Object
2886          */
2887         YESNO : [
2888             {
2889                 name  : 'no',
2890                 html : 'No'
2891             },
2892             {
2893                 name  :'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             }
2897         ],
2898
2899         /**
2900          * Button config that displays OK and Cancel buttons
2901          * @type Object
2902          */
2903         OKCANCEL : [
2904             {
2905                name : 'cancel',
2906                 html : 'Cancel'
2907             },
2908             {
2909                 name : 'ok',
2910                 weight : 'primary',
2911                 html : 'OK'
2912             }
2913         ],
2914         /**
2915          * Button config that displays Yes, No and Cancel buttons
2916          * @type Object
2917          */
2918         YESNOCANCEL : [
2919             {
2920                 name : 'yes',
2921                 weight : 'primary',
2922                 html : 'Yes'
2923             },
2924             {
2925                 name : 'no',
2926                 html : 'No'
2927             },
2928             {
2929                 name : 'cancel',
2930                 html : 'Cancel'
2931             }
2932         ],
2933         
2934         zIndex : 10001
2935 });
2936 /*
2937  * - LGPL
2938  *
2939  * messagebox - can be used as a replace
2940  * 
2941  */
2942 /**
2943  * @class Roo.MessageBox
2944  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2945  * Example usage:
2946  *<pre><code>
2947 // Basic alert:
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2949
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2952     if (btn == 'ok'){
2953         // process text value...
2954     }
2955 });
2956
2957 // Show a dialog using config options:
2958 Roo.Msg.show({
2959    title:'Save Changes?',
2960    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961    buttons: Roo.Msg.YESNOCANCEL,
2962    fn: processResult,
2963    animEl: 'elId'
2964 });
2965 </code></pre>
2966  * @singleton
2967  */
2968 Roo.bootstrap.MessageBox = function(){
2969     var dlg, opt, mask, waitTimer;
2970     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971     var buttons, activeTextEl, bwidth;
2972
2973     
2974     // private
2975     var handleButton = function(button){
2976         dlg.hide();
2977         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2978     };
2979
2980     // private
2981     var handleHide = function(){
2982         if(opt && opt.cls){
2983             dlg.el.removeClass(opt.cls);
2984         }
2985         //if(waitTimer){
2986         //    Roo.TaskMgr.stop(waitTimer);
2987         //    waitTimer = null;
2988         //}
2989     };
2990
2991     // private
2992     var updateButtons = function(b){
2993         var width = 0;
2994         if(!b){
2995             buttons["ok"].hide();
2996             buttons["cancel"].hide();
2997             buttons["yes"].hide();
2998             buttons["no"].hide();
2999             //dlg.footer.dom.style.display = 'none';
3000             return width;
3001         }
3002         dlg.footerEl.dom.style.display = '';
3003         for(var k in buttons){
3004             if(typeof buttons[k] != "function"){
3005                 if(b[k]){
3006                     buttons[k].show();
3007                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008                     width += buttons[k].el.getWidth()+15;
3009                 }else{
3010                     buttons[k].hide();
3011                 }
3012             }
3013         }
3014         return width;
3015     };
3016
3017     // private
3018     var handleEsc = function(d, k, e){
3019         if(opt && opt.closable !== false){
3020             dlg.hide();
3021         }
3022         if(e){
3023             e.stopEvent();
3024         }
3025     };
3026
3027     return {
3028         /**
3029          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030          * @return {Roo.BasicDialog} The BasicDialog element
3031          */
3032         getDialog : function(){
3033            if(!dlg){
3034                 dlg = new Roo.bootstrap.Modal( {
3035                     //draggable: true,
3036                     //resizable:false,
3037                     //constraintoviewport:false,
3038                     //fixedcenter:true,
3039                     //collapsible : false,
3040                     //shim:true,
3041                     //modal: true,
3042                   //  width: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     this.addEvents({
7446         /**
7447          * @event clientvalidation
7448          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7449          * @param {Form} this
7450          * @param {Boolean} valid true if the form has passed client-side validation
7451          */
7452         clientvalidation: true,
7453         /**
7454          * @event beforeaction
7455          * Fires before any action is performed. Return false to cancel the action.
7456          * @param {Form} this
7457          * @param {Action} action The action to be performed
7458          */
7459         beforeaction: true,
7460         /**
7461          * @event actionfailed
7462          * Fires when an action fails.
7463          * @param {Form} this
7464          * @param {Action} action The action that failed
7465          */
7466         actionfailed : true,
7467         /**
7468          * @event actioncomplete
7469          * Fires when an action is completed.
7470          * @param {Form} this
7471          * @param {Action} action The action that completed
7472          */
7473         actioncomplete : true
7474     });
7475
7476 };
7477
7478 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7479
7480      /**
7481      * @cfg {String} method
7482      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7483      */
7484     method : 'POST',
7485     /**
7486      * @cfg {String} url
7487      * The URL to use for form actions if one isn't supplied in the action options.
7488      */
7489     /**
7490      * @cfg {Boolean} fileUpload
7491      * Set to true if this form is a file upload.
7492      */
7493
7494     /**
7495      * @cfg {Object} baseParams
7496      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7497      */
7498
7499     /**
7500      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7501      */
7502     timeout: 30,
7503     /**
7504      * @cfg {Sting} align (left|right) for navbar forms
7505      */
7506     align : 'left',
7507
7508     // private
7509     activeAction : null,
7510
7511     /**
7512      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7513      * element by passing it or its id or mask the form itself by passing in true.
7514      * @type Mixed
7515      */
7516     waitMsgTarget : false,
7517
7518     loadMask : true,
7519     
7520     /**
7521      * @cfg {Boolean} errPopover (true|false) default false
7522      */
7523     errPopover : false,
7524
7525     getAutoCreate : function(){
7526
7527         var cfg = {
7528             tag: 'form',
7529             method : this.method || 'POST',
7530             id : this.id || Roo.id(),
7531             cls : ''
7532         };
7533         if (this.parent().xtype.match(/^Nav/)) {
7534             cfg.cls = 'navbar-form navbar-' + this.align;
7535
7536         }
7537
7538         if (this.labelAlign == 'left' ) {
7539             cfg.cls += ' form-horizontal';
7540         }
7541
7542
7543         return cfg;
7544     },
7545     initEvents : function()
7546     {
7547         this.el.on('submit', this.onSubmit, this);
7548         // this was added as random key presses on the form where triggering form submit.
7549         this.el.on('keypress', function(e) {
7550             if (e.getCharCode() != 13) {
7551                 return true;
7552             }
7553             // we might need to allow it for textareas.. and some other items.
7554             // check e.getTarget().
7555
7556             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7557                 return true;
7558             }
7559
7560             Roo.log("keypress blocked");
7561
7562             e.preventDefault();
7563             return false;
7564         });
7565         
7566     },
7567     // private
7568     onSubmit : function(e){
7569         e.stopEvent();
7570     },
7571
7572      /**
7573      * Returns true if client-side validation on the form is successful.
7574      * @return Boolean
7575      */
7576     isValid : function(){
7577         var items = this.getItems();
7578         var valid = true;
7579         var target = false;
7580         items.each(function(f){
7581            if(!f.validate()){
7582                valid = false;
7583                
7584                if(!target){
7585                    target = f;
7586                }
7587            }
7588         });
7589         
7590         if(this.errPopover && !valid){
7591             this.showErrPopover(target);
7592         }
7593         
7594         return valid;
7595     },
7596     
7597     showErrPopover : function(target)
7598     {
7599         if(!this.errPopover){
7600             return;
7601         }
7602         
7603         target.inputEl().focus();
7604         
7605         var oIndex = target.el.getStyle('z-index');
7606         
7607         target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7608         
7609         target.el.addClass('roo-invalid-outline');
7610         
7611         var fadeout = function(){
7612             
7613             target.inputEl().un('blur', fadeout);
7614             target.inputEl().un('keyup', fadeout);
7615             
7616             target.el.setStyle('z-index', oIndex);
7617         
7618             target.el.removeClass('roo-invalid-outline');
7619             
7620         }
7621         
7622         target.inputEl().on('blur', fadeout);
7623         target.inputEl().on('keyup', fadeout);
7624         
7625         Roo.log(target.el);
7626         
7627         Roo.log(target);
7628           
7629     },
7630     
7631     /**
7632      * Returns true if any fields in this form have changed since their original load.
7633      * @return Boolean
7634      */
7635     isDirty : function(){
7636         var dirty = false;
7637         var items = this.getItems();
7638         items.each(function(f){
7639            if(f.isDirty()){
7640                dirty = true;
7641                return false;
7642            }
7643            return true;
7644         });
7645         return dirty;
7646     },
7647      /**
7648      * Performs a predefined action (submit or load) or custom actions you define on this form.
7649      * @param {String} actionName The name of the action type
7650      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7651      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7652      * accept other config options):
7653      * <pre>
7654 Property          Type             Description
7655 ----------------  ---------------  ----------------------------------------------------------------------------------
7656 url               String           The url for the action (defaults to the form's url)
7657 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7658 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7659 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7660                                    validate the form on the client (defaults to false)
7661      * </pre>
7662      * @return {BasicForm} this
7663      */
7664     doAction : function(action, options){
7665         if(typeof action == 'string'){
7666             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7667         }
7668         if(this.fireEvent('beforeaction', this, action) !== false){
7669             this.beforeAction(action);
7670             action.run.defer(100, action);
7671         }
7672         return this;
7673     },
7674
7675     // private
7676     beforeAction : function(action){
7677         var o = action.options;
7678
7679         if(this.loadMask){
7680             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7681         }
7682         // not really supported yet.. ??
7683
7684         //if(this.waitMsgTarget === true){
7685         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7686         //}else if(this.waitMsgTarget){
7687         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7688         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7689         //}else {
7690         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7691        // }
7692
7693     },
7694
7695     // private
7696     afterAction : function(action, success){
7697         this.activeAction = null;
7698         var o = action.options;
7699
7700         //if(this.waitMsgTarget === true){
7701             this.el.unmask();
7702         //}else if(this.waitMsgTarget){
7703         //    this.waitMsgTarget.unmask();
7704         //}else{
7705         //    Roo.MessageBox.updateProgress(1);
7706         //    Roo.MessageBox.hide();
7707        // }
7708         //
7709         if(success){
7710             if(o.reset){
7711                 this.reset();
7712             }
7713             Roo.callback(o.success, o.scope, [this, action]);
7714             this.fireEvent('actioncomplete', this, action);
7715
7716         }else{
7717
7718             // failure condition..
7719             // we have a scenario where updates need confirming.
7720             // eg. if a locking scenario exists..
7721             // we look for { errors : { needs_confirm : true }} in the response.
7722             if (
7723                 (typeof(action.result) != 'undefined')  &&
7724                 (typeof(action.result.errors) != 'undefined')  &&
7725                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7726            ){
7727                 var _t = this;
7728                 Roo.log("not supported yet");
7729                  /*
7730
7731                 Roo.MessageBox.confirm(
7732                     "Change requires confirmation",
7733                     action.result.errorMsg,
7734                     function(r) {
7735                         if (r != 'yes') {
7736                             return;
7737                         }
7738                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7739                     }
7740
7741                 );
7742                 */
7743
7744
7745                 return;
7746             }
7747
7748             Roo.callback(o.failure, o.scope, [this, action]);
7749             // show an error message if no failed handler is set..
7750             if (!this.hasListener('actionfailed')) {
7751                 Roo.log("need to add dialog support");
7752                 /*
7753                 Roo.MessageBox.alert("Error",
7754                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7755                         action.result.errorMsg :
7756                         "Saving Failed, please check your entries or try again"
7757                 );
7758                 */
7759             }
7760
7761             this.fireEvent('actionfailed', this, action);
7762         }
7763
7764     },
7765     /**
7766      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7767      * @param {String} id The value to search for
7768      * @return Field
7769      */
7770     findField : function(id){
7771         var items = this.getItems();
7772         var field = items.get(id);
7773         if(!field){
7774              items.each(function(f){
7775                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7776                     field = f;
7777                     return false;
7778                 }
7779                 return true;
7780             });
7781         }
7782         return field || null;
7783     },
7784      /**
7785      * Mark fields in this form invalid in bulk.
7786      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7787      * @return {BasicForm} this
7788      */
7789     markInvalid : function(errors){
7790         if(errors instanceof Array){
7791             for(var i = 0, len = errors.length; i < len; i++){
7792                 var fieldError = errors[i];
7793                 var f = this.findField(fieldError.id);
7794                 if(f){
7795                     f.markInvalid(fieldError.msg);
7796                 }
7797             }
7798         }else{
7799             var field, id;
7800             for(id in errors){
7801                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7802                     field.markInvalid(errors[id]);
7803                 }
7804             }
7805         }
7806         //Roo.each(this.childForms || [], function (f) {
7807         //    f.markInvalid(errors);
7808         //});
7809
7810         return this;
7811     },
7812
7813     /**
7814      * Set values for fields in this form in bulk.
7815      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7816      * @return {BasicForm} this
7817      */
7818     setValues : function(values){
7819         if(values instanceof Array){ // array of objects
7820             for(var i = 0, len = values.length; i < len; i++){
7821                 var v = values[i];
7822                 var f = this.findField(v.id);
7823                 if(f){
7824                     f.setValue(v.value);
7825                     if(this.trackResetOnLoad){
7826                         f.originalValue = f.getValue();
7827                     }
7828                 }
7829             }
7830         }else{ // object hash
7831             var field, id;
7832             for(id in values){
7833                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7834
7835                     if (field.setFromData &&
7836                         field.valueField &&
7837                         field.displayField &&
7838                         // combos' with local stores can
7839                         // be queried via setValue()
7840                         // to set their value..
7841                         (field.store && !field.store.isLocal)
7842                         ) {
7843                         // it's a combo
7844                         var sd = { };
7845                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7846                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7847                         field.setFromData(sd);
7848
7849                     } else {
7850                         field.setValue(values[id]);
7851                     }
7852
7853
7854                     if(this.trackResetOnLoad){
7855                         field.originalValue = field.getValue();
7856                     }
7857                 }
7858             }
7859         }
7860
7861         //Roo.each(this.childForms || [], function (f) {
7862         //    f.setValues(values);
7863         //});
7864
7865         return this;
7866     },
7867
7868     /**
7869      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7870      * they are returned as an array.
7871      * @param {Boolean} asString
7872      * @return {Object}
7873      */
7874     getValues : function(asString){
7875         //if (this.childForms) {
7876             // copy values from the child forms
7877         //    Roo.each(this.childForms, function (f) {
7878         //        this.setValues(f.getValues());
7879         //    }, this);
7880         //}
7881
7882
7883
7884         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7885         if(asString === true){
7886             return fs;
7887         }
7888         return Roo.urlDecode(fs);
7889     },
7890
7891     /**
7892      * Returns the fields in this form as an object with key/value pairs.
7893      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7894      * @return {Object}
7895      */
7896     getFieldValues : function(with_hidden)
7897     {
7898         var items = this.getItems();
7899         var ret = {};
7900         items.each(function(f){
7901             if (!f.getName()) {
7902                 return;
7903             }
7904             var v = f.getValue();
7905             if (f.inputType =='radio') {
7906                 if (typeof(ret[f.getName()]) == 'undefined') {
7907                     ret[f.getName()] = ''; // empty..
7908                 }
7909
7910                 if (!f.el.dom.checked) {
7911                     return;
7912
7913                 }
7914                 v = f.el.dom.value;
7915
7916             }
7917
7918             // not sure if this supported any more..
7919             if ((typeof(v) == 'object') && f.getRawValue) {
7920                 v = f.getRawValue() ; // dates..
7921             }
7922             // combo boxes where name != hiddenName...
7923             if (f.name != f.getName()) {
7924                 ret[f.name] = f.getRawValue();
7925             }
7926             ret[f.getName()] = v;
7927         });
7928
7929         return ret;
7930     },
7931
7932     /**
7933      * Clears all invalid messages in this form.
7934      * @return {BasicForm} this
7935      */
7936     clearInvalid : function(){
7937         var items = this.getItems();
7938
7939         items.each(function(f){
7940            f.clearInvalid();
7941         });
7942
7943
7944
7945         return this;
7946     },
7947
7948     /**
7949      * Resets this form.
7950      * @return {BasicForm} this
7951      */
7952     reset : function(){
7953         var items = this.getItems();
7954         items.each(function(f){
7955             f.reset();
7956         });
7957
7958         Roo.each(this.childForms || [], function (f) {
7959             f.reset();
7960         });
7961
7962
7963         return this;
7964     },
7965     getItems : function()
7966     {
7967         var r=new Roo.util.MixedCollection(false, function(o){
7968             return o.id || (o.id = Roo.id());
7969         });
7970         var iter = function(el) {
7971             if (el.inputEl) {
7972                 r.add(el);
7973             }
7974             if (!el.items) {
7975                 return;
7976             }
7977             Roo.each(el.items,function(e) {
7978                 iter(e);
7979             });
7980
7981
7982         };
7983
7984         iter(this);
7985         return r;
7986
7987
7988
7989
7990     }
7991
7992 });
7993 /*
7994  * Based on:
7995  * Ext JS Library 1.1.1
7996  * Copyright(c) 2006-2007, Ext JS, LLC.
7997  *
7998  * Originally Released Under LGPL - original licence link has changed is not relivant.
7999  *
8000  * Fork - LGPL
8001  * <script type="text/javascript">
8002  */
8003 /**
8004  * @class Roo.form.VTypes
8005  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8006  * @singleton
8007  */
8008 Roo.form.VTypes = function(){
8009     // closure these in so they are only created once.
8010     var alpha = /^[a-zA-Z_]+$/;
8011     var alphanum = /^[a-zA-Z0-9_]+$/;
8012     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8013     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8014
8015     // All these messages and functions are configurable
8016     return {
8017         /**
8018          * The function used to validate email addresses
8019          * @param {String} value The email address
8020          */
8021         'email' : function(v){
8022             return email.test(v);
8023         },
8024         /**
8025          * The error text to display when the email validation function returns false
8026          * @type String
8027          */
8028         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8029         /**
8030          * The keystroke filter mask to be applied on email input
8031          * @type RegExp
8032          */
8033         'emailMask' : /[a-z0-9_\.\-@]/i,
8034
8035         /**
8036          * The function used to validate URLs
8037          * @param {String} value The URL
8038          */
8039         'url' : function(v){
8040             return url.test(v);
8041         },
8042         /**
8043          * The error text to display when the url validation function returns false
8044          * @type String
8045          */
8046         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8047         
8048         /**
8049          * The function used to validate alpha values
8050          * @param {String} value The value
8051          */
8052         'alpha' : function(v){
8053             return alpha.test(v);
8054         },
8055         /**
8056          * The error text to display when the alpha validation function returns false
8057          * @type String
8058          */
8059         'alphaText' : 'This field should only contain letters and _',
8060         /**
8061          * The keystroke filter mask to be applied on alpha input
8062          * @type RegExp
8063          */
8064         'alphaMask' : /[a-z_]/i,
8065
8066         /**
8067          * The function used to validate alphanumeric values
8068          * @param {String} value The value
8069          */
8070         'alphanum' : function(v){
8071             return alphanum.test(v);
8072         },
8073         /**
8074          * The error text to display when the alphanumeric validation function returns false
8075          * @type String
8076          */
8077         'alphanumText' : 'This field should only contain letters, numbers and _',
8078         /**
8079          * The keystroke filter mask to be applied on alphanumeric input
8080          * @type RegExp
8081          */
8082         'alphanumMask' : /[a-z0-9_]/i
8083     };
8084 }();/*
8085  * - LGPL
8086  *
8087  * Input
8088  * 
8089  */
8090
8091 /**
8092  * @class Roo.bootstrap.Input
8093  * @extends Roo.bootstrap.Component
8094  * Bootstrap Input class
8095  * @cfg {Boolean} disabled is it disabled
8096  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8097  * @cfg {String} name name of the input
8098  * @cfg {string} fieldLabel - the label associated
8099  * @cfg {string} placeholder - placeholder to put in text.
8100  * @cfg {string}  before - input group add on before
8101  * @cfg {string} after - input group add on after
8102  * @cfg {string} size - (lg|sm) or leave empty..
8103  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8104  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8105  * @cfg {Number} md colspan out of 12 for computer-sized screens
8106  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8107  * @cfg {string} value default value of the input
8108  * @cfg {Number} labelWidth set the width of label (0-12)
8109  * @cfg {String} labelAlign (top|left)
8110  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8111  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8112  * @cfg {String} indicatorpos (left|right) default left
8113
8114  * @cfg {String} align (left|center|right) Default left
8115  * @cfg {Boolean} forceFeedback (true|false) Default false
8116  * 
8117  * 
8118  * 
8119  * 
8120  * @constructor
8121  * Create a new Input
8122  * @param {Object} config The config object
8123  */
8124
8125 Roo.bootstrap.Input = function(config){
8126     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8127    
8128         this.addEvents({
8129             /**
8130              * @event focus
8131              * Fires when this field receives input focus.
8132              * @param {Roo.form.Field} this
8133              */
8134             focus : true,
8135             /**
8136              * @event blur
8137              * Fires when this field loses input focus.
8138              * @param {Roo.form.Field} this
8139              */
8140             blur : true,
8141             /**
8142              * @event specialkey
8143              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8144              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8145              * @param {Roo.form.Field} this
8146              * @param {Roo.EventObject} e The event object
8147              */
8148             specialkey : true,
8149             /**
8150              * @event change
8151              * Fires just before the field blurs if the field value has changed.
8152              * @param {Roo.form.Field} this
8153              * @param {Mixed} newValue The new value
8154              * @param {Mixed} oldValue The original value
8155              */
8156             change : true,
8157             /**
8158              * @event invalid
8159              * Fires after the field has been marked as invalid.
8160              * @param {Roo.form.Field} this
8161              * @param {String} msg The validation message
8162              */
8163             invalid : true,
8164             /**
8165              * @event valid
8166              * Fires after the field has been validated with no errors.
8167              * @param {Roo.form.Field} this
8168              */
8169             valid : true,
8170              /**
8171              * @event keyup
8172              * Fires after the key up
8173              * @param {Roo.form.Field} this
8174              * @param {Roo.EventObject}  e The event Object
8175              */
8176             keyup : true
8177         });
8178 };
8179
8180 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8181      /**
8182      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8183       automatic validation (defaults to "keyup").
8184      */
8185     validationEvent : "keyup",
8186      /**
8187      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8188      */
8189     validateOnBlur : true,
8190     /**
8191      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8192      */
8193     validationDelay : 250,
8194      /**
8195      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8196      */
8197     focusClass : "x-form-focus",  // not needed???
8198     
8199        
8200     /**
8201      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8202      */
8203     invalidClass : "has-warning",
8204     
8205     /**
8206      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8207      */
8208     validClass : "has-success",
8209     
8210     /**
8211      * @cfg {Boolean} hasFeedback (true|false) default true
8212      */
8213     hasFeedback : true,
8214     
8215     /**
8216      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8217      */
8218     invalidFeedbackClass : "glyphicon-warning-sign",
8219     
8220     /**
8221      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8222      */
8223     validFeedbackClass : "glyphicon-ok",
8224     
8225     /**
8226      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8227      */
8228     selectOnFocus : false,
8229     
8230      /**
8231      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8232      */
8233     maskRe : null,
8234        /**
8235      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8236      */
8237     vtype : null,
8238     
8239       /**
8240      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8241      */
8242     disableKeyFilter : false,
8243     
8244        /**
8245      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8246      */
8247     disabled : false,
8248      /**
8249      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8250      */
8251     allowBlank : true,
8252     /**
8253      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8254      */
8255     blankText : "This field is required",
8256     
8257      /**
8258      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8259      */
8260     minLength : 0,
8261     /**
8262      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8263      */
8264     maxLength : Number.MAX_VALUE,
8265     /**
8266      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8267      */
8268     minLengthText : "The minimum length for this field is {0}",
8269     /**
8270      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8271      */
8272     maxLengthText : "The maximum length for this field is {0}",
8273   
8274     
8275     /**
8276      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8277      * If available, this function will be called only after the basic validators all return true, and will be passed the
8278      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8279      */
8280     validator : null,
8281     /**
8282      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8283      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8284      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8285      */
8286     regex : null,
8287     /**
8288      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8289      */
8290     regexText : "",
8291     
8292     autocomplete: false,
8293     
8294     
8295     fieldLabel : '',
8296     inputType : 'text',
8297     
8298     name : false,
8299     placeholder: false,
8300     before : false,
8301     after : false,
8302     size : false,
8303     hasFocus : false,
8304     preventMark: false,
8305     isFormField : true,
8306     value : '',
8307     labelWidth : 2,
8308     labelAlign : false,
8309     readOnly : false,
8310     align : false,
8311     formatedValue : false,
8312     forceFeedback : false,
8313     
8314     indicatorpos : 'left',
8315     
8316     parentLabelAlign : function()
8317     {
8318         var parent = this;
8319         while (parent.parent()) {
8320             parent = parent.parent();
8321             if (typeof(parent.labelAlign) !='undefined') {
8322                 return parent.labelAlign;
8323             }
8324         }
8325         return 'left';
8326         
8327     },
8328     
8329     getAutoCreate : function()
8330     {
8331         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8332         
8333         var id = Roo.id();
8334         
8335         var cfg = {};
8336         
8337         if(this.inputType != 'hidden'){
8338             cfg.cls = 'form-group' //input-group
8339         }
8340         
8341         var input =  {
8342             tag: 'input',
8343             id : id,
8344             type : this.inputType,
8345             value : this.value,
8346             cls : 'form-control',
8347             placeholder : this.placeholder || '',
8348             autocomplete : this.autocomplete || 'new-password'
8349         };
8350         
8351         if(this.align){
8352             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8353         }
8354         
8355         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8356             input.maxLength = this.maxLength;
8357         }
8358         
8359         if (this.disabled) {
8360             input.disabled=true;
8361         }
8362         
8363         if (this.readOnly) {
8364             input.readonly=true;
8365         }
8366         
8367         if (this.name) {
8368             input.name = this.name;
8369         }
8370         
8371         if (this.size) {
8372             input.cls += ' input-' + this.size;
8373         }
8374         
8375         var settings=this;
8376         ['xs','sm','md','lg'].map(function(size){
8377             if (settings[size]) {
8378                 cfg.cls += ' col-' + size + '-' + settings[size];
8379             }
8380         });
8381         
8382         var inputblock = input;
8383         
8384         var feedback = {
8385             tag: 'span',
8386             cls: 'glyphicon form-control-feedback'
8387         };
8388             
8389         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8390             
8391             inputblock = {
8392                 cls : 'has-feedback',
8393                 cn :  [
8394                     input,
8395                     feedback
8396                 ] 
8397             };  
8398         }
8399         
8400         if (this.before || this.after) {
8401             
8402             inputblock = {
8403                 cls : 'input-group',
8404                 cn :  [] 
8405             };
8406             
8407             if (this.before && typeof(this.before) == 'string') {
8408                 
8409                 inputblock.cn.push({
8410                     tag :'span',
8411                     cls : 'roo-input-before input-group-addon',
8412                     html : this.before
8413                 });
8414             }
8415             if (this.before && typeof(this.before) == 'object') {
8416                 this.before = Roo.factory(this.before);
8417                 
8418                 inputblock.cn.push({
8419                     tag :'span',
8420                     cls : 'roo-input-before input-group-' +
8421                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8422                 });
8423             }
8424             
8425             inputblock.cn.push(input);
8426             
8427             if (this.after && typeof(this.after) == 'string') {
8428                 inputblock.cn.push({
8429                     tag :'span',
8430                     cls : 'roo-input-after input-group-addon',
8431                     html : this.after
8432                 });
8433             }
8434             if (this.after && typeof(this.after) == 'object') {
8435                 this.after = Roo.factory(this.after);
8436                 
8437                 inputblock.cn.push({
8438                     tag :'span',
8439                     cls : 'roo-input-after input-group-' +
8440                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8441                 });
8442             }
8443             
8444             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8445                 inputblock.cls += ' has-feedback';
8446                 inputblock.cn.push(feedback);
8447             }
8448         };
8449         
8450         if (align ==='left' && this.fieldLabel.length) {
8451             
8452             cfg.cn = [
8453                 {
8454                     tag : 'i',
8455                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8456                     tooltip : 'This field is required'
8457                 },
8458                 {
8459                     tag: 'label',
8460                     'for' :  id,
8461                     cls : 'control-label col-sm-' + this.labelWidth,
8462                     html : this.fieldLabel
8463
8464                 },
8465                 {
8466                     cls : "col-sm-" + (12 - this.labelWidth), 
8467                     cn: [
8468                         inputblock
8469                     ]
8470                 }
8471
8472             ];
8473             
8474             if(this.indicatorpos == 'right'){
8475                 cfg.cn = [
8476                     {
8477                         tag: 'label',
8478                         'for' :  id,
8479                         cls : 'control-label col-sm-' + this.labelWidth,
8480                         html : this.fieldLabel
8481
8482                     },
8483                     {
8484                         tag : 'i',
8485                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8486                         tooltip : 'This field is required'
8487                     },
8488                     {
8489                         cls : "col-sm-" + (12 - this.labelWidth), 
8490                         cn: [
8491                             inputblock
8492                         ]
8493                     }
8494
8495                 ];
8496             }
8497             
8498         } else if ( this.fieldLabel.length) {
8499                 
8500             cfg.cn = [
8501                 {
8502                     tag : 'i',
8503                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8504                     tooltip : 'This field is required'
8505                 },
8506                 {
8507                     tag: 'label',
8508                    //cls : 'input-group-addon',
8509                     html : this.fieldLabel
8510
8511                 },
8512
8513                inputblock
8514
8515            ];
8516            
8517            if(this.indicatorpos == 'right'){
8518                 
8519                 cfg.cn = [
8520                     {
8521                         tag: 'label',
8522                        //cls : 'input-group-addon',
8523                         html : this.fieldLabel
8524
8525                     },
8526                     {
8527                         tag : 'i',
8528                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8529                         tooltip : 'This field is required'
8530                     },
8531
8532                    inputblock
8533
8534                ];
8535
8536             }
8537
8538         } else {
8539             
8540             cfg.cn = [
8541
8542                     inputblock
8543
8544             ];
8545                 
8546                 
8547         };
8548         
8549         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8550            cfg.cls += ' navbar-form';
8551         }
8552         
8553         if (this.parentType === 'NavGroup') {
8554            cfg.cls += ' navbar-form';
8555            cfg.tag = 'li';
8556         }
8557         
8558         return cfg;
8559         
8560     },
8561     /**
8562      * return the real input element.
8563      */
8564     inputEl: function ()
8565     {
8566         return this.el.select('input.form-control',true).first();
8567     },
8568     
8569     tooltipEl : function()
8570     {
8571         return this.inputEl();
8572     },
8573     
8574     indicatorEl : function()
8575     {
8576         var indicator = this.el.select('i.roo-required-indicator',true).first();
8577         
8578         if(!indicator){
8579             return false;
8580         }
8581         
8582         return indicator;
8583         
8584     },
8585     
8586     setDisabled : function(v)
8587     {
8588         var i  = this.inputEl().dom;
8589         if (!v) {
8590             i.removeAttribute('disabled');
8591             return;
8592             
8593         }
8594         i.setAttribute('disabled','true');
8595     },
8596     initEvents : function()
8597     {
8598           
8599         this.inputEl().on("keydown" , this.fireKey,  this);
8600         this.inputEl().on("focus", this.onFocus,  this);
8601         this.inputEl().on("blur", this.onBlur,  this);
8602         
8603         this.inputEl().relayEvent('keyup', this);
8604         
8605         this.indicator = this.indicatorEl();
8606         
8607         if(this.indicator){
8608             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8609             this.indicator.hide();
8610         }
8611  
8612         // reference to original value for reset
8613         this.originalValue = this.getValue();
8614         //Roo.form.TextField.superclass.initEvents.call(this);
8615         if(this.validationEvent == 'keyup'){
8616             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8617             this.inputEl().on('keyup', this.filterValidation, this);
8618         }
8619         else if(this.validationEvent !== false){
8620             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8621         }
8622         
8623         if(this.selectOnFocus){
8624             this.on("focus", this.preFocus, this);
8625             
8626         }
8627         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8628             this.inputEl().on("keypress", this.filterKeys, this);
8629         } else {
8630             this.inputEl().relayEvent('keypress', this);
8631         }
8632        /* if(this.grow){
8633             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8634             this.el.on("click", this.autoSize,  this);
8635         }
8636         */
8637         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8638             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8639         }
8640         
8641         if (typeof(this.before) == 'object') {
8642             this.before.render(this.el.select('.roo-input-before',true).first());
8643         }
8644         if (typeof(this.after) == 'object') {
8645             this.after.render(this.el.select('.roo-input-after',true).first());
8646         }
8647         
8648         
8649     },
8650     filterValidation : function(e){
8651         if(!e.isNavKeyPress()){
8652             this.validationTask.delay(this.validationDelay);
8653         }
8654     },
8655      /**
8656      * Validates the field value
8657      * @return {Boolean} True if the value is valid, else false
8658      */
8659     validate : function(){
8660         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8661         if(this.disabled || this.validateValue(this.getRawValue())){
8662             this.markValid();
8663             return true;
8664         }
8665         
8666         this.markInvalid();
8667         return false;
8668     },
8669     
8670     
8671     /**
8672      * Validates a value according to the field's validation rules and marks the field as invalid
8673      * if the validation fails
8674      * @param {Mixed} value The value to validate
8675      * @return {Boolean} True if the value is valid, else false
8676      */
8677     validateValue : function(value){
8678         if(value.length < 1)  { // if it's blank
8679             if(this.allowBlank){
8680                 return true;
8681             }
8682             return false;
8683         }
8684         
8685         if(value.length < this.minLength){
8686             return false;
8687         }
8688         if(value.length > this.maxLength){
8689             return false;
8690         }
8691         if(this.vtype){
8692             var vt = Roo.form.VTypes;
8693             if(!vt[this.vtype](value, this)){
8694                 return false;
8695             }
8696         }
8697         if(typeof this.validator == "function"){
8698             var msg = this.validator(value);
8699             if(msg !== true){
8700                 return false;
8701             }
8702         }
8703         
8704         if(this.regex && !this.regex.test(value)){
8705             return false;
8706         }
8707         
8708         return true;
8709     },
8710
8711     
8712     
8713      // private
8714     fireKey : function(e){
8715         //Roo.log('field ' + e.getKey());
8716         if(e.isNavKeyPress()){
8717             this.fireEvent("specialkey", this, e);
8718         }
8719     },
8720     focus : function (selectText){
8721         if(this.rendered){
8722             this.inputEl().focus();
8723             if(selectText === true){
8724                 this.inputEl().dom.select();
8725             }
8726         }
8727         return this;
8728     } ,
8729     
8730     onFocus : function(){
8731         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8732            // this.el.addClass(this.focusClass);
8733         }
8734         if(!this.hasFocus){
8735             this.hasFocus = true;
8736             this.startValue = this.getValue();
8737             this.fireEvent("focus", this);
8738         }
8739     },
8740     
8741     beforeBlur : Roo.emptyFn,
8742
8743     
8744     // private
8745     onBlur : function(){
8746         this.beforeBlur();
8747         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8748             //this.el.removeClass(this.focusClass);
8749         }
8750         this.hasFocus = false;
8751         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8752             this.validate();
8753         }
8754         var v = this.getValue();
8755         if(String(v) !== String(this.startValue)){
8756             this.fireEvent('change', this, v, this.startValue);
8757         }
8758         this.fireEvent("blur", this);
8759     },
8760     
8761     /**
8762      * Resets the current field value to the originally loaded value and clears any validation messages
8763      */
8764     reset : function(){
8765         this.setValue(this.originalValue);
8766         this.validate();
8767     },
8768      /**
8769      * Returns the name of the field
8770      * @return {Mixed} name The name field
8771      */
8772     getName: function(){
8773         return this.name;
8774     },
8775      /**
8776      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8777      * @return {Mixed} value The field value
8778      */
8779     getValue : function(){
8780         
8781         var v = this.inputEl().getValue();
8782         
8783         return v;
8784     },
8785     /**
8786      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8787      * @return {Mixed} value The field value
8788      */
8789     getRawValue : function(){
8790         var v = this.inputEl().getValue();
8791         
8792         return v;
8793     },
8794     
8795     /**
8796      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8797      * @param {Mixed} value The value to set
8798      */
8799     setRawValue : function(v){
8800         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8801     },
8802     
8803     selectText : function(start, end){
8804         var v = this.getRawValue();
8805         if(v.length > 0){
8806             start = start === undefined ? 0 : start;
8807             end = end === undefined ? v.length : end;
8808             var d = this.inputEl().dom;
8809             if(d.setSelectionRange){
8810                 d.setSelectionRange(start, end);
8811             }else if(d.createTextRange){
8812                 var range = d.createTextRange();
8813                 range.moveStart("character", start);
8814                 range.moveEnd("character", v.length-end);
8815                 range.select();
8816             }
8817         }
8818     },
8819     
8820     /**
8821      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8822      * @param {Mixed} value The value to set
8823      */
8824     setValue : function(v){
8825         this.value = v;
8826         if(this.rendered){
8827             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8828             this.validate();
8829         }
8830     },
8831     
8832     /*
8833     processValue : function(value){
8834         if(this.stripCharsRe){
8835             var newValue = value.replace(this.stripCharsRe, '');
8836             if(newValue !== value){
8837                 this.setRawValue(newValue);
8838                 return newValue;
8839             }
8840         }
8841         return value;
8842     },
8843   */
8844     preFocus : function(){
8845         
8846         if(this.selectOnFocus){
8847             this.inputEl().dom.select();
8848         }
8849     },
8850     filterKeys : function(e){
8851         var k = e.getKey();
8852         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8853             return;
8854         }
8855         var c = e.getCharCode(), cc = String.fromCharCode(c);
8856         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8857             return;
8858         }
8859         if(!this.maskRe.test(cc)){
8860             e.stopEvent();
8861         }
8862     },
8863      /**
8864      * Clear any invalid styles/messages for this field
8865      */
8866     clearInvalid : function(){
8867         
8868         if(!this.el || this.preventMark){ // not rendered
8869             return;
8870         }
8871         
8872         if(this.indicator){
8873             this.indicator.hide();
8874         }
8875         
8876         this.el.removeClass(this.invalidClass);
8877         
8878         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8879             
8880             var feedback = this.el.select('.form-control-feedback', true).first();
8881             
8882             if(feedback){
8883                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8884             }
8885             
8886         }
8887         
8888         this.fireEvent('valid', this);
8889     },
8890     
8891      /**
8892      * Mark this field as valid
8893      */
8894     markValid : function()
8895     {
8896         if(!this.el  || this.preventMark){ // not rendered
8897             return;
8898         }
8899         
8900         this.el.removeClass([this.invalidClass, this.validClass]);
8901         
8902         var feedback = this.el.select('.form-control-feedback', true).first();
8903             
8904         if(feedback){
8905             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8906         }
8907
8908         if(this.disabled){
8909             return;
8910         }
8911         
8912         if(this.allowBlank && !this.getRawValue().length){
8913             return;
8914         }
8915         
8916         if(this.indicator){
8917             this.indicator.hide();
8918         }
8919         
8920         this.el.addClass(this.validClass);
8921         
8922         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8923             
8924             var feedback = this.el.select('.form-control-feedback', true).first();
8925             
8926             if(feedback){
8927                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8928                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8929             }
8930             
8931         }
8932         
8933         this.fireEvent('valid', this);
8934     },
8935     
8936      /**
8937      * Mark this field as invalid
8938      * @param {String} msg The validation message
8939      */
8940     markInvalid : function(msg)
8941     {
8942         if(!this.el  || this.preventMark){ // not rendered
8943             return;
8944         }
8945         
8946         this.el.removeClass([this.invalidClass, this.validClass]);
8947         
8948         var feedback = this.el.select('.form-control-feedback', true).first();
8949             
8950         if(feedback){
8951             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8952         }
8953
8954         if(this.disabled){
8955             return;
8956         }
8957         
8958         if(this.allowBlank && !this.getRawValue().length){
8959             return;
8960         }
8961         
8962         if(this.indicator){
8963             this.indicator.show();
8964         }
8965         
8966         this.el.addClass(this.invalidClass);
8967         
8968         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8969             
8970             var feedback = this.el.select('.form-control-feedback', true).first();
8971             
8972             if(feedback){
8973                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8974                 
8975                 if(this.getValue().length || this.forceFeedback){
8976                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8977                 }
8978                 
8979             }
8980             
8981         }
8982         
8983         this.fireEvent('invalid', this, msg);
8984     },
8985     // private
8986     SafariOnKeyDown : function(event)
8987     {
8988         // this is a workaround for a password hang bug on chrome/ webkit.
8989         if (this.inputEl().dom.type != 'password') {
8990             return;
8991         }
8992         
8993         var isSelectAll = false;
8994         
8995         if(this.inputEl().dom.selectionEnd > 0){
8996             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8997         }
8998         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8999             event.preventDefault();
9000             this.setValue('');
9001             return;
9002         }
9003         
9004         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9005             
9006             event.preventDefault();
9007             // this is very hacky as keydown always get's upper case.
9008             //
9009             var cc = String.fromCharCode(event.getCharCode());
9010             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9011             
9012         }
9013     },
9014     adjustWidth : function(tag, w){
9015         tag = tag.toLowerCase();
9016         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9017             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9018                 if(tag == 'input'){
9019                     return w + 2;
9020                 }
9021                 if(tag == 'textarea'){
9022                     return w-2;
9023                 }
9024             }else if(Roo.isOpera){
9025                 if(tag == 'input'){
9026                     return w + 2;
9027                 }
9028                 if(tag == 'textarea'){
9029                     return w-2;
9030                 }
9031             }
9032         }
9033         return w;
9034     }
9035     
9036 });
9037
9038  
9039 /*
9040  * - LGPL
9041  *
9042  * Input
9043  * 
9044  */
9045
9046 /**
9047  * @class Roo.bootstrap.TextArea
9048  * @extends Roo.bootstrap.Input
9049  * Bootstrap TextArea class
9050  * @cfg {Number} cols Specifies the visible width of a text area
9051  * @cfg {Number} rows Specifies the visible number of lines in a text area
9052  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9053  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9054  * @cfg {string} html text
9055  * 
9056  * @constructor
9057  * Create a new TextArea
9058  * @param {Object} config The config object
9059  */
9060
9061 Roo.bootstrap.TextArea = function(config){
9062     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9063    
9064 };
9065
9066 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9067      
9068     cols : false,
9069     rows : 5,
9070     readOnly : false,
9071     warp : 'soft',
9072     resize : false,
9073     value: false,
9074     html: false,
9075     
9076     getAutoCreate : function(){
9077         
9078         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9079         
9080         var id = Roo.id();
9081         
9082         var cfg = {};
9083         
9084         var input =  {
9085             tag: 'textarea',
9086             id : id,
9087             warp : this.warp,
9088             rows : this.rows,
9089             value : this.value || '',
9090             html: this.html || '',
9091             cls : 'form-control',
9092             placeholder : this.placeholder || '' 
9093             
9094         };
9095         
9096         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9097             input.maxLength = this.maxLength;
9098         }
9099         
9100         if(this.resize){
9101             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9102         }
9103         
9104         if(this.cols){
9105             input.cols = this.cols;
9106         }
9107         
9108         if (this.readOnly) {
9109             input.readonly = true;
9110         }
9111         
9112         if (this.name) {
9113             input.name = this.name;
9114         }
9115         
9116         if (this.size) {
9117             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9118         }
9119         
9120         var settings=this;
9121         ['xs','sm','md','lg'].map(function(size){
9122             if (settings[size]) {
9123                 cfg.cls += ' col-' + size + '-' + settings[size];
9124             }
9125         });
9126         
9127         var inputblock = input;
9128         
9129         if(this.hasFeedback && !this.allowBlank){
9130             
9131             var feedback = {
9132                 tag: 'span',
9133                 cls: 'glyphicon form-control-feedback'
9134             };
9135
9136             inputblock = {
9137                 cls : 'has-feedback',
9138                 cn :  [
9139                     input,
9140                     feedback
9141                 ] 
9142             };  
9143         }
9144         
9145         
9146         if (this.before || this.after) {
9147             
9148             inputblock = {
9149                 cls : 'input-group',
9150                 cn :  [] 
9151             };
9152             if (this.before) {
9153                 inputblock.cn.push({
9154                     tag :'span',
9155                     cls : 'input-group-addon',
9156                     html : this.before
9157                 });
9158             }
9159             
9160             inputblock.cn.push(input);
9161             
9162             if(this.hasFeedback && !this.allowBlank){
9163                 inputblock.cls += ' has-feedback';
9164                 inputblock.cn.push(feedback);
9165             }
9166             
9167             if (this.after) {
9168                 inputblock.cn.push({
9169                     tag :'span',
9170                     cls : 'input-group-addon',
9171                     html : this.after
9172                 });
9173             }
9174             
9175         }
9176         
9177         if (align ==='left' && this.fieldLabel.length) {
9178 //                Roo.log("left and has label");
9179                 cfg.cn = [
9180                     
9181                     {
9182                         tag: 'label',
9183                         'for' :  id,
9184                         cls : 'control-label col-sm-' + this.labelWidth,
9185                         html : this.fieldLabel
9186                         
9187                     },
9188                     {
9189                         cls : "col-sm-" + (12 - this.labelWidth), 
9190                         cn: [
9191                             inputblock
9192                         ]
9193                     }
9194                     
9195                 ];
9196         } else if ( this.fieldLabel.length) {
9197 //                Roo.log(" label");
9198                  cfg.cn = [
9199                    
9200                     {
9201                         tag: 'label',
9202                         //cls : 'input-group-addon',
9203                         html : this.fieldLabel
9204                         
9205                     },
9206                     
9207                     inputblock
9208                     
9209                 ];
9210
9211         } else {
9212             
9213 //                   Roo.log(" no label && no align");
9214                 cfg.cn = [
9215                     
9216                         inputblock
9217                     
9218                 ];
9219                 
9220                 
9221         }
9222         
9223         if (this.disabled) {
9224             input.disabled=true;
9225         }
9226         
9227         return cfg;
9228         
9229     },
9230     /**
9231      * return the real textarea element.
9232      */
9233     inputEl: function ()
9234     {
9235         return this.el.select('textarea.form-control',true).first();
9236     },
9237     
9238     /**
9239      * Clear any invalid styles/messages for this field
9240      */
9241     clearInvalid : function()
9242     {
9243         
9244         if(!this.el || this.preventMark){ // not rendered
9245             return;
9246         }
9247         
9248         var label = this.el.select('label', true).first();
9249         var icon = this.el.select('i.fa-star', true).first();
9250         
9251         if(label && icon){
9252             icon.remove();
9253         }
9254         
9255         this.el.removeClass(this.invalidClass);
9256         
9257         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9258             
9259             var feedback = this.el.select('.form-control-feedback', true).first();
9260             
9261             if(feedback){
9262                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9263             }
9264             
9265         }
9266         
9267         this.fireEvent('valid', this);
9268     },
9269     
9270      /**
9271      * Mark this field as valid
9272      */
9273     markValid : function()
9274     {
9275         if(!this.el  || this.preventMark){ // not rendered
9276             return;
9277         }
9278         
9279         this.el.removeClass([this.invalidClass, this.validClass]);
9280         
9281         var feedback = this.el.select('.form-control-feedback', true).first();
9282             
9283         if(feedback){
9284             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9285         }
9286
9287         if(this.disabled || this.allowBlank){
9288             return;
9289         }
9290         
9291         var label = this.el.select('label', true).first();
9292         var icon = this.el.select('i.fa-star', true).first();
9293         
9294         if(label && icon){
9295             icon.remove();
9296         }
9297         
9298         this.el.addClass(this.validClass);
9299         
9300         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9301             
9302             var feedback = this.el.select('.form-control-feedback', true).first();
9303             
9304             if(feedback){
9305                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9306                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9307             }
9308             
9309         }
9310         
9311         this.fireEvent('valid', this);
9312     },
9313     
9314      /**
9315      * Mark this field as invalid
9316      * @param {String} msg The validation message
9317      */
9318     markInvalid : function(msg)
9319     {
9320         if(!this.el  || this.preventMark){ // not rendered
9321             return;
9322         }
9323         
9324         this.el.removeClass([this.invalidClass, this.validClass]);
9325         
9326         var feedback = this.el.select('.form-control-feedback', true).first();
9327             
9328         if(feedback){
9329             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9330         }
9331
9332         if(this.disabled || this.allowBlank){
9333             return;
9334         }
9335         
9336         var label = this.el.select('label', true).first();
9337         var icon = this.el.select('i.fa-star', true).first();
9338         
9339         if(!this.getValue().length && label && !icon){
9340             this.el.createChild({
9341                 tag : 'i',
9342                 cls : 'text-danger fa fa-lg fa-star',
9343                 tooltip : 'This field is required',
9344                 style : 'margin-right:5px;'
9345             }, label, true);
9346         }
9347
9348         this.el.addClass(this.invalidClass);
9349         
9350         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9351             
9352             var feedback = this.el.select('.form-control-feedback', true).first();
9353             
9354             if(feedback){
9355                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9356                 
9357                 if(this.getValue().length || this.forceFeedback){
9358                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9359                 }
9360                 
9361             }
9362             
9363         }
9364         
9365         this.fireEvent('invalid', this, msg);
9366     }
9367 });
9368
9369  
9370 /*
9371  * - LGPL
9372  *
9373  * trigger field - base class for combo..
9374  * 
9375  */
9376  
9377 /**
9378  * @class Roo.bootstrap.TriggerField
9379  * @extends Roo.bootstrap.Input
9380  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9381  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9382  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9383  * for which you can provide a custom implementation.  For example:
9384  * <pre><code>
9385 var trigger = new Roo.bootstrap.TriggerField();
9386 trigger.onTriggerClick = myTriggerFn;
9387 trigger.applyTo('my-field');
9388 </code></pre>
9389  *
9390  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9391  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9392  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9393  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9394  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9395
9396  * @constructor
9397  * Create a new TriggerField.
9398  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9399  * to the base TextField)
9400  */
9401 Roo.bootstrap.TriggerField = function(config){
9402     this.mimicing = false;
9403     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9404 };
9405
9406 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9407     /**
9408      * @cfg {String} triggerClass A CSS class to apply to the trigger
9409      */
9410      /**
9411      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9412      */
9413     hideTrigger:false,
9414
9415     /**
9416      * @cfg {Boolean} removable (true|false) special filter default false
9417      */
9418     removable : false,
9419     
9420     /** @cfg {Boolean} grow @hide */
9421     /** @cfg {Number} growMin @hide */
9422     /** @cfg {Number} growMax @hide */
9423
9424     /**
9425      * @hide 
9426      * @method
9427      */
9428     autoSize: Roo.emptyFn,
9429     // private
9430     monitorTab : true,
9431     // private
9432     deferHeight : true,
9433
9434     
9435     actionMode : 'wrap',
9436     
9437     caret : false,
9438     
9439     
9440     getAutoCreate : function(){
9441        
9442         var align = this.labelAlign || this.parentLabelAlign();
9443         
9444         var id = Roo.id();
9445         
9446         var cfg = {
9447             cls: 'form-group' //input-group
9448         };
9449         
9450         
9451         var input =  {
9452             tag: 'input',
9453             id : id,
9454             type : this.inputType,
9455             cls : 'form-control',
9456             autocomplete: 'new-password',
9457             placeholder : this.placeholder || '' 
9458             
9459         };
9460         if (this.name) {
9461             input.name = this.name;
9462         }
9463         if (this.size) {
9464             input.cls += ' input-' + this.size;
9465         }
9466         
9467         if (this.disabled) {
9468             input.disabled=true;
9469         }
9470         
9471         var inputblock = input;
9472         
9473         if(this.hasFeedback && !this.allowBlank){
9474             
9475             var feedback = {
9476                 tag: 'span',
9477                 cls: 'glyphicon form-control-feedback'
9478             };
9479             
9480             if(this.removable && !this.editable && !this.tickable){
9481                 inputblock = {
9482                     cls : 'has-feedback',
9483                     cn :  [
9484                         inputblock,
9485                         {
9486                             tag: 'button',
9487                             html : 'x',
9488                             cls : 'roo-combo-removable-btn close'
9489                         },
9490                         feedback
9491                     ] 
9492                 };
9493             } else {
9494                 inputblock = {
9495                     cls : 'has-feedback',
9496                     cn :  [
9497                         inputblock,
9498                         feedback
9499                     ] 
9500                 };
9501             }
9502
9503         } else {
9504             if(this.removable && !this.editable && !this.tickable){
9505                 inputblock = {
9506                     cls : 'roo-removable',
9507                     cn :  [
9508                         inputblock,
9509                         {
9510                             tag: 'button',
9511                             html : 'x',
9512                             cls : 'roo-combo-removable-btn close'
9513                         }
9514                     ] 
9515                 };
9516             }
9517         }
9518         
9519         if (this.before || this.after) {
9520             
9521             inputblock = {
9522                 cls : 'input-group',
9523                 cn :  [] 
9524             };
9525             if (this.before) {
9526                 inputblock.cn.push({
9527                     tag :'span',
9528                     cls : 'input-group-addon',
9529                     html : this.before
9530                 });
9531             }
9532             
9533             inputblock.cn.push(input);
9534             
9535             if(this.hasFeedback && !this.allowBlank){
9536                 inputblock.cls += ' has-feedback';
9537                 inputblock.cn.push(feedback);
9538             }
9539             
9540             if (this.after) {
9541                 inputblock.cn.push({
9542                     tag :'span',
9543                     cls : 'input-group-addon',
9544                     html : this.after
9545                 });
9546             }
9547             
9548         };
9549         
9550         var box = {
9551             tag: 'div',
9552             cn: [
9553                 {
9554                     tag: 'input',
9555                     type : 'hidden',
9556                     cls: 'form-hidden-field'
9557                 },
9558                 inputblock
9559             ]
9560             
9561         };
9562         
9563         if(this.multiple){
9564             box = {
9565                 tag: 'div',
9566                 cn: [
9567                     {
9568                         tag: 'input',
9569                         type : 'hidden',
9570                         cls: 'form-hidden-field'
9571                     },
9572                     {
9573                         tag: 'ul',
9574                         cls: 'roo-select2-choices',
9575                         cn:[
9576                             {
9577                                 tag: 'li',
9578                                 cls: 'roo-select2-search-field',
9579                                 cn: [
9580
9581                                     inputblock
9582                                 ]
9583                             }
9584                         ]
9585                     }
9586                 ]
9587             }
9588         };
9589         
9590         var combobox = {
9591             cls: 'roo-select2-container input-group',
9592             cn: [
9593                 box
9594 //                {
9595 //                    tag: 'ul',
9596 //                    cls: 'typeahead typeahead-long dropdown-menu',
9597 //                    style: 'display:none'
9598 //                }
9599             ]
9600         };
9601         
9602         if(!this.multiple && this.showToggleBtn){
9603             
9604             var caret = {
9605                         tag: 'span',
9606                         cls: 'caret'
9607              };
9608             if (this.caret != false) {
9609                 caret = {
9610                      tag: 'i',
9611                      cls: 'fa fa-' + this.caret
9612                 };
9613                 
9614             }
9615             
9616             combobox.cn.push({
9617                 tag :'span',
9618                 cls : 'input-group-addon btn dropdown-toggle',
9619                 cn : [
9620                     caret,
9621                     {
9622                         tag: 'span',
9623                         cls: 'combobox-clear',
9624                         cn  : [
9625                             {
9626                                 tag : 'i',
9627                                 cls: 'icon-remove'
9628                             }
9629                         ]
9630                     }
9631                 ]
9632
9633             })
9634         }
9635         
9636         if(this.multiple){
9637             combobox.cls += ' roo-select2-container-multi';
9638         }
9639         
9640         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9641             
9642 //                Roo.log("left and has label");
9643             cfg.cn = [
9644                 {
9645                     tag : 'i',
9646                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9647                     tooltip : 'This field is required'
9648                 },
9649                 {
9650                     tag: 'label',
9651                     'for' :  id,
9652                     cls : 'control-label col-sm-' + this.labelWidth,
9653                     html : this.fieldLabel
9654
9655                 },
9656                 {
9657                     cls : "col-sm-" + (12 - this.labelWidth), 
9658                     cn: [
9659                         combobox
9660                     ]
9661                 }
9662
9663             ];
9664             
9665             if(this.indicatorpos == 'right'){
9666                 cfg.cn = [
9667                     {
9668                         tag: 'label',
9669                         'for' :  id,
9670                         cls : 'control-label col-sm-' + this.labelWidth,
9671                         html : this.fieldLabel
9672
9673                     },
9674                     {
9675                         tag : 'i',
9676                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9677                         tooltip : 'This field is required'
9678                     },
9679                     {
9680                         cls : "col-sm-" + (12 - this.labelWidth), 
9681                         cn: [
9682                             combobox
9683                         ]
9684                     }
9685
9686                 ];
9687             }
9688             
9689         } else if ( this.fieldLabel.length) {
9690 //                Roo.log(" label");
9691             cfg.cn = [
9692                 {
9693                    tag : 'i',
9694                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9695                    tooltip : 'This field is required'
9696                },
9697                {
9698                    tag: 'label',
9699                    //cls : 'input-group-addon',
9700                    html : this.fieldLabel
9701
9702                },
9703
9704                combobox
9705
9706             ];
9707             
9708             if(this.indicatorpos == 'right'){
9709                 
9710                 cfg.cn = [
9711                     {
9712                        tag: 'label',
9713                        //cls : 'input-group-addon',
9714                        html : this.fieldLabel
9715
9716                     },
9717                     {
9718                        tag : 'i',
9719                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9720                        tooltip : 'This field is required'
9721                     },
9722                     
9723                     combobox
9724
9725                 ];
9726
9727             }
9728
9729         } else {
9730             
9731 //                Roo.log(" no label && no align");
9732                 cfg = combobox
9733                      
9734                 
9735         }
9736          
9737         var settings=this;
9738         ['xs','sm','md','lg'].map(function(size){
9739             if (settings[size]) {
9740                 cfg.cls += ' col-' + size + '-' + settings[size];
9741             }
9742         });
9743         
9744         return cfg;
9745         
9746     },
9747     
9748     
9749     
9750     // private
9751     onResize : function(w, h){
9752 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9753 //        if(typeof w == 'number'){
9754 //            var x = w - this.trigger.getWidth();
9755 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9756 //            this.trigger.setStyle('left', x+'px');
9757 //        }
9758     },
9759
9760     // private
9761     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9762
9763     // private
9764     getResizeEl : function(){
9765         return this.inputEl();
9766     },
9767
9768     // private
9769     getPositionEl : function(){
9770         return this.inputEl();
9771     },
9772
9773     // private
9774     alignErrorIcon : function(){
9775         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9776     },
9777
9778     // private
9779     initEvents : function(){
9780         
9781         this.createList();
9782         
9783         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9784         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9785         if(!this.multiple && this.showToggleBtn){
9786             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9787             if(this.hideTrigger){
9788                 this.trigger.setDisplayed(false);
9789             }
9790             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9791         }
9792         
9793         if(this.multiple){
9794             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9795         }
9796         
9797         if(this.removable && !this.editable && !this.tickable){
9798             var close = this.closeTriggerEl();
9799             
9800             if(close){
9801                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9802                 close.on('click', this.removeBtnClick, this, close);
9803             }
9804         }
9805         
9806         //this.trigger.addClassOnOver('x-form-trigger-over');
9807         //this.trigger.addClassOnClick('x-form-trigger-click');
9808         
9809         //if(!this.width){
9810         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9811         //}
9812     },
9813     
9814     closeTriggerEl : function()
9815     {
9816         var close = this.el.select('.roo-combo-removable-btn', true).first();
9817         return close ? close : false;
9818     },
9819     
9820     removeBtnClick : function(e, h, el)
9821     {
9822         e.preventDefault();
9823         
9824         if(this.fireEvent("remove", this) !== false){
9825             this.reset();
9826             this.fireEvent("afterremove", this)
9827         }
9828     },
9829     
9830     createList : function()
9831     {
9832         this.list = Roo.get(document.body).createChild({
9833             tag: 'ul',
9834             cls: 'typeahead typeahead-long dropdown-menu',
9835             style: 'display:none'
9836         });
9837         
9838         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9839         
9840     },
9841
9842     // private
9843     initTrigger : function(){
9844        
9845     },
9846
9847     // private
9848     onDestroy : function(){
9849         if(this.trigger){
9850             this.trigger.removeAllListeners();
9851           //  this.trigger.remove();
9852         }
9853         //if(this.wrap){
9854         //    this.wrap.remove();
9855         //}
9856         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9857     },
9858
9859     // private
9860     onFocus : function(){
9861         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9862         /*
9863         if(!this.mimicing){
9864             this.wrap.addClass('x-trigger-wrap-focus');
9865             this.mimicing = true;
9866             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9867             if(this.monitorTab){
9868                 this.el.on("keydown", this.checkTab, this);
9869             }
9870         }
9871         */
9872     },
9873
9874     // private
9875     checkTab : function(e){
9876         if(e.getKey() == e.TAB){
9877             this.triggerBlur();
9878         }
9879     },
9880
9881     // private
9882     onBlur : function(){
9883         // do nothing
9884     },
9885
9886     // private
9887     mimicBlur : function(e, t){
9888         /*
9889         if(!this.wrap.contains(t) && this.validateBlur()){
9890             this.triggerBlur();
9891         }
9892         */
9893     },
9894
9895     // private
9896     triggerBlur : function(){
9897         this.mimicing = false;
9898         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9899         if(this.monitorTab){
9900             this.el.un("keydown", this.checkTab, this);
9901         }
9902         //this.wrap.removeClass('x-trigger-wrap-focus');
9903         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9904     },
9905
9906     // private
9907     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9908     validateBlur : function(e, t){
9909         return true;
9910     },
9911
9912     // private
9913     onDisable : function(){
9914         this.inputEl().dom.disabled = true;
9915         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9916         //if(this.wrap){
9917         //    this.wrap.addClass('x-item-disabled');
9918         //}
9919     },
9920
9921     // private
9922     onEnable : function(){
9923         this.inputEl().dom.disabled = false;
9924         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9925         //if(this.wrap){
9926         //    this.el.removeClass('x-item-disabled');
9927         //}
9928     },
9929
9930     // private
9931     onShow : function(){
9932         var ae = this.getActionEl();
9933         
9934         if(ae){
9935             ae.dom.style.display = '';
9936             ae.dom.style.visibility = 'visible';
9937         }
9938     },
9939
9940     // private
9941     
9942     onHide : function(){
9943         var ae = this.getActionEl();
9944         ae.dom.style.display = 'none';
9945     },
9946
9947     /**
9948      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9949      * by an implementing function.
9950      * @method
9951      * @param {EventObject} e
9952      */
9953     onTriggerClick : Roo.emptyFn
9954 });
9955  /*
9956  * Based on:
9957  * Ext JS Library 1.1.1
9958  * Copyright(c) 2006-2007, Ext JS, LLC.
9959  *
9960  * Originally Released Under LGPL - original licence link has changed is not relivant.
9961  *
9962  * Fork - LGPL
9963  * <script type="text/javascript">
9964  */
9965
9966
9967 /**
9968  * @class Roo.data.SortTypes
9969  * @singleton
9970  * Defines the default sorting (casting?) comparison functions used when sorting data.
9971  */
9972 Roo.data.SortTypes = {
9973     /**
9974      * Default sort that does nothing
9975      * @param {Mixed} s The value being converted
9976      * @return {Mixed} The comparison value
9977      */
9978     none : function(s){
9979         return s;
9980     },
9981     
9982     /**
9983      * The regular expression used to strip tags
9984      * @type {RegExp}
9985      * @property
9986      */
9987     stripTagsRE : /<\/?[^>]+>/gi,
9988     
9989     /**
9990      * Strips all HTML tags to sort on text only
9991      * @param {Mixed} s The value being converted
9992      * @return {String} The comparison value
9993      */
9994     asText : function(s){
9995         return String(s).replace(this.stripTagsRE, "");
9996     },
9997     
9998     /**
9999      * Strips all HTML tags to sort on text only - Case insensitive
10000      * @param {Mixed} s The value being converted
10001      * @return {String} The comparison value
10002      */
10003     asUCText : function(s){
10004         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10005     },
10006     
10007     /**
10008      * Case insensitive string
10009      * @param {Mixed} s The value being converted
10010      * @return {String} The comparison value
10011      */
10012     asUCString : function(s) {
10013         return String(s).toUpperCase();
10014     },
10015     
10016     /**
10017      * Date sorting
10018      * @param {Mixed} s The value being converted
10019      * @return {Number} The comparison value
10020      */
10021     asDate : function(s) {
10022         if(!s){
10023             return 0;
10024         }
10025         if(s instanceof Date){
10026             return s.getTime();
10027         }
10028         return Date.parse(String(s));
10029     },
10030     
10031     /**
10032      * Float sorting
10033      * @param {Mixed} s The value being converted
10034      * @return {Float} The comparison value
10035      */
10036     asFloat : function(s) {
10037         var val = parseFloat(String(s).replace(/,/g, ""));
10038         if(isNaN(val)) {
10039             val = 0;
10040         }
10041         return val;
10042     },
10043     
10044     /**
10045      * Integer sorting
10046      * @param {Mixed} s The value being converted
10047      * @return {Number} The comparison value
10048      */
10049     asInt : function(s) {
10050         var val = parseInt(String(s).replace(/,/g, ""));
10051         if(isNaN(val)) {
10052             val = 0;
10053         }
10054         return val;
10055     }
10056 };/*
10057  * Based on:
10058  * Ext JS Library 1.1.1
10059  * Copyright(c) 2006-2007, Ext JS, LLC.
10060  *
10061  * Originally Released Under LGPL - original licence link has changed is not relivant.
10062  *
10063  * Fork - LGPL
10064  * <script type="text/javascript">
10065  */
10066
10067 /**
10068 * @class Roo.data.Record
10069  * Instances of this class encapsulate both record <em>definition</em> information, and record
10070  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10071  * to access Records cached in an {@link Roo.data.Store} object.<br>
10072  * <p>
10073  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10074  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10075  * objects.<br>
10076  * <p>
10077  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10078  * @constructor
10079  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10080  * {@link #create}. The parameters are the same.
10081  * @param {Array} data An associative Array of data values keyed by the field name.
10082  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10083  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10084  * not specified an integer id is generated.
10085  */
10086 Roo.data.Record = function(data, id){
10087     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10088     this.data = data;
10089 };
10090
10091 /**
10092  * Generate a constructor for a specific record layout.
10093  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10094  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10095  * Each field definition object may contain the following properties: <ul>
10096  * <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,
10097  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10098  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10099  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10100  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10101  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10102  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10103  * this may be omitted.</p></li>
10104  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10105  * <ul><li>auto (Default, implies no conversion)</li>
10106  * <li>string</li>
10107  * <li>int</li>
10108  * <li>float</li>
10109  * <li>boolean</li>
10110  * <li>date</li></ul></p></li>
10111  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10112  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10113  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10114  * by the Reader into an object that will be stored in the Record. It is passed the
10115  * following parameters:<ul>
10116  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10117  * </ul></p></li>
10118  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10119  * </ul>
10120  * <br>usage:<br><pre><code>
10121 var TopicRecord = Roo.data.Record.create(
10122     {name: 'title', mapping: 'topic_title'},
10123     {name: 'author', mapping: 'username'},
10124     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10125     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10126     {name: 'lastPoster', mapping: 'user2'},
10127     {name: 'excerpt', mapping: 'post_text'}
10128 );
10129
10130 var myNewRecord = new TopicRecord({
10131     title: 'Do my job please',
10132     author: 'noobie',
10133     totalPosts: 1,
10134     lastPost: new Date(),
10135     lastPoster: 'Animal',
10136     excerpt: 'No way dude!'
10137 });
10138 myStore.add(myNewRecord);
10139 </code></pre>
10140  * @method create
10141  * @static
10142  */
10143 Roo.data.Record.create = function(o){
10144     var f = function(){
10145         f.superclass.constructor.apply(this, arguments);
10146     };
10147     Roo.extend(f, Roo.data.Record);
10148     var p = f.prototype;
10149     p.fields = new Roo.util.MixedCollection(false, function(field){
10150         return field.name;
10151     });
10152     for(var i = 0, len = o.length; i < len; i++){
10153         p.fields.add(new Roo.data.Field(o[i]));
10154     }
10155     f.getField = function(name){
10156         return p.fields.get(name);  
10157     };
10158     return f;
10159 };
10160
10161 Roo.data.Record.AUTO_ID = 1000;
10162 Roo.data.Record.EDIT = 'edit';
10163 Roo.data.Record.REJECT = 'reject';
10164 Roo.data.Record.COMMIT = 'commit';
10165
10166 Roo.data.Record.prototype = {
10167     /**
10168      * Readonly flag - true if this record has been modified.
10169      * @type Boolean
10170      */
10171     dirty : false,
10172     editing : false,
10173     error: null,
10174     modified: null,
10175
10176     // private
10177     join : function(store){
10178         this.store = store;
10179     },
10180
10181     /**
10182      * Set the named field to the specified value.
10183      * @param {String} name The name of the field to set.
10184      * @param {Object} value The value to set the field to.
10185      */
10186     set : function(name, value){
10187         if(this.data[name] == value){
10188             return;
10189         }
10190         this.dirty = true;
10191         if(!this.modified){
10192             this.modified = {};
10193         }
10194         if(typeof this.modified[name] == 'undefined'){
10195             this.modified[name] = this.data[name];
10196         }
10197         this.data[name] = value;
10198         if(!this.editing && this.store){
10199             this.store.afterEdit(this);
10200         }       
10201     },
10202
10203     /**
10204      * Get the value of the named field.
10205      * @param {String} name The name of the field to get the value of.
10206      * @return {Object} The value of the field.
10207      */
10208     get : function(name){
10209         return this.data[name]; 
10210     },
10211
10212     // private
10213     beginEdit : function(){
10214         this.editing = true;
10215         this.modified = {}; 
10216     },
10217
10218     // private
10219     cancelEdit : function(){
10220         this.editing = false;
10221         delete this.modified;
10222     },
10223
10224     // private
10225     endEdit : function(){
10226         this.editing = false;
10227         if(this.dirty && this.store){
10228             this.store.afterEdit(this);
10229         }
10230     },
10231
10232     /**
10233      * Usually called by the {@link Roo.data.Store} which owns the Record.
10234      * Rejects all changes made to the Record since either creation, or the last commit operation.
10235      * Modified fields are reverted to their original values.
10236      * <p>
10237      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10238      * of reject operations.
10239      */
10240     reject : function(){
10241         var m = this.modified;
10242         for(var n in m){
10243             if(typeof m[n] != "function"){
10244                 this.data[n] = m[n];
10245             }
10246         }
10247         this.dirty = false;
10248         delete this.modified;
10249         this.editing = false;
10250         if(this.store){
10251             this.store.afterReject(this);
10252         }
10253     },
10254
10255     /**
10256      * Usually called by the {@link Roo.data.Store} which owns the Record.
10257      * Commits all changes made to the Record since either creation, or the last commit operation.
10258      * <p>
10259      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10260      * of commit operations.
10261      */
10262     commit : function(){
10263         this.dirty = false;
10264         delete this.modified;
10265         this.editing = false;
10266         if(this.store){
10267             this.store.afterCommit(this);
10268         }
10269     },
10270
10271     // private
10272     hasError : function(){
10273         return this.error != null;
10274     },
10275
10276     // private
10277     clearError : function(){
10278         this.error = null;
10279     },
10280
10281     /**
10282      * Creates a copy of this record.
10283      * @param {String} id (optional) A new record id if you don't want to use this record's id
10284      * @return {Record}
10285      */
10286     copy : function(newId) {
10287         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10288     }
10289 };/*
10290  * Based on:
10291  * Ext JS Library 1.1.1
10292  * Copyright(c) 2006-2007, Ext JS, LLC.
10293  *
10294  * Originally Released Under LGPL - original licence link has changed is not relivant.
10295  *
10296  * Fork - LGPL
10297  * <script type="text/javascript">
10298  */
10299
10300
10301
10302 /**
10303  * @class Roo.data.Store
10304  * @extends Roo.util.Observable
10305  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10306  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10307  * <p>
10308  * 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
10309  * has no knowledge of the format of the data returned by the Proxy.<br>
10310  * <p>
10311  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10312  * instances from the data object. These records are cached and made available through accessor functions.
10313  * @constructor
10314  * Creates a new Store.
10315  * @param {Object} config A config object containing the objects needed for the Store to access data,
10316  * and read the data into Records.
10317  */
10318 Roo.data.Store = function(config){
10319     this.data = new Roo.util.MixedCollection(false);
10320     this.data.getKey = function(o){
10321         return o.id;
10322     };
10323     this.baseParams = {};
10324     // private
10325     this.paramNames = {
10326         "start" : "start",
10327         "limit" : "limit",
10328         "sort" : "sort",
10329         "dir" : "dir",
10330         "multisort" : "_multisort"
10331     };
10332
10333     if(config && config.data){
10334         this.inlineData = config.data;
10335         delete config.data;
10336     }
10337
10338     Roo.apply(this, config);
10339     
10340     if(this.reader){ // reader passed
10341         this.reader = Roo.factory(this.reader, Roo.data);
10342         this.reader.xmodule = this.xmodule || false;
10343         if(!this.recordType){
10344             this.recordType = this.reader.recordType;
10345         }
10346         if(this.reader.onMetaChange){
10347             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10348         }
10349     }
10350
10351     if(this.recordType){
10352         this.fields = this.recordType.prototype.fields;
10353     }
10354     this.modified = [];
10355
10356     this.addEvents({
10357         /**
10358          * @event datachanged
10359          * Fires when the data cache has changed, and a widget which is using this Store
10360          * as a Record cache should refresh its view.
10361          * @param {Store} this
10362          */
10363         datachanged : true,
10364         /**
10365          * @event metachange
10366          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10367          * @param {Store} this
10368          * @param {Object} meta The JSON metadata
10369          */
10370         metachange : true,
10371         /**
10372          * @event add
10373          * Fires when Records have been added to the Store
10374          * @param {Store} this
10375          * @param {Roo.data.Record[]} records The array of Records added
10376          * @param {Number} index The index at which the record(s) were added
10377          */
10378         add : true,
10379         /**
10380          * @event remove
10381          * Fires when a Record has been removed from the Store
10382          * @param {Store} this
10383          * @param {Roo.data.Record} record The Record that was removed
10384          * @param {Number} index The index at which the record was removed
10385          */
10386         remove : true,
10387         /**
10388          * @event update
10389          * Fires when a Record has been updated
10390          * @param {Store} this
10391          * @param {Roo.data.Record} record The Record that was updated
10392          * @param {String} operation The update operation being performed.  Value may be one of:
10393          * <pre><code>
10394  Roo.data.Record.EDIT
10395  Roo.data.Record.REJECT
10396  Roo.data.Record.COMMIT
10397          * </code></pre>
10398          */
10399         update : true,
10400         /**
10401          * @event clear
10402          * Fires when the data cache has been cleared.
10403          * @param {Store} this
10404          */
10405         clear : true,
10406         /**
10407          * @event beforeload
10408          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10409          * the load action will be canceled.
10410          * @param {Store} this
10411          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10412          */
10413         beforeload : true,
10414         /**
10415          * @event beforeloadadd
10416          * Fires after a new set of Records has been loaded.
10417          * @param {Store} this
10418          * @param {Roo.data.Record[]} records The Records that were loaded
10419          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10420          */
10421         beforeloadadd : true,
10422         /**
10423          * @event load
10424          * Fires after a new set of Records has been loaded, before they are added to the store.
10425          * @param {Store} this
10426          * @param {Roo.data.Record[]} records The Records that were loaded
10427          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10428          * @params {Object} return from reader
10429          */
10430         load : true,
10431         /**
10432          * @event loadexception
10433          * Fires if an exception occurs in the Proxy during loading.
10434          * Called with the signature of the Proxy's "loadexception" event.
10435          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10436          * 
10437          * @param {Proxy} 
10438          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10439          * @param {Object} load options 
10440          * @param {Object} jsonData from your request (normally this contains the Exception)
10441          */
10442         loadexception : true
10443     });
10444     
10445     if(this.proxy){
10446         this.proxy = Roo.factory(this.proxy, Roo.data);
10447         this.proxy.xmodule = this.xmodule || false;
10448         this.relayEvents(this.proxy,  ["loadexception"]);
10449     }
10450     this.sortToggle = {};
10451     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10452
10453     Roo.data.Store.superclass.constructor.call(this);
10454
10455     if(this.inlineData){
10456         this.loadData(this.inlineData);
10457         delete this.inlineData;
10458     }
10459 };
10460
10461 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10462      /**
10463     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10464     * without a remote query - used by combo/forms at present.
10465     */
10466     
10467     /**
10468     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10469     */
10470     /**
10471     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10472     */
10473     /**
10474     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10475     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10476     */
10477     /**
10478     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10479     * on any HTTP request
10480     */
10481     /**
10482     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10483     */
10484     /**
10485     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10486     */
10487     multiSort: false,
10488     /**
10489     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10490     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10491     */
10492     remoteSort : false,
10493
10494     /**
10495     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10496      * loaded or when a record is removed. (defaults to false).
10497     */
10498     pruneModifiedRecords : false,
10499
10500     // private
10501     lastOptions : null,
10502
10503     /**
10504      * Add Records to the Store and fires the add event.
10505      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10506      */
10507     add : function(records){
10508         records = [].concat(records);
10509         for(var i = 0, len = records.length; i < len; i++){
10510             records[i].join(this);
10511         }
10512         var index = this.data.length;
10513         this.data.addAll(records);
10514         this.fireEvent("add", this, records, index);
10515     },
10516
10517     /**
10518      * Remove a Record from the Store and fires the remove event.
10519      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10520      */
10521     remove : function(record){
10522         var index = this.data.indexOf(record);
10523         this.data.removeAt(index);
10524         if(this.pruneModifiedRecords){
10525             this.modified.remove(record);
10526         }
10527         this.fireEvent("remove", this, record, index);
10528     },
10529
10530     /**
10531      * Remove all Records from the Store and fires the clear event.
10532      */
10533     removeAll : function(){
10534         this.data.clear();
10535         if(this.pruneModifiedRecords){
10536             this.modified = [];
10537         }
10538         this.fireEvent("clear", this);
10539     },
10540
10541     /**
10542      * Inserts Records to the Store at the given index and fires the add event.
10543      * @param {Number} index The start index at which to insert the passed Records.
10544      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10545      */
10546     insert : function(index, records){
10547         records = [].concat(records);
10548         for(var i = 0, len = records.length; i < len; i++){
10549             this.data.insert(index, records[i]);
10550             records[i].join(this);
10551         }
10552         this.fireEvent("add", this, records, index);
10553     },
10554
10555     /**
10556      * Get the index within the cache of the passed Record.
10557      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10558      * @return {Number} The index of the passed Record. Returns -1 if not found.
10559      */
10560     indexOf : function(record){
10561         return this.data.indexOf(record);
10562     },
10563
10564     /**
10565      * Get the index within the cache of the Record with the passed id.
10566      * @param {String} id The id of the Record to find.
10567      * @return {Number} The index of the Record. Returns -1 if not found.
10568      */
10569     indexOfId : function(id){
10570         return this.data.indexOfKey(id);
10571     },
10572
10573     /**
10574      * Get the Record with the specified id.
10575      * @param {String} id The id of the Record to find.
10576      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10577      */
10578     getById : function(id){
10579         return this.data.key(id);
10580     },
10581
10582     /**
10583      * Get the Record at the specified index.
10584      * @param {Number} index The index of the Record to find.
10585      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10586      */
10587     getAt : function(index){
10588         return this.data.itemAt(index);
10589     },
10590
10591     /**
10592      * Returns a range of Records between specified indices.
10593      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10594      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10595      * @return {Roo.data.Record[]} An array of Records
10596      */
10597     getRange : function(start, end){
10598         return this.data.getRange(start, end);
10599     },
10600
10601     // private
10602     storeOptions : function(o){
10603         o = Roo.apply({}, o);
10604         delete o.callback;
10605         delete o.scope;
10606         this.lastOptions = o;
10607     },
10608
10609     /**
10610      * Loads the Record cache from the configured Proxy using the configured Reader.
10611      * <p>
10612      * If using remote paging, then the first load call must specify the <em>start</em>
10613      * and <em>limit</em> properties in the options.params property to establish the initial
10614      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10615      * <p>
10616      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10617      * and this call will return before the new data has been loaded. Perform any post-processing
10618      * in a callback function, or in a "load" event handler.</strong>
10619      * <p>
10620      * @param {Object} options An object containing properties which control loading options:<ul>
10621      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10622      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10623      * passed the following arguments:<ul>
10624      * <li>r : Roo.data.Record[]</li>
10625      * <li>options: Options object from the load call</li>
10626      * <li>success: Boolean success indicator</li></ul></li>
10627      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10628      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10629      * </ul>
10630      */
10631     load : function(options){
10632         options = options || {};
10633         if(this.fireEvent("beforeload", this, options) !== false){
10634             this.storeOptions(options);
10635             var p = Roo.apply(options.params || {}, this.baseParams);
10636             // if meta was not loaded from remote source.. try requesting it.
10637             if (!this.reader.metaFromRemote) {
10638                 p._requestMeta = 1;
10639             }
10640             if(this.sortInfo && this.remoteSort){
10641                 var pn = this.paramNames;
10642                 p[pn["sort"]] = this.sortInfo.field;
10643                 p[pn["dir"]] = this.sortInfo.direction;
10644             }
10645             if (this.multiSort) {
10646                 var pn = this.paramNames;
10647                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10648             }
10649             
10650             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10651         }
10652     },
10653
10654     /**
10655      * Reloads the Record cache from the configured Proxy using the configured Reader and
10656      * the options from the last load operation performed.
10657      * @param {Object} options (optional) An object containing properties which may override the options
10658      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10659      * the most recently used options are reused).
10660      */
10661     reload : function(options){
10662         this.load(Roo.applyIf(options||{}, this.lastOptions));
10663     },
10664
10665     // private
10666     // Called as a callback by the Reader during a load operation.
10667     loadRecords : function(o, options, success){
10668         if(!o || success === false){
10669             if(success !== false){
10670                 this.fireEvent("load", this, [], options, o);
10671             }
10672             if(options.callback){
10673                 options.callback.call(options.scope || this, [], options, false);
10674             }
10675             return;
10676         }
10677         // if data returned failure - throw an exception.
10678         if (o.success === false) {
10679             // show a message if no listener is registered.
10680             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10681                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10682             }
10683             // loadmask wil be hooked into this..
10684             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10685             return;
10686         }
10687         var r = o.records, t = o.totalRecords || r.length;
10688         
10689         this.fireEvent("beforeloadadd", this, r, options, o);
10690         
10691         if(!options || options.add !== true){
10692             if(this.pruneModifiedRecords){
10693                 this.modified = [];
10694             }
10695             for(var i = 0, len = r.length; i < len; i++){
10696                 r[i].join(this);
10697             }
10698             if(this.snapshot){
10699                 this.data = this.snapshot;
10700                 delete this.snapshot;
10701             }
10702             this.data.clear();
10703             this.data.addAll(r);
10704             this.totalLength = t;
10705             this.applySort();
10706             this.fireEvent("datachanged", this);
10707         }else{
10708             this.totalLength = Math.max(t, this.data.length+r.length);
10709             this.add(r);
10710         }
10711         this.fireEvent("load", this, r, options, o);
10712         if(options.callback){
10713             options.callback.call(options.scope || this, r, options, true);
10714         }
10715     },
10716
10717
10718     /**
10719      * Loads data from a passed data block. A Reader which understands the format of the data
10720      * must have been configured in the constructor.
10721      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10722      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10723      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10724      */
10725     loadData : function(o, append){
10726         var r = this.reader.readRecords(o);
10727         this.loadRecords(r, {add: append}, true);
10728     },
10729
10730     /**
10731      * Gets the number of cached records.
10732      * <p>
10733      * <em>If using paging, this may not be the total size of the dataset. If the data object
10734      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10735      * the data set size</em>
10736      */
10737     getCount : function(){
10738         return this.data.length || 0;
10739     },
10740
10741     /**
10742      * Gets the total number of records in the dataset as returned by the server.
10743      * <p>
10744      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10745      * the dataset size</em>
10746      */
10747     getTotalCount : function(){
10748         return this.totalLength || 0;
10749     },
10750
10751     /**
10752      * Returns the sort state of the Store as an object with two properties:
10753      * <pre><code>
10754  field {String} The name of the field by which the Records are sorted
10755  direction {String} The sort order, "ASC" or "DESC"
10756      * </code></pre>
10757      */
10758     getSortState : function(){
10759         return this.sortInfo;
10760     },
10761
10762     // private
10763     applySort : function(){
10764         if(this.sortInfo && !this.remoteSort){
10765             var s = this.sortInfo, f = s.field;
10766             var st = this.fields.get(f).sortType;
10767             var fn = function(r1, r2){
10768                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10769                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10770             };
10771             this.data.sort(s.direction, fn);
10772             if(this.snapshot && this.snapshot != this.data){
10773                 this.snapshot.sort(s.direction, fn);
10774             }
10775         }
10776     },
10777
10778     /**
10779      * Sets the default sort column and order to be used by the next load operation.
10780      * @param {String} fieldName The name of the field to sort by.
10781      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10782      */
10783     setDefaultSort : function(field, dir){
10784         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10785     },
10786
10787     /**
10788      * Sort the Records.
10789      * If remote sorting is used, the sort is performed on the server, and the cache is
10790      * reloaded. If local sorting is used, the cache is sorted internally.
10791      * @param {String} fieldName The name of the field to sort by.
10792      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10793      */
10794     sort : function(fieldName, dir){
10795         var f = this.fields.get(fieldName);
10796         if(!dir){
10797             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10798             
10799             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10800                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10801             }else{
10802                 dir = f.sortDir;
10803             }
10804         }
10805         this.sortToggle[f.name] = dir;
10806         this.sortInfo = {field: f.name, direction: dir};
10807         if(!this.remoteSort){
10808             this.applySort();
10809             this.fireEvent("datachanged", this);
10810         }else{
10811             this.load(this.lastOptions);
10812         }
10813     },
10814
10815     /**
10816      * Calls the specified function for each of the Records in the cache.
10817      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10818      * Returning <em>false</em> aborts and exits the iteration.
10819      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10820      */
10821     each : function(fn, scope){
10822         this.data.each(fn, scope);
10823     },
10824
10825     /**
10826      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10827      * (e.g., during paging).
10828      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10829      */
10830     getModifiedRecords : function(){
10831         return this.modified;
10832     },
10833
10834     // private
10835     createFilterFn : function(property, value, anyMatch){
10836         if(!value.exec){ // not a regex
10837             value = String(value);
10838             if(value.length == 0){
10839                 return false;
10840             }
10841             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10842         }
10843         return function(r){
10844             return value.test(r.data[property]);
10845         };
10846     },
10847
10848     /**
10849      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10850      * @param {String} property A field on your records
10851      * @param {Number} start The record index to start at (defaults to 0)
10852      * @param {Number} end The last record index to include (defaults to length - 1)
10853      * @return {Number} The sum
10854      */
10855     sum : function(property, start, end){
10856         var rs = this.data.items, v = 0;
10857         start = start || 0;
10858         end = (end || end === 0) ? end : rs.length-1;
10859
10860         for(var i = start; i <= end; i++){
10861             v += (rs[i].data[property] || 0);
10862         }
10863         return v;
10864     },
10865
10866     /**
10867      * Filter the records by a specified property.
10868      * @param {String} field A field on your records
10869      * @param {String/RegExp} value Either a string that the field
10870      * should start with or a RegExp to test against the field
10871      * @param {Boolean} anyMatch True to match any part not just the beginning
10872      */
10873     filter : function(property, value, anyMatch){
10874         var fn = this.createFilterFn(property, value, anyMatch);
10875         return fn ? this.filterBy(fn) : this.clearFilter();
10876     },
10877
10878     /**
10879      * Filter by a function. The specified function will be called with each
10880      * record in this data source. If the function returns true the record is included,
10881      * otherwise it is filtered.
10882      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10883      * @param {Object} scope (optional) The scope of the function (defaults to this)
10884      */
10885     filterBy : function(fn, scope){
10886         this.snapshot = this.snapshot || this.data;
10887         this.data = this.queryBy(fn, scope||this);
10888         this.fireEvent("datachanged", this);
10889     },
10890
10891     /**
10892      * Query the records by a specified property.
10893      * @param {String} field A field on your records
10894      * @param {String/RegExp} value Either a string that the field
10895      * should start with or a RegExp to test against the field
10896      * @param {Boolean} anyMatch True to match any part not just the beginning
10897      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10898      */
10899     query : function(property, value, anyMatch){
10900         var fn = this.createFilterFn(property, value, anyMatch);
10901         return fn ? this.queryBy(fn) : this.data.clone();
10902     },
10903
10904     /**
10905      * Query by a function. The specified function will be called with each
10906      * record in this data source. If the function returns true the record is included
10907      * in the results.
10908      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10909      * @param {Object} scope (optional) The scope of the function (defaults to this)
10910       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10911      **/
10912     queryBy : function(fn, scope){
10913         var data = this.snapshot || this.data;
10914         return data.filterBy(fn, scope||this);
10915     },
10916
10917     /**
10918      * Collects unique values for a particular dataIndex from this store.
10919      * @param {String} dataIndex The property to collect
10920      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10921      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10922      * @return {Array} An array of the unique values
10923      **/
10924     collect : function(dataIndex, allowNull, bypassFilter){
10925         var d = (bypassFilter === true && this.snapshot) ?
10926                 this.snapshot.items : this.data.items;
10927         var v, sv, r = [], l = {};
10928         for(var i = 0, len = d.length; i < len; i++){
10929             v = d[i].data[dataIndex];
10930             sv = String(v);
10931             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10932                 l[sv] = true;
10933                 r[r.length] = v;
10934             }
10935         }
10936         return r;
10937     },
10938
10939     /**
10940      * Revert to a view of the Record cache with no filtering applied.
10941      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10942      */
10943     clearFilter : function(suppressEvent){
10944         if(this.snapshot && this.snapshot != this.data){
10945             this.data = this.snapshot;
10946             delete this.snapshot;
10947             if(suppressEvent !== true){
10948                 this.fireEvent("datachanged", this);
10949             }
10950         }
10951     },
10952
10953     // private
10954     afterEdit : function(record){
10955         if(this.modified.indexOf(record) == -1){
10956             this.modified.push(record);
10957         }
10958         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10959     },
10960     
10961     // private
10962     afterReject : function(record){
10963         this.modified.remove(record);
10964         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10965     },
10966
10967     // private
10968     afterCommit : function(record){
10969         this.modified.remove(record);
10970         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10971     },
10972
10973     /**
10974      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10975      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10976      */
10977     commitChanges : function(){
10978         var m = this.modified.slice(0);
10979         this.modified = [];
10980         for(var i = 0, len = m.length; i < len; i++){
10981             m[i].commit();
10982         }
10983     },
10984
10985     /**
10986      * Cancel outstanding changes on all changed records.
10987      */
10988     rejectChanges : function(){
10989         var m = this.modified.slice(0);
10990         this.modified = [];
10991         for(var i = 0, len = m.length; i < len; i++){
10992             m[i].reject();
10993         }
10994     },
10995
10996     onMetaChange : function(meta, rtype, o){
10997         this.recordType = rtype;
10998         this.fields = rtype.prototype.fields;
10999         delete this.snapshot;
11000         this.sortInfo = meta.sortInfo || this.sortInfo;
11001         this.modified = [];
11002         this.fireEvent('metachange', this, this.reader.meta);
11003     },
11004     
11005     moveIndex : function(data, type)
11006     {
11007         var index = this.indexOf(data);
11008         
11009         var newIndex = index + type;
11010         
11011         this.remove(data);
11012         
11013         this.insert(newIndex, data);
11014         
11015     }
11016 });/*
11017  * Based on:
11018  * Ext JS Library 1.1.1
11019  * Copyright(c) 2006-2007, Ext JS, LLC.
11020  *
11021  * Originally Released Under LGPL - original licence link has changed is not relivant.
11022  *
11023  * Fork - LGPL
11024  * <script type="text/javascript">
11025  */
11026
11027 /**
11028  * @class Roo.data.SimpleStore
11029  * @extends Roo.data.Store
11030  * Small helper class to make creating Stores from Array data easier.
11031  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11032  * @cfg {Array} fields An array of field definition objects, or field name strings.
11033  * @cfg {Array} data The multi-dimensional array of data
11034  * @constructor
11035  * @param {Object} config
11036  */
11037 Roo.data.SimpleStore = function(config){
11038     Roo.data.SimpleStore.superclass.constructor.call(this, {
11039         isLocal : true,
11040         reader: new Roo.data.ArrayReader({
11041                 id: config.id
11042             },
11043             Roo.data.Record.create(config.fields)
11044         ),
11045         proxy : new Roo.data.MemoryProxy(config.data)
11046     });
11047     this.load();
11048 };
11049 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11050  * Based on:
11051  * Ext JS Library 1.1.1
11052  * Copyright(c) 2006-2007, Ext JS, LLC.
11053  *
11054  * Originally Released Under LGPL - original licence link has changed is not relivant.
11055  *
11056  * Fork - LGPL
11057  * <script type="text/javascript">
11058  */
11059
11060 /**
11061 /**
11062  * @extends Roo.data.Store
11063  * @class Roo.data.JsonStore
11064  * Small helper class to make creating Stores for JSON data easier. <br/>
11065 <pre><code>
11066 var store = new Roo.data.JsonStore({
11067     url: 'get-images.php',
11068     root: 'images',
11069     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11070 });
11071 </code></pre>
11072  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11073  * JsonReader and HttpProxy (unless inline data is provided).</b>
11074  * @cfg {Array} fields An array of field definition objects, or field name strings.
11075  * @constructor
11076  * @param {Object} config
11077  */
11078 Roo.data.JsonStore = function(c){
11079     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11080         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11081         reader: new Roo.data.JsonReader(c, c.fields)
11082     }));
11083 };
11084 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11085  * Based on:
11086  * Ext JS Library 1.1.1
11087  * Copyright(c) 2006-2007, Ext JS, LLC.
11088  *
11089  * Originally Released Under LGPL - original licence link has changed is not relivant.
11090  *
11091  * Fork - LGPL
11092  * <script type="text/javascript">
11093  */
11094
11095  
11096 Roo.data.Field = function(config){
11097     if(typeof config == "string"){
11098         config = {name: config};
11099     }
11100     Roo.apply(this, config);
11101     
11102     if(!this.type){
11103         this.type = "auto";
11104     }
11105     
11106     var st = Roo.data.SortTypes;
11107     // named sortTypes are supported, here we look them up
11108     if(typeof this.sortType == "string"){
11109         this.sortType = st[this.sortType];
11110     }
11111     
11112     // set default sortType for strings and dates
11113     if(!this.sortType){
11114         switch(this.type){
11115             case "string":
11116                 this.sortType = st.asUCString;
11117                 break;
11118             case "date":
11119                 this.sortType = st.asDate;
11120                 break;
11121             default:
11122                 this.sortType = st.none;
11123         }
11124     }
11125
11126     // define once
11127     var stripRe = /[\$,%]/g;
11128
11129     // prebuilt conversion function for this field, instead of
11130     // switching every time we're reading a value
11131     if(!this.convert){
11132         var cv, dateFormat = this.dateFormat;
11133         switch(this.type){
11134             case "":
11135             case "auto":
11136             case undefined:
11137                 cv = function(v){ return v; };
11138                 break;
11139             case "string":
11140                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11141                 break;
11142             case "int":
11143                 cv = function(v){
11144                     return v !== undefined && v !== null && v !== '' ?
11145                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11146                     };
11147                 break;
11148             case "float":
11149                 cv = function(v){
11150                     return v !== undefined && v !== null && v !== '' ?
11151                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11152                     };
11153                 break;
11154             case "bool":
11155             case "boolean":
11156                 cv = function(v){ return v === true || v === "true" || v == 1; };
11157                 break;
11158             case "date":
11159                 cv = function(v){
11160                     if(!v){
11161                         return '';
11162                     }
11163                     if(v instanceof Date){
11164                         return v;
11165                     }
11166                     if(dateFormat){
11167                         if(dateFormat == "timestamp"){
11168                             return new Date(v*1000);
11169                         }
11170                         return Date.parseDate(v, dateFormat);
11171                     }
11172                     var parsed = Date.parse(v);
11173                     return parsed ? new Date(parsed) : null;
11174                 };
11175              break;
11176             
11177         }
11178         this.convert = cv;
11179     }
11180 };
11181
11182 Roo.data.Field.prototype = {
11183     dateFormat: null,
11184     defaultValue: "",
11185     mapping: null,
11186     sortType : null,
11187     sortDir : "ASC"
11188 };/*
11189  * Based on:
11190  * Ext JS Library 1.1.1
11191  * Copyright(c) 2006-2007, Ext JS, LLC.
11192  *
11193  * Originally Released Under LGPL - original licence link has changed is not relivant.
11194  *
11195  * Fork - LGPL
11196  * <script type="text/javascript">
11197  */
11198  
11199 // Base class for reading structured data from a data source.  This class is intended to be
11200 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11201
11202 /**
11203  * @class Roo.data.DataReader
11204  * Base class for reading structured data from a data source.  This class is intended to be
11205  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11206  */
11207
11208 Roo.data.DataReader = function(meta, recordType){
11209     
11210     this.meta = meta;
11211     
11212     this.recordType = recordType instanceof Array ? 
11213         Roo.data.Record.create(recordType) : recordType;
11214 };
11215
11216 Roo.data.DataReader.prototype = {
11217      /**
11218      * Create an empty record
11219      * @param {Object} data (optional) - overlay some values
11220      * @return {Roo.data.Record} record created.
11221      */
11222     newRow :  function(d) {
11223         var da =  {};
11224         this.recordType.prototype.fields.each(function(c) {
11225             switch( c.type) {
11226                 case 'int' : da[c.name] = 0; break;
11227                 case 'date' : da[c.name] = new Date(); break;
11228                 case 'float' : da[c.name] = 0.0; break;
11229                 case 'boolean' : da[c.name] = false; break;
11230                 default : da[c.name] = ""; break;
11231             }
11232             
11233         });
11234         return new this.recordType(Roo.apply(da, d));
11235     }
11236     
11237 };/*
11238  * Based on:
11239  * Ext JS Library 1.1.1
11240  * Copyright(c) 2006-2007, Ext JS, LLC.
11241  *
11242  * Originally Released Under LGPL - original licence link has changed is not relivant.
11243  *
11244  * Fork - LGPL
11245  * <script type="text/javascript">
11246  */
11247
11248 /**
11249  * @class Roo.data.DataProxy
11250  * @extends Roo.data.Observable
11251  * This class is an abstract base class for implementations which provide retrieval of
11252  * unformatted data objects.<br>
11253  * <p>
11254  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11255  * (of the appropriate type which knows how to parse the data object) to provide a block of
11256  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11257  * <p>
11258  * Custom implementations must implement the load method as described in
11259  * {@link Roo.data.HttpProxy#load}.
11260  */
11261 Roo.data.DataProxy = function(){
11262     this.addEvents({
11263         /**
11264          * @event beforeload
11265          * Fires before a network request is made to retrieve a data object.
11266          * @param {Object} This DataProxy object.
11267          * @param {Object} params The params parameter to the load function.
11268          */
11269         beforeload : true,
11270         /**
11271          * @event load
11272          * Fires before the load method's callback is called.
11273          * @param {Object} This DataProxy object.
11274          * @param {Object} o The data object.
11275          * @param {Object} arg The callback argument object passed to the load function.
11276          */
11277         load : true,
11278         /**
11279          * @event loadexception
11280          * Fires if an Exception occurs during data retrieval.
11281          * @param {Object} This DataProxy object.
11282          * @param {Object} o The data object.
11283          * @param {Object} arg The callback argument object passed to the load function.
11284          * @param {Object} e The Exception.
11285          */
11286         loadexception : true
11287     });
11288     Roo.data.DataProxy.superclass.constructor.call(this);
11289 };
11290
11291 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11292
11293     /**
11294      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11295      */
11296 /*
11297  * Based on:
11298  * Ext JS Library 1.1.1
11299  * Copyright(c) 2006-2007, Ext JS, LLC.
11300  *
11301  * Originally Released Under LGPL - original licence link has changed is not relivant.
11302  *
11303  * Fork - LGPL
11304  * <script type="text/javascript">
11305  */
11306 /**
11307  * @class Roo.data.MemoryProxy
11308  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11309  * to the Reader when its load method is called.
11310  * @constructor
11311  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11312  */
11313 Roo.data.MemoryProxy = function(data){
11314     if (data.data) {
11315         data = data.data;
11316     }
11317     Roo.data.MemoryProxy.superclass.constructor.call(this);
11318     this.data = data;
11319 };
11320
11321 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11322     
11323     /**
11324      * Load data from the requested source (in this case an in-memory
11325      * data object passed to the constructor), read the data object into
11326      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11327      * process that block using the passed callback.
11328      * @param {Object} params This parameter is not used by the MemoryProxy class.
11329      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11330      * object into a block of Roo.data.Records.
11331      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11332      * The function must be passed <ul>
11333      * <li>The Record block object</li>
11334      * <li>The "arg" argument from the load function</li>
11335      * <li>A boolean success indicator</li>
11336      * </ul>
11337      * @param {Object} scope The scope in which to call the callback
11338      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11339      */
11340     load : function(params, reader, callback, scope, arg){
11341         params = params || {};
11342         var result;
11343         try {
11344             result = reader.readRecords(this.data);
11345         }catch(e){
11346             this.fireEvent("loadexception", this, arg, null, e);
11347             callback.call(scope, null, arg, false);
11348             return;
11349         }
11350         callback.call(scope, result, arg, true);
11351     },
11352     
11353     // private
11354     update : function(params, records){
11355         
11356     }
11357 });/*
11358  * Based on:
11359  * Ext JS Library 1.1.1
11360  * Copyright(c) 2006-2007, Ext JS, LLC.
11361  *
11362  * Originally Released Under LGPL - original licence link has changed is not relivant.
11363  *
11364  * Fork - LGPL
11365  * <script type="text/javascript">
11366  */
11367 /**
11368  * @class Roo.data.HttpProxy
11369  * @extends Roo.data.DataProxy
11370  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11371  * configured to reference a certain URL.<br><br>
11372  * <p>
11373  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11374  * from which the running page was served.<br><br>
11375  * <p>
11376  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11377  * <p>
11378  * Be aware that to enable the browser to parse an XML document, the server must set
11379  * the Content-Type header in the HTTP response to "text/xml".
11380  * @constructor
11381  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11382  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11383  * will be used to make the request.
11384  */
11385 Roo.data.HttpProxy = function(conn){
11386     Roo.data.HttpProxy.superclass.constructor.call(this);
11387     // is conn a conn config or a real conn?
11388     this.conn = conn;
11389     this.useAjax = !conn || !conn.events;
11390   
11391 };
11392
11393 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11394     // thse are take from connection...
11395     
11396     /**
11397      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11398      */
11399     /**
11400      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11401      * extra parameters to each request made by this object. (defaults to undefined)
11402      */
11403     /**
11404      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11405      *  to each request made by this object. (defaults to undefined)
11406      */
11407     /**
11408      * @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)
11409      */
11410     /**
11411      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11412      */
11413      /**
11414      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11415      * @type Boolean
11416      */
11417   
11418
11419     /**
11420      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11421      * @type Boolean
11422      */
11423     /**
11424      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11425      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11426      * a finer-grained basis than the DataProxy events.
11427      */
11428     getConnection : function(){
11429         return this.useAjax ? Roo.Ajax : this.conn;
11430     },
11431
11432     /**
11433      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11434      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11435      * process that block using the passed callback.
11436      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11437      * for the request to the remote server.
11438      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11439      * object into a block of Roo.data.Records.
11440      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11441      * The function must be passed <ul>
11442      * <li>The Record block object</li>
11443      * <li>The "arg" argument from the load function</li>
11444      * <li>A boolean success indicator</li>
11445      * </ul>
11446      * @param {Object} scope The scope in which to call the callback
11447      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11448      */
11449     load : function(params, reader, callback, scope, arg){
11450         if(this.fireEvent("beforeload", this, params) !== false){
11451             var  o = {
11452                 params : params || {},
11453                 request: {
11454                     callback : callback,
11455                     scope : scope,
11456                     arg : arg
11457                 },
11458                 reader: reader,
11459                 callback : this.loadResponse,
11460                 scope: this
11461             };
11462             if(this.useAjax){
11463                 Roo.applyIf(o, this.conn);
11464                 if(this.activeRequest){
11465                     Roo.Ajax.abort(this.activeRequest);
11466                 }
11467                 this.activeRequest = Roo.Ajax.request(o);
11468             }else{
11469                 this.conn.request(o);
11470             }
11471         }else{
11472             callback.call(scope||this, null, arg, false);
11473         }
11474     },
11475
11476     // private
11477     loadResponse : function(o, success, response){
11478         delete this.activeRequest;
11479         if(!success){
11480             this.fireEvent("loadexception", this, o, response);
11481             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11482             return;
11483         }
11484         var result;
11485         try {
11486             result = o.reader.read(response);
11487         }catch(e){
11488             this.fireEvent("loadexception", this, o, response, e);
11489             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11490             return;
11491         }
11492         
11493         this.fireEvent("load", this, o, o.request.arg);
11494         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11495     },
11496
11497     // private
11498     update : function(dataSet){
11499
11500     },
11501
11502     // private
11503     updateResponse : function(dataSet){
11504
11505     }
11506 });/*
11507  * Based on:
11508  * Ext JS Library 1.1.1
11509  * Copyright(c) 2006-2007, Ext JS, LLC.
11510  *
11511  * Originally Released Under LGPL - original licence link has changed is not relivant.
11512  *
11513  * Fork - LGPL
11514  * <script type="text/javascript">
11515  */
11516
11517 /**
11518  * @class Roo.data.ScriptTagProxy
11519  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11520  * other than the originating domain of the running page.<br><br>
11521  * <p>
11522  * <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
11523  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11524  * <p>
11525  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11526  * source code that is used as the source inside a &lt;script> tag.<br><br>
11527  * <p>
11528  * In order for the browser to process the returned data, the server must wrap the data object
11529  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11530  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11531  * depending on whether the callback name was passed:
11532  * <p>
11533  * <pre><code>
11534 boolean scriptTag = false;
11535 String cb = request.getParameter("callback");
11536 if (cb != null) {
11537     scriptTag = true;
11538     response.setContentType("text/javascript");
11539 } else {
11540     response.setContentType("application/x-json");
11541 }
11542 Writer out = response.getWriter();
11543 if (scriptTag) {
11544     out.write(cb + "(");
11545 }
11546 out.print(dataBlock.toJsonString());
11547 if (scriptTag) {
11548     out.write(");");
11549 }
11550 </pre></code>
11551  *
11552  * @constructor
11553  * @param {Object} config A configuration object.
11554  */
11555 Roo.data.ScriptTagProxy = function(config){
11556     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11557     Roo.apply(this, config);
11558     this.head = document.getElementsByTagName("head")[0];
11559 };
11560
11561 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11562
11563 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11564     /**
11565      * @cfg {String} url The URL from which to request the data object.
11566      */
11567     /**
11568      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11569      */
11570     timeout : 30000,
11571     /**
11572      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11573      * the server the name of the callback function set up by the load call to process the returned data object.
11574      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11575      * javascript output which calls this named function passing the data object as its only parameter.
11576      */
11577     callbackParam : "callback",
11578     /**
11579      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11580      * name to the request.
11581      */
11582     nocache : true,
11583
11584     /**
11585      * Load data from the configured URL, read the data object into
11586      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11587      * process that block using the passed callback.
11588      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11589      * for the request to the remote server.
11590      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11591      * object into a block of Roo.data.Records.
11592      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11593      * The function must be passed <ul>
11594      * <li>The Record block object</li>
11595      * <li>The "arg" argument from the load function</li>
11596      * <li>A boolean success indicator</li>
11597      * </ul>
11598      * @param {Object} scope The scope in which to call the callback
11599      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11600      */
11601     load : function(params, reader, callback, scope, arg){
11602         if(this.fireEvent("beforeload", this, params) !== false){
11603
11604             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11605
11606             var url = this.url;
11607             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11608             if(this.nocache){
11609                 url += "&_dc=" + (new Date().getTime());
11610             }
11611             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11612             var trans = {
11613                 id : transId,
11614                 cb : "stcCallback"+transId,
11615                 scriptId : "stcScript"+transId,
11616                 params : params,
11617                 arg : arg,
11618                 url : url,
11619                 callback : callback,
11620                 scope : scope,
11621                 reader : reader
11622             };
11623             var conn = this;
11624
11625             window[trans.cb] = function(o){
11626                 conn.handleResponse(o, trans);
11627             };
11628
11629             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11630
11631             if(this.autoAbort !== false){
11632                 this.abort();
11633             }
11634
11635             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11636
11637             var script = document.createElement("script");
11638             script.setAttribute("src", url);
11639             script.setAttribute("type", "text/javascript");
11640             script.setAttribute("id", trans.scriptId);
11641             this.head.appendChild(script);
11642
11643             this.trans = trans;
11644         }else{
11645             callback.call(scope||this, null, arg, false);
11646         }
11647     },
11648
11649     // private
11650     isLoading : function(){
11651         return this.trans ? true : false;
11652     },
11653
11654     /**
11655      * Abort the current server request.
11656      */
11657     abort : function(){
11658         if(this.isLoading()){
11659             this.destroyTrans(this.trans);
11660         }
11661     },
11662
11663     // private
11664     destroyTrans : function(trans, isLoaded){
11665         this.head.removeChild(document.getElementById(trans.scriptId));
11666         clearTimeout(trans.timeoutId);
11667         if(isLoaded){
11668             window[trans.cb] = undefined;
11669             try{
11670                 delete window[trans.cb];
11671             }catch(e){}
11672         }else{
11673             // if hasn't been loaded, wait for load to remove it to prevent script error
11674             window[trans.cb] = function(){
11675                 window[trans.cb] = undefined;
11676                 try{
11677                     delete window[trans.cb];
11678                 }catch(e){}
11679             };
11680         }
11681     },
11682
11683     // private
11684     handleResponse : function(o, trans){
11685         this.trans = false;
11686         this.destroyTrans(trans, true);
11687         var result;
11688         try {
11689             result = trans.reader.readRecords(o);
11690         }catch(e){
11691             this.fireEvent("loadexception", this, o, trans.arg, e);
11692             trans.callback.call(trans.scope||window, null, trans.arg, false);
11693             return;
11694         }
11695         this.fireEvent("load", this, o, trans.arg);
11696         trans.callback.call(trans.scope||window, result, trans.arg, true);
11697     },
11698
11699     // private
11700     handleFailure : function(trans){
11701         this.trans = false;
11702         this.destroyTrans(trans, false);
11703         this.fireEvent("loadexception", this, null, trans.arg);
11704         trans.callback.call(trans.scope||window, null, trans.arg, false);
11705     }
11706 });/*
11707  * Based on:
11708  * Ext JS Library 1.1.1
11709  * Copyright(c) 2006-2007, Ext JS, LLC.
11710  *
11711  * Originally Released Under LGPL - original licence link has changed is not relivant.
11712  *
11713  * Fork - LGPL
11714  * <script type="text/javascript">
11715  */
11716
11717 /**
11718  * @class Roo.data.JsonReader
11719  * @extends Roo.data.DataReader
11720  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11721  * based on mappings in a provided Roo.data.Record constructor.
11722  * 
11723  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11724  * in the reply previously. 
11725  * 
11726  * <p>
11727  * Example code:
11728  * <pre><code>
11729 var RecordDef = Roo.data.Record.create([
11730     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11731     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11732 ]);
11733 var myReader = new Roo.data.JsonReader({
11734     totalProperty: "results",    // The property which contains the total dataset size (optional)
11735     root: "rows",                // The property which contains an Array of row objects
11736     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11737 }, RecordDef);
11738 </code></pre>
11739  * <p>
11740  * This would consume a JSON file like this:
11741  * <pre><code>
11742 { 'results': 2, 'rows': [
11743     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11744     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11745 }
11746 </code></pre>
11747  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11748  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11749  * paged from the remote server.
11750  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11751  * @cfg {String} root name of the property which contains the Array of row objects.
11752  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11753  * @cfg {Array} fields Array of field definition objects
11754  * @constructor
11755  * Create a new JsonReader
11756  * @param {Object} meta Metadata configuration options
11757  * @param {Object} recordType Either an Array of field definition objects,
11758  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11759  */
11760 Roo.data.JsonReader = function(meta, recordType){
11761     
11762     meta = meta || {};
11763     // set some defaults:
11764     Roo.applyIf(meta, {
11765         totalProperty: 'total',
11766         successProperty : 'success',
11767         root : 'data',
11768         id : 'id'
11769     });
11770     
11771     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11772 };
11773 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11774     
11775     /**
11776      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11777      * Used by Store query builder to append _requestMeta to params.
11778      * 
11779      */
11780     metaFromRemote : false,
11781     /**
11782      * This method is only used by a DataProxy which has retrieved data from a remote server.
11783      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11784      * @return {Object} data A data block which is used by an Roo.data.Store object as
11785      * a cache of Roo.data.Records.
11786      */
11787     read : function(response){
11788         var json = response.responseText;
11789        
11790         var o = /* eval:var:o */ eval("("+json+")");
11791         if(!o) {
11792             throw {message: "JsonReader.read: Json object not found"};
11793         }
11794         
11795         if(o.metaData){
11796             
11797             delete this.ef;
11798             this.metaFromRemote = true;
11799             this.meta = o.metaData;
11800             this.recordType = Roo.data.Record.create(o.metaData.fields);
11801             this.onMetaChange(this.meta, this.recordType, o);
11802         }
11803         return this.readRecords(o);
11804     },
11805
11806     // private function a store will implement
11807     onMetaChange : function(meta, recordType, o){
11808
11809     },
11810
11811     /**
11812          * @ignore
11813          */
11814     simpleAccess: function(obj, subsc) {
11815         return obj[subsc];
11816     },
11817
11818         /**
11819          * @ignore
11820          */
11821     getJsonAccessor: function(){
11822         var re = /[\[\.]/;
11823         return function(expr) {
11824             try {
11825                 return(re.test(expr))
11826                     ? new Function("obj", "return obj." + expr)
11827                     : function(obj){
11828                         return obj[expr];
11829                     };
11830             } catch(e){}
11831             return Roo.emptyFn;
11832         };
11833     }(),
11834
11835     /**
11836      * Create a data block containing Roo.data.Records from an XML document.
11837      * @param {Object} o An object which contains an Array of row objects in the property specified
11838      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11839      * which contains the total size of the dataset.
11840      * @return {Object} data A data block which is used by an Roo.data.Store object as
11841      * a cache of Roo.data.Records.
11842      */
11843     readRecords : function(o){
11844         /**
11845          * After any data loads, the raw JSON data is available for further custom processing.
11846          * @type Object
11847          */
11848         this.o = o;
11849         var s = this.meta, Record = this.recordType,
11850             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11851
11852 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11853         if (!this.ef) {
11854             if(s.totalProperty) {
11855                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11856                 }
11857                 if(s.successProperty) {
11858                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11859                 }
11860                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11861                 if (s.id) {
11862                         var g = this.getJsonAccessor(s.id);
11863                         this.getId = function(rec) {
11864                                 var r = g(rec);  
11865                                 return (r === undefined || r === "") ? null : r;
11866                         };
11867                 } else {
11868                         this.getId = function(){return null;};
11869                 }
11870             this.ef = [];
11871             for(var jj = 0; jj < fl; jj++){
11872                 f = fi[jj];
11873                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11874                 this.ef[jj] = this.getJsonAccessor(map);
11875             }
11876         }
11877
11878         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11879         if(s.totalProperty){
11880             var vt = parseInt(this.getTotal(o), 10);
11881             if(!isNaN(vt)){
11882                 totalRecords = vt;
11883             }
11884         }
11885         if(s.successProperty){
11886             var vs = this.getSuccess(o);
11887             if(vs === false || vs === 'false'){
11888                 success = false;
11889             }
11890         }
11891         var records = [];
11892         for(var i = 0; i < c; i++){
11893                 var n = root[i];
11894             var values = {};
11895             var id = this.getId(n);
11896             for(var j = 0; j < fl; j++){
11897                 f = fi[j];
11898             var v = this.ef[j](n);
11899             if (!f.convert) {
11900                 Roo.log('missing convert for ' + f.name);
11901                 Roo.log(f);
11902                 continue;
11903             }
11904             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11905             }
11906             var record = new Record(values, id);
11907             record.json = n;
11908             records[i] = record;
11909         }
11910         return {
11911             raw : o,
11912             success : success,
11913             records : records,
11914             totalRecords : totalRecords
11915         };
11916     }
11917 });/*
11918  * Based on:
11919  * Ext JS Library 1.1.1
11920  * Copyright(c) 2006-2007, Ext JS, LLC.
11921  *
11922  * Originally Released Under LGPL - original licence link has changed is not relivant.
11923  *
11924  * Fork - LGPL
11925  * <script type="text/javascript">
11926  */
11927
11928 /**
11929  * @class Roo.data.ArrayReader
11930  * @extends Roo.data.DataReader
11931  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11932  * Each element of that Array represents a row of data fields. The
11933  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11934  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11935  * <p>
11936  * Example code:.
11937  * <pre><code>
11938 var RecordDef = Roo.data.Record.create([
11939     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11940     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11941 ]);
11942 var myReader = new Roo.data.ArrayReader({
11943     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11944 }, RecordDef);
11945 </code></pre>
11946  * <p>
11947  * This would consume an Array like this:
11948  * <pre><code>
11949 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11950   </code></pre>
11951  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11952  * @constructor
11953  * Create a new JsonReader
11954  * @param {Object} meta Metadata configuration options.
11955  * @param {Object} recordType Either an Array of field definition objects
11956  * as specified to {@link Roo.data.Record#create},
11957  * or an {@link Roo.data.Record} object
11958  * created using {@link Roo.data.Record#create}.
11959  */
11960 Roo.data.ArrayReader = function(meta, recordType){
11961     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11962 };
11963
11964 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11965     /**
11966      * Create a data block containing Roo.data.Records from an XML document.
11967      * @param {Object} o An Array of row objects which represents the dataset.
11968      * @return {Object} data A data block which is used by an Roo.data.Store object as
11969      * a cache of Roo.data.Records.
11970      */
11971     readRecords : function(o){
11972         var sid = this.meta ? this.meta.id : null;
11973         var recordType = this.recordType, fields = recordType.prototype.fields;
11974         var records = [];
11975         var root = o;
11976             for(var i = 0; i < root.length; i++){
11977                     var n = root[i];
11978                 var values = {};
11979                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11980                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11981                 var f = fields.items[j];
11982                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11983                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11984                 v = f.convert(v);
11985                 values[f.name] = v;
11986             }
11987                 var record = new recordType(values, id);
11988                 record.json = n;
11989                 records[records.length] = record;
11990             }
11991             return {
11992                 records : records,
11993                 totalRecords : records.length
11994             };
11995     }
11996 });/*
11997  * - LGPL
11998  * * 
11999  */
12000
12001 /**
12002  * @class Roo.bootstrap.ComboBox
12003  * @extends Roo.bootstrap.TriggerField
12004  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12005  * @cfg {Boolean} append (true|false) default false
12006  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12007  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12008  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12009  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12010  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12011  * @cfg {Boolean} animate default true
12012  * @cfg {Boolean} emptyResultText only for touch device
12013  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12014  * @constructor
12015  * Create a new ComboBox.
12016  * @param {Object} config Configuration options
12017  */
12018 Roo.bootstrap.ComboBox = function(config){
12019     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12020     this.addEvents({
12021         /**
12022          * @event expand
12023          * Fires when the dropdown list is expanded
12024              * @param {Roo.bootstrap.ComboBox} combo This combo box
12025              */
12026         'expand' : true,
12027         /**
12028          * @event collapse
12029          * Fires when the dropdown list is collapsed
12030              * @param {Roo.bootstrap.ComboBox} combo This combo box
12031              */
12032         'collapse' : true,
12033         /**
12034          * @event beforeselect
12035          * Fires before a list item is selected. Return false to cancel the selection.
12036              * @param {Roo.bootstrap.ComboBox} combo This combo box
12037              * @param {Roo.data.Record} record The data record returned from the underlying store
12038              * @param {Number} index The index of the selected item in the dropdown list
12039              */
12040         'beforeselect' : true,
12041         /**
12042          * @event select
12043          * Fires when a list item is selected
12044              * @param {Roo.bootstrap.ComboBox} combo This combo box
12045              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12046              * @param {Number} index The index of the selected item in the dropdown list
12047              */
12048         'select' : true,
12049         /**
12050          * @event beforequery
12051          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12052          * The event object passed has these properties:
12053              * @param {Roo.bootstrap.ComboBox} combo This combo box
12054              * @param {String} query The query
12055              * @param {Boolean} forceAll true to force "all" query
12056              * @param {Boolean} cancel true to cancel the query
12057              * @param {Object} e The query event object
12058              */
12059         'beforequery': true,
12060          /**
12061          * @event add
12062          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12063              * @param {Roo.bootstrap.ComboBox} combo This combo box
12064              */
12065         'add' : true,
12066         /**
12067          * @event edit
12068          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12069              * @param {Roo.bootstrap.ComboBox} combo This combo box
12070              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12071              */
12072         'edit' : true,
12073         /**
12074          * @event remove
12075          * Fires when the remove value from the combobox array
12076              * @param {Roo.bootstrap.ComboBox} combo This combo box
12077              */
12078         'remove' : true,
12079         /**
12080          * @event afterremove
12081          * Fires when the remove value from the combobox array
12082              * @param {Roo.bootstrap.ComboBox} combo This combo box
12083              */
12084         'afterremove' : true,
12085         /**
12086          * @event specialfilter
12087          * Fires when specialfilter
12088             * @param {Roo.bootstrap.ComboBox} combo This combo box
12089             */
12090         'specialfilter' : true,
12091         /**
12092          * @event tick
12093          * Fires when tick the element
12094             * @param {Roo.bootstrap.ComboBox} combo This combo box
12095             */
12096         'tick' : true,
12097         /**
12098          * @event touchviewdisplay
12099          * Fires when touch view require special display (default is using displayField)
12100             * @param {Roo.bootstrap.ComboBox} combo This combo box
12101             * @param {Object} cfg set html .
12102             */
12103         'touchviewdisplay' : true
12104         
12105     });
12106     
12107     this.item = [];
12108     this.tickItems = [];
12109     
12110     this.selectedIndex = -1;
12111     if(this.mode == 'local'){
12112         if(config.queryDelay === undefined){
12113             this.queryDelay = 10;
12114         }
12115         if(config.minChars === undefined){
12116             this.minChars = 0;
12117         }
12118     }
12119 };
12120
12121 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12122      
12123     /**
12124      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12125      * rendering into an Roo.Editor, defaults to false)
12126      */
12127     /**
12128      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12129      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12130      */
12131     /**
12132      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12133      */
12134     /**
12135      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12136      * the dropdown list (defaults to undefined, with no header element)
12137      */
12138
12139      /**
12140      * @cfg {String/Roo.Template} tpl The template to use to render the output
12141      */
12142      
12143      /**
12144      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12145      */
12146     listWidth: undefined,
12147     /**
12148      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12149      * mode = 'remote' or 'text' if mode = 'local')
12150      */
12151     displayField: undefined,
12152     
12153     /**
12154      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12155      * mode = 'remote' or 'value' if mode = 'local'). 
12156      * Note: use of a valueField requires the user make a selection
12157      * in order for a value to be mapped.
12158      */
12159     valueField: undefined,
12160     /**
12161      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12162      */
12163     modalTitle : '',
12164     
12165     /**
12166      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12167      * field's data value (defaults to the underlying DOM element's name)
12168      */
12169     hiddenName: undefined,
12170     /**
12171      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12172      */
12173     listClass: '',
12174     /**
12175      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12176      */
12177     selectedClass: 'active',
12178     
12179     /**
12180      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12181      */
12182     shadow:'sides',
12183     /**
12184      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12185      * anchor positions (defaults to 'tl-bl')
12186      */
12187     listAlign: 'tl-bl?',
12188     /**
12189      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12190      */
12191     maxHeight: 300,
12192     /**
12193      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12194      * query specified by the allQuery config option (defaults to 'query')
12195      */
12196     triggerAction: 'query',
12197     /**
12198      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12199      * (defaults to 4, does not apply if editable = false)
12200      */
12201     minChars : 4,
12202     /**
12203      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12204      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12205      */
12206     typeAhead: false,
12207     /**
12208      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12209      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12210      */
12211     queryDelay: 500,
12212     /**
12213      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12214      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12215      */
12216     pageSize: 0,
12217     /**
12218      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12219      * when editable = true (defaults to false)
12220      */
12221     selectOnFocus:false,
12222     /**
12223      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12224      */
12225     queryParam: 'query',
12226     /**
12227      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12228      * when mode = 'remote' (defaults to 'Loading...')
12229      */
12230     loadingText: 'Loading...',
12231     /**
12232      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12233      */
12234     resizable: false,
12235     /**
12236      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12237      */
12238     handleHeight : 8,
12239     /**
12240      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12241      * traditional select (defaults to true)
12242      */
12243     editable: true,
12244     /**
12245      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12246      */
12247     allQuery: '',
12248     /**
12249      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12250      */
12251     mode: 'remote',
12252     /**
12253      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12254      * listWidth has a higher value)
12255      */
12256     minListWidth : 70,
12257     /**
12258      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12259      * allow the user to set arbitrary text into the field (defaults to false)
12260      */
12261     forceSelection:false,
12262     /**
12263      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12264      * if typeAhead = true (defaults to 250)
12265      */
12266     typeAheadDelay : 250,
12267     /**
12268      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12269      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12270      */
12271     valueNotFoundText : undefined,
12272     /**
12273      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12274      */
12275     blockFocus : false,
12276     
12277     /**
12278      * @cfg {Boolean} disableClear Disable showing of clear button.
12279      */
12280     disableClear : false,
12281     /**
12282      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12283      */
12284     alwaysQuery : false,
12285     
12286     /**
12287      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12288      */
12289     multiple : false,
12290     
12291     /**
12292      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12293      */
12294     invalidClass : "has-warning",
12295     
12296     /**
12297      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12298      */
12299     validClass : "has-success",
12300     
12301     /**
12302      * @cfg {Boolean} specialFilter (true|false) special filter default false
12303      */
12304     specialFilter : false,
12305     
12306     /**
12307      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12308      */
12309     mobileTouchView : true,
12310     
12311     /**
12312      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12313      */
12314     useNativeIOS : false,
12315     
12316     ios_options : false,
12317     
12318     //private
12319     addicon : false,
12320     editicon: false,
12321     
12322     page: 0,
12323     hasQuery: false,
12324     append: false,
12325     loadNext: false,
12326     autoFocus : true,
12327     tickable : false,
12328     btnPosition : 'right',
12329     triggerList : true,
12330     showToggleBtn : true,
12331     animate : true,
12332     emptyResultText: 'Empty',
12333     triggerText : 'Select',
12334     
12335     // element that contains real text value.. (when hidden is used..)
12336     
12337     getAutoCreate : function()
12338     {
12339         var cfg = false;
12340         
12341         /*
12342          * Render classic select for iso
12343          */
12344         
12345         if(Roo.isIOS && this.useNativeIOS){
12346             cfg = this.getAutoCreateNativeIOS();
12347             return cfg;
12348         }
12349         
12350         /*
12351          * Touch Devices
12352          */
12353         
12354         if(Roo.isTouch && this.mobileTouchView){
12355             cfg = this.getAutoCreateTouchView();
12356             return cfg;;
12357         }
12358         
12359         /*
12360          *  Normal ComboBox
12361          */
12362         if(!this.tickable){
12363             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12364             return cfg;
12365         }
12366         
12367         /*
12368          *  ComboBox with tickable selections
12369          */
12370              
12371         var align = this.labelAlign || this.parentLabelAlign();
12372         
12373         cfg = {
12374             cls : 'form-group roo-combobox-tickable' //input-group
12375         };
12376         
12377         var buttons = {
12378             tag : 'div',
12379             cls : 'tickable-buttons',
12380             cn : [
12381                 {
12382                     tag : 'button',
12383                     type : 'button',
12384                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12385                     html : this.triggerText
12386                 },
12387                 {
12388                     tag : 'button',
12389                     type : 'button',
12390                     name : 'ok',
12391                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12392                     html : 'Done'
12393                 },
12394                 {
12395                     tag : 'button',
12396                     type : 'button',
12397                     name : 'cancel',
12398                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12399                     html : 'Cancel'
12400                 }
12401             ]
12402         };
12403         
12404         if(this.editable){
12405             buttons.cn.unshift({
12406                 tag: 'input',
12407                 cls: 'roo-select2-search-field-input'
12408             });
12409         }
12410         
12411         var _this = this;
12412         
12413         Roo.each(buttons.cn, function(c){
12414             if (_this.size) {
12415                 c.cls += ' btn-' + _this.size;
12416             }
12417
12418             if (_this.disabled) {
12419                 c.disabled = true;
12420             }
12421         });
12422         
12423         var box = {
12424             tag: 'div',
12425             cn: [
12426                 {
12427                     tag: 'input',
12428                     type : 'hidden',
12429                     cls: 'form-hidden-field'
12430                 },
12431                 {
12432                     tag: 'ul',
12433                     cls: 'roo-select2-choices',
12434                     cn:[
12435                         {
12436                             tag: 'li',
12437                             cls: 'roo-select2-search-field',
12438                             cn: [
12439
12440                                 buttons
12441                             ]
12442                         }
12443                     ]
12444                 }
12445             ]
12446         };
12447         
12448         var combobox = {
12449             cls: 'roo-select2-container input-group roo-select2-container-multi',
12450             cn: [
12451                 box
12452 //                {
12453 //                    tag: 'ul',
12454 //                    cls: 'typeahead typeahead-long dropdown-menu',
12455 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12456 //                }
12457             ]
12458         };
12459         
12460         if(this.hasFeedback && !this.allowBlank){
12461             
12462             var feedback = {
12463                 tag: 'span',
12464                 cls: 'glyphicon form-control-feedback'
12465             };
12466
12467             combobox.cn.push(feedback);
12468         }
12469         
12470         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12471             
12472 //                Roo.log("left and has label");
12473             cfg.cn = [
12474                 {
12475                     tag : 'i',
12476                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12477                     tooltip : 'This field is required'
12478                 },
12479                 {
12480                     tag: 'label',
12481                     'for' :  id,
12482                     cls : 'control-label col-sm-' + this.labelWidth,
12483                     html : this.fieldLabel
12484
12485                 },
12486                 {
12487                     cls : "col-sm-" + (12 - this.labelWidth), 
12488                     cn: [
12489                         combobox
12490                     ]
12491                 }
12492
12493             ];
12494
12495             if(this.indicatorpos == 'right'){
12496                 
12497                 cfg.cn = [
12498                     {
12499                         tag: 'label',
12500                         'for' :  id,
12501                         cls : 'control-label col-sm-' + this.labelWidth,
12502                         html : this.fieldLabel
12503
12504                     },
12505                     {
12506                         tag : 'i',
12507                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12508                         tooltip : 'This field is required'
12509                     },
12510                     {
12511                         cls : "col-sm-" + (12 - this.labelWidth), 
12512                         cn: [
12513                             combobox
12514                         ]
12515                     }
12516
12517                 ];
12518             
12519             }
12520                 
12521                 
12522         } else if ( this.fieldLabel.length) {
12523 //                Roo.log(" label");
12524                  cfg.cn = [
12525                     {
12526                         tag : 'i',
12527                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12528                         tooltip : 'This field is required'
12529                     },
12530                     {
12531                         tag: 'label',
12532                         //cls : 'input-group-addon',
12533                         html : this.fieldLabel
12534                         
12535                     },
12536                     
12537                     combobox
12538                     
12539                 ];
12540                 
12541                 if(this.indicatorpos == 'right'){
12542                     
12543                     cfg.cn = [
12544                         {
12545                             tag: 'label',
12546                             //cls : 'input-group-addon',
12547                             html : this.fieldLabel
12548
12549                         },
12550                         
12551                         {
12552                             tag : 'i',
12553                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12554                             tooltip : 'This field is required'
12555                         },
12556                         
12557                         combobox
12558
12559                     ];
12560                 
12561                 }
12562
12563         } else {
12564             
12565 //                Roo.log(" no label && no align");
12566                 cfg = combobox
12567                      
12568                 
12569         }
12570          
12571         var settings=this;
12572         ['xs','sm','md','lg'].map(function(size){
12573             if (settings[size]) {
12574                 cfg.cls += ' col-' + size + '-' + settings[size];
12575             }
12576         });
12577         
12578         return cfg;
12579         
12580     },
12581     
12582     _initEventsCalled : false,
12583     
12584     // private
12585     initEvents: function()
12586     {   
12587         if (this._initEventsCalled) { // as we call render... prevent looping...
12588             return;
12589         }
12590         this._initEventsCalled = true;
12591         
12592         if (!this.store) {
12593             throw "can not find store for combo";
12594         }
12595         
12596         this.store = Roo.factory(this.store, Roo.data);
12597         
12598         // if we are building from html. then this element is so complex, that we can not really
12599         // use the rendered HTML.
12600         // so we have to trash and replace the previous code.
12601         if (Roo.XComponent.build_from_html) {
12602             
12603             // remove this element....
12604             var e = this.el.dom, k=0;
12605             while (e ) { e = e.previousSibling;  ++k;}
12606
12607             this.el.remove();
12608             
12609             this.el=false;
12610             this.rendered = false;
12611             
12612             this.render(this.parent().getChildContainer(true), k);
12613             
12614             
12615             
12616         }
12617         
12618         if(Roo.isIOS && this.useNativeIOS){
12619             this.initIOSView();
12620             return;
12621         }
12622         
12623         /*
12624          * Touch Devices
12625          */
12626         
12627         if(Roo.isTouch && this.mobileTouchView){
12628             this.initTouchView();
12629             return;
12630         }
12631         
12632         if(this.tickable){
12633             this.initTickableEvents();
12634             return;
12635         }
12636         
12637         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12638         
12639         if(this.hiddenName){
12640             
12641             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12642             
12643             this.hiddenField.dom.value =
12644                 this.hiddenValue !== undefined ? this.hiddenValue :
12645                 this.value !== undefined ? this.value : '';
12646
12647             // prevent input submission
12648             this.el.dom.removeAttribute('name');
12649             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12650              
12651              
12652         }
12653         //if(Roo.isGecko){
12654         //    this.el.dom.setAttribute('autocomplete', 'off');
12655         //}
12656         
12657         var cls = 'x-combo-list';
12658         
12659         //this.list = new Roo.Layer({
12660         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12661         //});
12662         
12663         var _this = this;
12664         
12665         (function(){
12666             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12667             _this.list.setWidth(lw);
12668         }).defer(100);
12669         
12670         this.list.on('mouseover', this.onViewOver, this);
12671         this.list.on('mousemove', this.onViewMove, this);
12672         
12673         this.list.on('scroll', this.onViewScroll, this);
12674         
12675         /*
12676         this.list.swallowEvent('mousewheel');
12677         this.assetHeight = 0;
12678
12679         if(this.title){
12680             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12681             this.assetHeight += this.header.getHeight();
12682         }
12683
12684         this.innerList = this.list.createChild({cls:cls+'-inner'});
12685         this.innerList.on('mouseover', this.onViewOver, this);
12686         this.innerList.on('mousemove', this.onViewMove, this);
12687         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12688         
12689         if(this.allowBlank && !this.pageSize && !this.disableClear){
12690             this.footer = this.list.createChild({cls:cls+'-ft'});
12691             this.pageTb = new Roo.Toolbar(this.footer);
12692            
12693         }
12694         if(this.pageSize){
12695             this.footer = this.list.createChild({cls:cls+'-ft'});
12696             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12697                     {pageSize: this.pageSize});
12698             
12699         }
12700         
12701         if (this.pageTb && this.allowBlank && !this.disableClear) {
12702             var _this = this;
12703             this.pageTb.add(new Roo.Toolbar.Fill(), {
12704                 cls: 'x-btn-icon x-btn-clear',
12705                 text: '&#160;',
12706                 handler: function()
12707                 {
12708                     _this.collapse();
12709                     _this.clearValue();
12710                     _this.onSelect(false, -1);
12711                 }
12712             });
12713         }
12714         if (this.footer) {
12715             this.assetHeight += this.footer.getHeight();
12716         }
12717         */
12718             
12719         if(!this.tpl){
12720             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12721         }
12722
12723         this.view = new Roo.View(this.list, this.tpl, {
12724             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12725         });
12726         //this.view.wrapEl.setDisplayed(false);
12727         this.view.on('click', this.onViewClick, this);
12728         
12729         
12730         
12731         this.store.on('beforeload', this.onBeforeLoad, this);
12732         this.store.on('load', this.onLoad, this);
12733         this.store.on('loadexception', this.onLoadException, this);
12734         /*
12735         if(this.resizable){
12736             this.resizer = new Roo.Resizable(this.list,  {
12737                pinned:true, handles:'se'
12738             });
12739             this.resizer.on('resize', function(r, w, h){
12740                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12741                 this.listWidth = w;
12742                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12743                 this.restrictHeight();
12744             }, this);
12745             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12746         }
12747         */
12748         if(!this.editable){
12749             this.editable = true;
12750             this.setEditable(false);
12751         }
12752         
12753         /*
12754         
12755         if (typeof(this.events.add.listeners) != 'undefined') {
12756             
12757             this.addicon = this.wrap.createChild(
12758                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12759        
12760             this.addicon.on('click', function(e) {
12761                 this.fireEvent('add', this);
12762             }, this);
12763         }
12764         if (typeof(this.events.edit.listeners) != 'undefined') {
12765             
12766             this.editicon = this.wrap.createChild(
12767                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12768             if (this.addicon) {
12769                 this.editicon.setStyle('margin-left', '40px');
12770             }
12771             this.editicon.on('click', function(e) {
12772                 
12773                 // we fire even  if inothing is selected..
12774                 this.fireEvent('edit', this, this.lastData );
12775                 
12776             }, this);
12777         }
12778         */
12779         
12780         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12781             "up" : function(e){
12782                 this.inKeyMode = true;
12783                 this.selectPrev();
12784             },
12785
12786             "down" : function(e){
12787                 if(!this.isExpanded()){
12788                     this.onTriggerClick();
12789                 }else{
12790                     this.inKeyMode = true;
12791                     this.selectNext();
12792                 }
12793             },
12794
12795             "enter" : function(e){
12796 //                this.onViewClick();
12797                 //return true;
12798                 this.collapse();
12799                 
12800                 if(this.fireEvent("specialkey", this, e)){
12801                     this.onViewClick(false);
12802                 }
12803                 
12804                 return true;
12805             },
12806
12807             "esc" : function(e){
12808                 this.collapse();
12809             },
12810
12811             "tab" : function(e){
12812                 this.collapse();
12813                 
12814                 if(this.fireEvent("specialkey", this, e)){
12815                     this.onViewClick(false);
12816                 }
12817                 
12818                 return true;
12819             },
12820
12821             scope : this,
12822
12823             doRelay : function(foo, bar, hname){
12824                 if(hname == 'down' || this.scope.isExpanded()){
12825                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12826                 }
12827                 return true;
12828             },
12829
12830             forceKeyDown: true
12831         });
12832         
12833         
12834         this.queryDelay = Math.max(this.queryDelay || 10,
12835                 this.mode == 'local' ? 10 : 250);
12836         
12837         
12838         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12839         
12840         if(this.typeAhead){
12841             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12842         }
12843         if(this.editable !== false){
12844             this.inputEl().on("keyup", this.onKeyUp, this);
12845         }
12846         if(this.forceSelection){
12847             this.inputEl().on('blur', this.doForce, this);
12848         }
12849         
12850         if(this.multiple){
12851             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12852             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12853         }
12854     },
12855     
12856     initTickableEvents: function()
12857     {   
12858         this.createList();
12859         
12860         if(this.hiddenName){
12861             
12862             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12863             
12864             this.hiddenField.dom.value =
12865                 this.hiddenValue !== undefined ? this.hiddenValue :
12866                 this.value !== undefined ? this.value : '';
12867
12868             // prevent input submission
12869             this.el.dom.removeAttribute('name');
12870             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12871              
12872              
12873         }
12874         
12875 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12876         
12877         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12878         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12879         if(this.triggerList){
12880             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12881         }
12882          
12883         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12884         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12885         
12886         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12887         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12888         
12889         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12890         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12891         
12892         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12893         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12894         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12895         
12896         this.okBtn.hide();
12897         this.cancelBtn.hide();
12898         
12899         var _this = this;
12900         
12901         (function(){
12902             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12903             _this.list.setWidth(lw);
12904         }).defer(100);
12905         
12906         this.list.on('mouseover', this.onViewOver, this);
12907         this.list.on('mousemove', this.onViewMove, this);
12908         
12909         this.list.on('scroll', this.onViewScroll, this);
12910         
12911         if(!this.tpl){
12912             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></li>';
12913         }
12914
12915         this.view = new Roo.View(this.list, this.tpl, {
12916             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12917         });
12918         
12919         //this.view.wrapEl.setDisplayed(false);
12920         this.view.on('click', this.onViewClick, this);
12921         
12922         
12923         
12924         this.store.on('beforeload', this.onBeforeLoad, this);
12925         this.store.on('load', this.onLoad, this);
12926         this.store.on('loadexception', this.onLoadException, this);
12927         
12928         if(this.editable){
12929             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12930                 "up" : function(e){
12931                     this.inKeyMode = true;
12932                     this.selectPrev();
12933                 },
12934
12935                 "down" : function(e){
12936                     this.inKeyMode = true;
12937                     this.selectNext();
12938                 },
12939
12940                 "enter" : function(e){
12941                     if(this.fireEvent("specialkey", this, e)){
12942                         this.onViewClick(false);
12943                     }
12944                     
12945                     return true;
12946                 },
12947
12948                 "esc" : function(e){
12949                     this.onTickableFooterButtonClick(e, false, false);
12950                 },
12951
12952                 "tab" : function(e){
12953                     this.fireEvent("specialkey", this, e);
12954                     
12955                     this.onTickableFooterButtonClick(e, false, false);
12956                     
12957                     return true;
12958                 },
12959
12960                 scope : this,
12961
12962                 doRelay : function(e, fn, key){
12963                     if(this.scope.isExpanded()){
12964                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12965                     }
12966                     return true;
12967                 },
12968
12969                 forceKeyDown: true
12970             });
12971         }
12972         
12973         this.queryDelay = Math.max(this.queryDelay || 10,
12974                 this.mode == 'local' ? 10 : 250);
12975         
12976         
12977         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12978         
12979         if(this.typeAhead){
12980             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12981         }
12982         
12983         if(this.editable !== false){
12984             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12985         }
12986         
12987     },
12988
12989     onDestroy : function(){
12990         if(this.view){
12991             this.view.setStore(null);
12992             this.view.el.removeAllListeners();
12993             this.view.el.remove();
12994             this.view.purgeListeners();
12995         }
12996         if(this.list){
12997             this.list.dom.innerHTML  = '';
12998         }
12999         
13000         if(this.store){
13001             this.store.un('beforeload', this.onBeforeLoad, this);
13002             this.store.un('load', this.onLoad, this);
13003             this.store.un('loadexception', this.onLoadException, this);
13004         }
13005         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13006     },
13007
13008     // private
13009     fireKey : function(e){
13010         if(e.isNavKeyPress() && !this.list.isVisible()){
13011             this.fireEvent("specialkey", this, e);
13012         }
13013     },
13014
13015     // private
13016     onResize: function(w, h){
13017 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13018 //        
13019 //        if(typeof w != 'number'){
13020 //            // we do not handle it!?!?
13021 //            return;
13022 //        }
13023 //        var tw = this.trigger.getWidth();
13024 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13025 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13026 //        var x = w - tw;
13027 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13028 //            
13029 //        //this.trigger.setStyle('left', x+'px');
13030 //        
13031 //        if(this.list && this.listWidth === undefined){
13032 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13033 //            this.list.setWidth(lw);
13034 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13035 //        }
13036         
13037     
13038         
13039     },
13040
13041     /**
13042      * Allow or prevent the user from directly editing the field text.  If false is passed,
13043      * the user will only be able to select from the items defined in the dropdown list.  This method
13044      * is the runtime equivalent of setting the 'editable' config option at config time.
13045      * @param {Boolean} value True to allow the user to directly edit the field text
13046      */
13047     setEditable : function(value){
13048         if(value == this.editable){
13049             return;
13050         }
13051         this.editable = value;
13052         if(!value){
13053             this.inputEl().dom.setAttribute('readOnly', true);
13054             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13055             this.inputEl().addClass('x-combo-noedit');
13056         }else{
13057             this.inputEl().dom.setAttribute('readOnly', false);
13058             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13059             this.inputEl().removeClass('x-combo-noedit');
13060         }
13061     },
13062
13063     // private
13064     
13065     onBeforeLoad : function(combo,opts){
13066         if(!this.hasFocus){
13067             return;
13068         }
13069          if (!opts.add) {
13070             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13071          }
13072         this.restrictHeight();
13073         this.selectedIndex = -1;
13074     },
13075
13076     // private
13077     onLoad : function(){
13078         
13079         this.hasQuery = false;
13080         
13081         if(!this.hasFocus){
13082             return;
13083         }
13084         
13085         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13086             this.loading.hide();
13087         }
13088              
13089         if(this.store.getCount() > 0){
13090             this.expand();
13091             this.restrictHeight();
13092             if(this.lastQuery == this.allQuery){
13093                 if(this.editable && !this.tickable){
13094                     this.inputEl().dom.select();
13095                 }
13096                 
13097                 if(
13098                     !this.selectByValue(this.value, true) &&
13099                     this.autoFocus && 
13100                     (
13101                         !this.store.lastOptions ||
13102                         typeof(this.store.lastOptions.add) == 'undefined' || 
13103                         this.store.lastOptions.add != true
13104                     )
13105                 ){
13106                     this.select(0, true);
13107                 }
13108             }else{
13109                 if(this.autoFocus){
13110                     this.selectNext();
13111                 }
13112                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13113                     this.taTask.delay(this.typeAheadDelay);
13114                 }
13115             }
13116         }else{
13117             this.onEmptyResults();
13118         }
13119         
13120         //this.el.focus();
13121     },
13122     // private
13123     onLoadException : function()
13124     {
13125         this.hasQuery = false;
13126         
13127         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13128             this.loading.hide();
13129         }
13130         
13131         if(this.tickable && this.editable){
13132             return;
13133         }
13134         
13135         this.collapse();
13136         // only causes errors at present
13137         //Roo.log(this.store.reader.jsonData);
13138         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13139             // fixme
13140             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13141         //}
13142         
13143         
13144     },
13145     // private
13146     onTypeAhead : function(){
13147         if(this.store.getCount() > 0){
13148             var r = this.store.getAt(0);
13149             var newValue = r.data[this.displayField];
13150             var len = newValue.length;
13151             var selStart = this.getRawValue().length;
13152             
13153             if(selStart != len){
13154                 this.setRawValue(newValue);
13155                 this.selectText(selStart, newValue.length);
13156             }
13157         }
13158     },
13159
13160     // private
13161     onSelect : function(record, index){
13162         
13163         if(this.fireEvent('beforeselect', this, record, index) !== false){
13164         
13165             this.setFromData(index > -1 ? record.data : false);
13166             
13167             this.collapse();
13168             this.fireEvent('select', this, record, index);
13169         }
13170     },
13171
13172     /**
13173      * Returns the currently selected field value or empty string if no value is set.
13174      * @return {String} value The selected value
13175      */
13176     getValue : function()
13177     {
13178         if(Roo.isIOS && this.useNativeIOS){
13179             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13180         }
13181         
13182         if(this.multiple){
13183             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13184         }
13185         
13186         if(this.valueField){
13187             return typeof this.value != 'undefined' ? this.value : '';
13188         }else{
13189             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13190         }
13191     },
13192     
13193     getRawValue : function()
13194     {
13195         if(Roo.isIOS && this.useNativeIOS){
13196             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13197         }
13198         
13199         var v = this.inputEl().getValue();
13200         
13201         return v;
13202     },
13203
13204     /**
13205      * Clears any text/value currently set in the field
13206      */
13207     clearValue : function(){
13208         
13209         if(this.hiddenField){
13210             this.hiddenField.dom.value = '';
13211         }
13212         this.value = '';
13213         this.setRawValue('');
13214         this.lastSelectionText = '';
13215         this.lastData = false;
13216         
13217         var close = this.closeTriggerEl();
13218         
13219         if(close){
13220             close.hide();
13221         }
13222         
13223         this.validate();
13224         
13225     },
13226
13227     /**
13228      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13229      * will be displayed in the field.  If the value does not match the data value of an existing item,
13230      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13231      * Otherwise the field will be blank (although the value will still be set).
13232      * @param {String} value The value to match
13233      */
13234     setValue : function(v)
13235     {
13236         if(Roo.isIOS && this.useNativeIOS){
13237             this.setIOSValue(v);
13238             return;
13239         }
13240         
13241         if(this.multiple){
13242             this.syncValue();
13243             return;
13244         }
13245         
13246         var text = v;
13247         if(this.valueField){
13248             var r = this.findRecord(this.valueField, v);
13249             if(r){
13250                 text = r.data[this.displayField];
13251             }else if(this.valueNotFoundText !== undefined){
13252                 text = this.valueNotFoundText;
13253             }
13254         }
13255         this.lastSelectionText = text;
13256         if(this.hiddenField){
13257             this.hiddenField.dom.value = v;
13258         }
13259         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13260         this.value = v;
13261         
13262         var close = this.closeTriggerEl();
13263         
13264         if(close){
13265             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13266         }
13267         
13268         this.validate();
13269     },
13270     /**
13271      * @property {Object} the last set data for the element
13272      */
13273     
13274     lastData : false,
13275     /**
13276      * Sets the value of the field based on a object which is related to the record format for the store.
13277      * @param {Object} value the value to set as. or false on reset?
13278      */
13279     setFromData : function(o){
13280         
13281         if(this.multiple){
13282             this.addItem(o);
13283             return;
13284         }
13285             
13286         var dv = ''; // display value
13287         var vv = ''; // value value..
13288         this.lastData = o;
13289         if (this.displayField) {
13290             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13291         } else {
13292             // this is an error condition!!!
13293             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13294         }
13295         
13296         if(this.valueField){
13297             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13298         }
13299         
13300         var close = this.closeTriggerEl();
13301         
13302         if(close){
13303             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13304         }
13305         
13306         if(this.hiddenField){
13307             this.hiddenField.dom.value = vv;
13308             
13309             this.lastSelectionText = dv;
13310             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13311             this.value = vv;
13312             return;
13313         }
13314         // no hidden field.. - we store the value in 'value', but still display
13315         // display field!!!!
13316         this.lastSelectionText = dv;
13317         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13318         this.value = vv;
13319         
13320         
13321         
13322     },
13323     // private
13324     reset : function(){
13325         // overridden so that last data is reset..
13326         
13327         if(this.multiple){
13328             this.clearItem();
13329             return;
13330         }
13331         
13332         this.setValue(this.originalValue);
13333         //this.clearInvalid();
13334         this.lastData = false;
13335         if (this.view) {
13336             this.view.clearSelections();
13337         }
13338         
13339         this.validate();
13340     },
13341     // private
13342     findRecord : function(prop, value){
13343         var record;
13344         if(this.store.getCount() > 0){
13345             this.store.each(function(r){
13346                 if(r.data[prop] == value){
13347                     record = r;
13348                     return false;
13349                 }
13350                 return true;
13351             });
13352         }
13353         return record;
13354     },
13355     
13356     getName: function()
13357     {
13358         // returns hidden if it's set..
13359         if (!this.rendered) {return ''};
13360         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13361         
13362     },
13363     // private
13364     onViewMove : function(e, t){
13365         this.inKeyMode = false;
13366     },
13367
13368     // private
13369     onViewOver : function(e, t){
13370         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13371             return;
13372         }
13373         var item = this.view.findItemFromChild(t);
13374         
13375         if(item){
13376             var index = this.view.indexOf(item);
13377             this.select(index, false);
13378         }
13379     },
13380
13381     // private
13382     onViewClick : function(view, doFocus, el, e)
13383     {
13384         var index = this.view.getSelectedIndexes()[0];
13385         
13386         var r = this.store.getAt(index);
13387         
13388         if(this.tickable){
13389             
13390             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13391                 return;
13392             }
13393             
13394             var rm = false;
13395             var _this = this;
13396             
13397             Roo.each(this.tickItems, function(v,k){
13398                 
13399                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13400                     Roo.log(v);
13401                     _this.tickItems.splice(k, 1);
13402                     
13403                     if(typeof(e) == 'undefined' && view == false){
13404                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13405                     }
13406                     
13407                     rm = true;
13408                     return;
13409                 }
13410             });
13411             
13412             if(rm){
13413                 return;
13414             }
13415             
13416             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13417                 this.tickItems.push(r.data);
13418             }
13419             
13420             if(typeof(e) == 'undefined' && view == false){
13421                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13422             }
13423                     
13424             return;
13425         }
13426         
13427         if(r){
13428             this.onSelect(r, index);
13429         }
13430         if(doFocus !== false && !this.blockFocus){
13431             this.inputEl().focus();
13432         }
13433     },
13434
13435     // private
13436     restrictHeight : function(){
13437         //this.innerList.dom.style.height = '';
13438         //var inner = this.innerList.dom;
13439         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13440         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13441         //this.list.beginUpdate();
13442         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13443         this.list.alignTo(this.inputEl(), this.listAlign);
13444         this.list.alignTo(this.inputEl(), this.listAlign);
13445         //this.list.endUpdate();
13446     },
13447
13448     // private
13449     onEmptyResults : function(){
13450         
13451         if(this.tickable && this.editable){
13452             this.restrictHeight();
13453             return;
13454         }
13455         
13456         this.collapse();
13457     },
13458
13459     /**
13460      * Returns true if the dropdown list is expanded, else false.
13461      */
13462     isExpanded : function(){
13463         return this.list.isVisible();
13464     },
13465
13466     /**
13467      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13468      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13469      * @param {String} value The data value of the item to select
13470      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13471      * selected item if it is not currently in view (defaults to true)
13472      * @return {Boolean} True if the value matched an item in the list, else false
13473      */
13474     selectByValue : function(v, scrollIntoView){
13475         if(v !== undefined && v !== null){
13476             var r = this.findRecord(this.valueField || this.displayField, v);
13477             if(r){
13478                 this.select(this.store.indexOf(r), scrollIntoView);
13479                 return true;
13480             }
13481         }
13482         return false;
13483     },
13484
13485     /**
13486      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13487      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13488      * @param {Number} index The zero-based index of the list item to select
13489      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13490      * selected item if it is not currently in view (defaults to true)
13491      */
13492     select : function(index, scrollIntoView){
13493         this.selectedIndex = index;
13494         this.view.select(index);
13495         if(scrollIntoView !== false){
13496             var el = this.view.getNode(index);
13497             /*
13498              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13499              */
13500             if(el){
13501                 this.list.scrollChildIntoView(el, false);
13502             }
13503         }
13504     },
13505
13506     // private
13507     selectNext : function(){
13508         var ct = this.store.getCount();
13509         if(ct > 0){
13510             if(this.selectedIndex == -1){
13511                 this.select(0);
13512             }else if(this.selectedIndex < ct-1){
13513                 this.select(this.selectedIndex+1);
13514             }
13515         }
13516     },
13517
13518     // private
13519     selectPrev : function(){
13520         var ct = this.store.getCount();
13521         if(ct > 0){
13522             if(this.selectedIndex == -1){
13523                 this.select(0);
13524             }else if(this.selectedIndex != 0){
13525                 this.select(this.selectedIndex-1);
13526             }
13527         }
13528     },
13529
13530     // private
13531     onKeyUp : function(e){
13532         if(this.editable !== false && !e.isSpecialKey()){
13533             this.lastKey = e.getKey();
13534             this.dqTask.delay(this.queryDelay);
13535         }
13536     },
13537
13538     // private
13539     validateBlur : function(){
13540         return !this.list || !this.list.isVisible();   
13541     },
13542
13543     // private
13544     initQuery : function(){
13545         
13546         var v = this.getRawValue();
13547         
13548         if(this.tickable && this.editable){
13549             v = this.tickableInputEl().getValue();
13550         }
13551         
13552         this.doQuery(v);
13553     },
13554
13555     // private
13556     doForce : function(){
13557         if(this.inputEl().dom.value.length > 0){
13558             this.inputEl().dom.value =
13559                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13560              
13561         }
13562     },
13563
13564     /**
13565      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13566      * query allowing the query action to be canceled if needed.
13567      * @param {String} query The SQL query to execute
13568      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13569      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13570      * saved in the current store (defaults to false)
13571      */
13572     doQuery : function(q, forceAll){
13573         
13574         if(q === undefined || q === null){
13575             q = '';
13576         }
13577         var qe = {
13578             query: q,
13579             forceAll: forceAll,
13580             combo: this,
13581             cancel:false
13582         };
13583         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13584             return false;
13585         }
13586         q = qe.query;
13587         
13588         forceAll = qe.forceAll;
13589         if(forceAll === true || (q.length >= this.minChars)){
13590             
13591             this.hasQuery = true;
13592             
13593             if(this.lastQuery != q || this.alwaysQuery){
13594                 this.lastQuery = q;
13595                 if(this.mode == 'local'){
13596                     this.selectedIndex = -1;
13597                     if(forceAll){
13598                         this.store.clearFilter();
13599                     }else{
13600                         
13601                         if(this.specialFilter){
13602                             this.fireEvent('specialfilter', this);
13603                             this.onLoad();
13604                             return;
13605                         }
13606                         
13607                         this.store.filter(this.displayField, q);
13608                     }
13609                     
13610                     this.store.fireEvent("datachanged", this.store);
13611                     
13612                     this.onLoad();
13613                     
13614                     
13615                 }else{
13616                     
13617                     this.store.baseParams[this.queryParam] = q;
13618                     
13619                     var options = {params : this.getParams(q)};
13620                     
13621                     if(this.loadNext){
13622                         options.add = true;
13623                         options.params.start = this.page * this.pageSize;
13624                     }
13625                     
13626                     this.store.load(options);
13627                     
13628                     /*
13629                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13630                      *  we should expand the list on onLoad
13631                      *  so command out it
13632                      */
13633 //                    this.expand();
13634                 }
13635             }else{
13636                 this.selectedIndex = -1;
13637                 this.onLoad();   
13638             }
13639         }
13640         
13641         this.loadNext = false;
13642     },
13643     
13644     // private
13645     getParams : function(q){
13646         var p = {};
13647         //p[this.queryParam] = q;
13648         
13649         if(this.pageSize){
13650             p.start = 0;
13651             p.limit = this.pageSize;
13652         }
13653         return p;
13654     },
13655
13656     /**
13657      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13658      */
13659     collapse : function(){
13660         if(!this.isExpanded()){
13661             return;
13662         }
13663         
13664         this.list.hide();
13665         
13666         this.hasFocus = false;
13667         
13668         if(this.tickable){
13669             this.okBtn.hide();
13670             this.cancelBtn.hide();
13671             this.trigger.show();
13672             
13673             if(this.editable){
13674                 this.tickableInputEl().dom.value = '';
13675                 this.tickableInputEl().blur();
13676             }
13677             
13678         }
13679         
13680         Roo.get(document).un('mousedown', this.collapseIf, this);
13681         Roo.get(document).un('mousewheel', this.collapseIf, this);
13682         if (!this.editable) {
13683             Roo.get(document).un('keydown', this.listKeyPress, this);
13684         }
13685         this.fireEvent('collapse', this);
13686         
13687         this.validate();
13688     },
13689
13690     // private
13691     collapseIf : function(e){
13692         var in_combo  = e.within(this.el);
13693         var in_list =  e.within(this.list);
13694         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13695         
13696         if (in_combo || in_list || is_list) {
13697             //e.stopPropagation();
13698             return;
13699         }
13700         
13701         if(this.tickable){
13702             this.onTickableFooterButtonClick(e, false, false);
13703         }
13704
13705         this.collapse();
13706         
13707     },
13708
13709     /**
13710      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13711      */
13712     expand : function(){
13713        
13714         if(this.isExpanded() || !this.hasFocus){
13715             return;
13716         }
13717         
13718         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13719         this.list.setWidth(lw);
13720         
13721         Roo.log('expand');
13722         
13723         this.list.show();
13724         
13725         this.restrictHeight();
13726         
13727         if(this.tickable){
13728             
13729             this.tickItems = Roo.apply([], this.item);
13730             
13731             this.okBtn.show();
13732             this.cancelBtn.show();
13733             this.trigger.hide();
13734             
13735             if(this.editable){
13736                 this.tickableInputEl().focus();
13737             }
13738             
13739         }
13740         
13741         Roo.get(document).on('mousedown', this.collapseIf, this);
13742         Roo.get(document).on('mousewheel', this.collapseIf, this);
13743         if (!this.editable) {
13744             Roo.get(document).on('keydown', this.listKeyPress, this);
13745         }
13746         
13747         this.fireEvent('expand', this);
13748     },
13749
13750     // private
13751     // Implements the default empty TriggerField.onTriggerClick function
13752     onTriggerClick : function(e)
13753     {
13754         Roo.log('trigger click');
13755         
13756         if(this.disabled || !this.triggerList){
13757             return;
13758         }
13759         
13760         this.page = 0;
13761         this.loadNext = false;
13762         
13763         if(this.isExpanded()){
13764             this.collapse();
13765             if (!this.blockFocus) {
13766                 this.inputEl().focus();
13767             }
13768             
13769         }else {
13770             this.hasFocus = true;
13771             if(this.triggerAction == 'all') {
13772                 this.doQuery(this.allQuery, true);
13773             } else {
13774                 this.doQuery(this.getRawValue());
13775             }
13776             if (!this.blockFocus) {
13777                 this.inputEl().focus();
13778             }
13779         }
13780     },
13781     
13782     onTickableTriggerClick : function(e)
13783     {
13784         if(this.disabled){
13785             return;
13786         }
13787         
13788         this.page = 0;
13789         this.loadNext = false;
13790         this.hasFocus = true;
13791         
13792         if(this.triggerAction == 'all') {
13793             this.doQuery(this.allQuery, true);
13794         } else {
13795             this.doQuery(this.getRawValue());
13796         }
13797     },
13798     
13799     onSearchFieldClick : function(e)
13800     {
13801         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13802             this.onTickableFooterButtonClick(e, false, false);
13803             return;
13804         }
13805         
13806         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13807             return;
13808         }
13809         
13810         this.page = 0;
13811         this.loadNext = false;
13812         this.hasFocus = true;
13813         
13814         if(this.triggerAction == 'all') {
13815             this.doQuery(this.allQuery, true);
13816         } else {
13817             this.doQuery(this.getRawValue());
13818         }
13819     },
13820     
13821     listKeyPress : function(e)
13822     {
13823         //Roo.log('listkeypress');
13824         // scroll to first matching element based on key pres..
13825         if (e.isSpecialKey()) {
13826             return false;
13827         }
13828         var k = String.fromCharCode(e.getKey()).toUpperCase();
13829         //Roo.log(k);
13830         var match  = false;
13831         var csel = this.view.getSelectedNodes();
13832         var cselitem = false;
13833         if (csel.length) {
13834             var ix = this.view.indexOf(csel[0]);
13835             cselitem  = this.store.getAt(ix);
13836             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13837                 cselitem = false;
13838             }
13839             
13840         }
13841         
13842         this.store.each(function(v) { 
13843             if (cselitem) {
13844                 // start at existing selection.
13845                 if (cselitem.id == v.id) {
13846                     cselitem = false;
13847                 }
13848                 return true;
13849             }
13850                 
13851             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13852                 match = this.store.indexOf(v);
13853                 return false;
13854             }
13855             return true;
13856         }, this);
13857         
13858         if (match === false) {
13859             return true; // no more action?
13860         }
13861         // scroll to?
13862         this.view.select(match);
13863         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13864         sn.scrollIntoView(sn.dom.parentNode, false);
13865     },
13866     
13867     onViewScroll : function(e, t){
13868         
13869         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){
13870             return;
13871         }
13872         
13873         this.hasQuery = true;
13874         
13875         this.loading = this.list.select('.loading', true).first();
13876         
13877         if(this.loading === null){
13878             this.list.createChild({
13879                 tag: 'div',
13880                 cls: 'loading roo-select2-more-results roo-select2-active',
13881                 html: 'Loading more results...'
13882             });
13883             
13884             this.loading = this.list.select('.loading', true).first();
13885             
13886             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13887             
13888             this.loading.hide();
13889         }
13890         
13891         this.loading.show();
13892         
13893         var _combo = this;
13894         
13895         this.page++;
13896         this.loadNext = true;
13897         
13898         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13899         
13900         return;
13901     },
13902     
13903     addItem : function(o)
13904     {   
13905         var dv = ''; // display value
13906         
13907         if (this.displayField) {
13908             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13909         } else {
13910             // this is an error condition!!!
13911             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13912         }
13913         
13914         if(!dv.length){
13915             return;
13916         }
13917         
13918         var choice = this.choices.createChild({
13919             tag: 'li',
13920             cls: 'roo-select2-search-choice',
13921             cn: [
13922                 {
13923                     tag: 'div',
13924                     html: dv
13925                 },
13926                 {
13927                     tag: 'a',
13928                     href: '#',
13929                     cls: 'roo-select2-search-choice-close',
13930                     tabindex: '-1'
13931                 }
13932             ]
13933             
13934         }, this.searchField);
13935         
13936         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13937         
13938         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13939         
13940         this.item.push(o);
13941         
13942         this.lastData = o;
13943         
13944         this.syncValue();
13945         
13946         this.inputEl().dom.value = '';
13947         
13948         this.validate();
13949     },
13950     
13951     onRemoveItem : function(e, _self, o)
13952     {
13953         e.preventDefault();
13954         
13955         this.lastItem = Roo.apply([], this.item);
13956         
13957         var index = this.item.indexOf(o.data) * 1;
13958         
13959         if( index < 0){
13960             Roo.log('not this item?!');
13961             return;
13962         }
13963         
13964         this.item.splice(index, 1);
13965         o.item.remove();
13966         
13967         this.syncValue();
13968         
13969         this.fireEvent('remove', this, e);
13970         
13971         this.validate();
13972         
13973     },
13974     
13975     syncValue : function()
13976     {
13977         if(!this.item.length){
13978             this.clearValue();
13979             return;
13980         }
13981             
13982         var value = [];
13983         var _this = this;
13984         Roo.each(this.item, function(i){
13985             if(_this.valueField){
13986                 value.push(i[_this.valueField]);
13987                 return;
13988             }
13989
13990             value.push(i);
13991         });
13992
13993         this.value = value.join(',');
13994
13995         if(this.hiddenField){
13996             this.hiddenField.dom.value = this.value;
13997         }
13998         
13999         this.store.fireEvent("datachanged", this.store);
14000         
14001         this.validate();
14002     },
14003     
14004     clearItem : function()
14005     {
14006         if(!this.multiple){
14007             return;
14008         }
14009         
14010         this.item = [];
14011         
14012         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14013            c.remove();
14014         });
14015         
14016         this.syncValue();
14017         
14018         this.validate();
14019         
14020         if(this.tickable && !Roo.isTouch){
14021             this.view.refresh();
14022         }
14023     },
14024     
14025     inputEl: function ()
14026     {
14027         if(Roo.isIOS && this.useNativeIOS){
14028             return this.el.select('select.roo-ios-select', true).first();
14029         }
14030         
14031         if(Roo.isTouch && this.mobileTouchView){
14032             return this.el.select('input.form-control',true).first();
14033         }
14034         
14035         if(this.tickable){
14036             return this.searchField;
14037         }
14038         
14039         return this.el.select('input.form-control',true).first();
14040     },
14041     
14042     onTickableFooterButtonClick : function(e, btn, el)
14043     {
14044         e.preventDefault();
14045         
14046         this.lastItem = Roo.apply([], this.item);
14047         
14048         if(btn && btn.name == 'cancel'){
14049             this.tickItems = Roo.apply([], this.item);
14050             this.collapse();
14051             return;
14052         }
14053         
14054         this.clearItem();
14055         
14056         var _this = this;
14057         
14058         Roo.each(this.tickItems, function(o){
14059             _this.addItem(o);
14060         });
14061         
14062         this.collapse();
14063         
14064     },
14065     
14066     validate : function()
14067     {
14068         var v = this.getRawValue();
14069         
14070         if(this.multiple){
14071             v = this.getValue();
14072         }
14073         
14074         if(this.disabled || this.allowBlank || v.length){
14075             this.markValid();
14076             return true;
14077         }
14078         
14079         this.markInvalid();
14080         return false;
14081     },
14082     
14083     tickableInputEl : function()
14084     {
14085         if(!this.tickable || !this.editable){
14086             return this.inputEl();
14087         }
14088         
14089         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14090     },
14091     
14092     
14093     getAutoCreateTouchView : function()
14094     {
14095         var id = Roo.id();
14096         
14097         var cfg = {
14098             cls: 'form-group' //input-group
14099         };
14100         
14101         var input =  {
14102             tag: 'input',
14103             id : id,
14104             type : this.inputType,
14105             cls : 'form-control x-combo-noedit',
14106             autocomplete: 'new-password',
14107             placeholder : this.placeholder || '',
14108             readonly : true
14109         };
14110         
14111         if (this.name) {
14112             input.name = this.name;
14113         }
14114         
14115         if (this.size) {
14116             input.cls += ' input-' + this.size;
14117         }
14118         
14119         if (this.disabled) {
14120             input.disabled = true;
14121         }
14122         
14123         var inputblock = {
14124             cls : '',
14125             cn : [
14126                 input
14127             ]
14128         };
14129         
14130         if(this.before){
14131             inputblock.cls += ' input-group';
14132             
14133             inputblock.cn.unshift({
14134                 tag :'span',
14135                 cls : 'input-group-addon',
14136                 html : this.before
14137             });
14138         }
14139         
14140         if(this.removable && !this.multiple){
14141             inputblock.cls += ' roo-removable';
14142             
14143             inputblock.cn.push({
14144                 tag: 'button',
14145                 html : 'x',
14146                 cls : 'roo-combo-removable-btn close'
14147             });
14148         }
14149
14150         if(this.hasFeedback && !this.allowBlank){
14151             
14152             inputblock.cls += ' has-feedback';
14153             
14154             inputblock.cn.push({
14155                 tag: 'span',
14156                 cls: 'glyphicon form-control-feedback'
14157             });
14158             
14159         }
14160         
14161         if (this.after) {
14162             
14163             inputblock.cls += (this.before) ? '' : ' input-group';
14164             
14165             inputblock.cn.push({
14166                 tag :'span',
14167                 cls : 'input-group-addon',
14168                 html : this.after
14169             });
14170         }
14171
14172         var box = {
14173             tag: 'div',
14174             cn: [
14175                 {
14176                     tag: 'input',
14177                     type : 'hidden',
14178                     cls: 'form-hidden-field'
14179                 },
14180                 inputblock
14181             ]
14182             
14183         };
14184         
14185         if(this.multiple){
14186             box = {
14187                 tag: 'div',
14188                 cn: [
14189                     {
14190                         tag: 'input',
14191                         type : 'hidden',
14192                         cls: 'form-hidden-field'
14193                     },
14194                     {
14195                         tag: 'ul',
14196                         cls: 'roo-select2-choices',
14197                         cn:[
14198                             {
14199                                 tag: 'li',
14200                                 cls: 'roo-select2-search-field',
14201                                 cn: [
14202
14203                                     inputblock
14204                                 ]
14205                             }
14206                         ]
14207                     }
14208                 ]
14209             }
14210         };
14211         
14212         var combobox = {
14213             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14214             cn: [
14215                 box
14216             ]
14217         };
14218         
14219         if(!this.multiple && this.showToggleBtn){
14220             
14221             var caret = {
14222                         tag: 'span',
14223                         cls: 'caret'
14224             };
14225             
14226             if (this.caret != false) {
14227                 caret = {
14228                      tag: 'i',
14229                      cls: 'fa fa-' + this.caret
14230                 };
14231                 
14232             }
14233             
14234             combobox.cn.push({
14235                 tag :'span',
14236                 cls : 'input-group-addon btn dropdown-toggle',
14237                 cn : [
14238                     caret,
14239                     {
14240                         tag: 'span',
14241                         cls: 'combobox-clear',
14242                         cn  : [
14243                             {
14244                                 tag : 'i',
14245                                 cls: 'icon-remove'
14246                             }
14247                         ]
14248                     }
14249                 ]
14250
14251             })
14252         }
14253         
14254         if(this.multiple){
14255             combobox.cls += ' roo-select2-container-multi';
14256         }
14257         
14258         var align = this.labelAlign || this.parentLabelAlign();
14259         
14260         cfg.cn = combobox;
14261         
14262         if(this.fieldLabel.length && this.labelWidth){
14263             
14264             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14265             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14266             
14267             cfg.cn = [
14268                 {
14269                    tag : 'i',
14270                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14271                    tooltip : 'This field is required'
14272                 },
14273                 {
14274                     tag: 'label',
14275                     cls : 'control-label ' + lw,
14276                     html : this.fieldLabel
14277
14278                 },
14279                 {
14280                     cls : cw, 
14281                     cn: [
14282                         combobox
14283                     ]
14284                 }
14285             ];
14286             
14287             if(this.indicatorpos == 'right'){
14288                 cfg.cn = [
14289                     {
14290                         tag: 'label',
14291                         cls : 'control-label ' + lw,
14292                         html : this.fieldLabel
14293
14294                     },
14295                     {
14296                        tag : 'i',
14297                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14298                        tooltip : 'This field is required'
14299                     },
14300                     {
14301                         cls : cw, 
14302                         cn: [
14303                             combobox
14304                         ]
14305                     }
14306                 ];
14307             }
14308         }
14309         
14310         var settings = this;
14311         
14312         ['xs','sm','md','lg'].map(function(size){
14313             if (settings[size]) {
14314                 cfg.cls += ' col-' + size + '-' + settings[size];
14315             }
14316         });
14317         
14318         return cfg;
14319     },
14320     
14321     initTouchView : function()
14322     {
14323         this.renderTouchView();
14324         
14325         this.touchViewEl.on('scroll', function(){
14326             this.el.dom.scrollTop = 0;
14327         }, this);
14328         
14329         this.originalValue = this.getValue();
14330         
14331         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14332         
14333         this.inputEl().on("click", this.showTouchView, this);
14334         if (this.triggerEl) {
14335             this.triggerEl.on("click", this.showTouchView, this);
14336         }
14337         
14338         
14339         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14340         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14341         
14342         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14343         
14344         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14345         this.store.on('load', this.onTouchViewLoad, this);
14346         this.store.on('loadexception', this.onTouchViewLoadException, this);
14347         
14348         if(this.hiddenName){
14349             
14350             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14351             
14352             this.hiddenField.dom.value =
14353                 this.hiddenValue !== undefined ? this.hiddenValue :
14354                 this.value !== undefined ? this.value : '';
14355         
14356             this.el.dom.removeAttribute('name');
14357             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14358         }
14359         
14360         if(this.multiple){
14361             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14362             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14363         }
14364         
14365         if(this.removable && !this.multiple){
14366             var close = this.closeTriggerEl();
14367             if(close){
14368                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14369                 close.on('click', this.removeBtnClick, this, close);
14370             }
14371         }
14372         /*
14373          * fix the bug in Safari iOS8
14374          */
14375         this.inputEl().on("focus", function(e){
14376             document.activeElement.blur();
14377         }, this);
14378         
14379         return;
14380         
14381         
14382     },
14383     
14384     renderTouchView : function()
14385     {
14386         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14387         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14388         
14389         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14390         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14391         
14392         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14393         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14394         this.touchViewBodyEl.setStyle('overflow', 'auto');
14395         
14396         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14397         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14398         
14399         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14400         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14401         
14402     },
14403     
14404     showTouchView : function()
14405     {
14406         if(this.disabled){
14407             return;
14408         }
14409         
14410         this.touchViewHeaderEl.hide();
14411
14412         if(this.modalTitle.length){
14413             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14414             this.touchViewHeaderEl.show();
14415         }
14416
14417         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14418         this.touchViewEl.show();
14419
14420         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14421         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14422                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14423
14424         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14425
14426         if(this.modalTitle.length){
14427             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14428         }
14429         
14430         this.touchViewBodyEl.setHeight(bodyHeight);
14431
14432         if(this.animate){
14433             var _this = this;
14434             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14435         }else{
14436             this.touchViewEl.addClass('in');
14437         }
14438
14439         this.doTouchViewQuery();
14440         
14441     },
14442     
14443     hideTouchView : function()
14444     {
14445         this.touchViewEl.removeClass('in');
14446
14447         if(this.animate){
14448             var _this = this;
14449             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14450         }else{
14451             this.touchViewEl.setStyle('display', 'none');
14452         }
14453         
14454     },
14455     
14456     setTouchViewValue : function()
14457     {
14458         if(this.multiple){
14459             this.clearItem();
14460         
14461             var _this = this;
14462
14463             Roo.each(this.tickItems, function(o){
14464                 this.addItem(o);
14465             }, this);
14466         }
14467         
14468         this.hideTouchView();
14469     },
14470     
14471     doTouchViewQuery : function()
14472     {
14473         var qe = {
14474             query: '',
14475             forceAll: true,
14476             combo: this,
14477             cancel:false
14478         };
14479         
14480         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14481             return false;
14482         }
14483         
14484         if(!this.alwaysQuery || this.mode == 'local'){
14485             this.onTouchViewLoad();
14486             return;
14487         }
14488         
14489         this.store.load();
14490     },
14491     
14492     onTouchViewBeforeLoad : function(combo,opts)
14493     {
14494         return;
14495     },
14496
14497     // private
14498     onTouchViewLoad : function()
14499     {
14500         if(this.store.getCount() < 1){
14501             this.onTouchViewEmptyResults();
14502             return;
14503         }
14504         
14505         this.clearTouchView();
14506         
14507         var rawValue = this.getRawValue();
14508         
14509         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14510         
14511         this.tickItems = [];
14512         
14513         this.store.data.each(function(d, rowIndex){
14514             var row = this.touchViewListGroup.createChild(template);
14515             
14516             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14517                 row.addClass(d.data.cls);
14518             }
14519             
14520             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14521                 var cfg = {
14522                     data : d.data,
14523                     html : d.data[this.displayField]
14524                 };
14525                 
14526                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14527                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14528                 }
14529             }
14530             row.removeClass('selected');
14531             if(!this.multiple && this.valueField &&
14532                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14533             {
14534                 // radio buttons..
14535                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14536                 row.addClass('selected');
14537             }
14538             
14539             if(this.multiple && this.valueField &&
14540                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14541             {
14542                 
14543                 // checkboxes...
14544                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14545                 this.tickItems.push(d.data);
14546             }
14547             
14548             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14549             
14550         }, this);
14551         
14552         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14553         
14554         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14555
14556         if(this.modalTitle.length){
14557             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14558         }
14559
14560         var listHeight = this.touchViewListGroup.getHeight();
14561         
14562         var _this = this;
14563         
14564         if(firstChecked && listHeight > bodyHeight){
14565             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14566         }
14567         
14568     },
14569     
14570     onTouchViewLoadException : function()
14571     {
14572         this.hideTouchView();
14573     },
14574     
14575     onTouchViewEmptyResults : function()
14576     {
14577         this.clearTouchView();
14578         
14579         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14580         
14581         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14582         
14583     },
14584     
14585     clearTouchView : function()
14586     {
14587         this.touchViewListGroup.dom.innerHTML = '';
14588     },
14589     
14590     onTouchViewClick : function(e, el, o)
14591     {
14592         e.preventDefault();
14593         
14594         var row = o.row;
14595         var rowIndex = o.rowIndex;
14596         
14597         var r = this.store.getAt(rowIndex);
14598         
14599         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14600             
14601             if(!this.multiple){
14602                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14603                     c.dom.removeAttribute('checked');
14604                 }, this);
14605
14606                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14607
14608                 this.setFromData(r.data);
14609
14610                 var close = this.closeTriggerEl();
14611
14612                 if(close){
14613                     close.show();
14614                 }
14615
14616                 this.hideTouchView();
14617
14618                 this.fireEvent('select', this, r, rowIndex);
14619
14620                 return;
14621             }
14622
14623             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14624                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14625                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14626                 return;
14627             }
14628
14629             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14630             this.addItem(r.data);
14631             this.tickItems.push(r.data);
14632         }
14633     },
14634     
14635     getAutoCreateNativeIOS : function()
14636     {
14637         var cfg = {
14638             cls: 'form-group' //input-group,
14639         };
14640         
14641         var combobox =  {
14642             tag: 'select',
14643             cls : 'roo-ios-select'
14644         };
14645         
14646         if (this.name) {
14647             combobox.name = this.name;
14648         }
14649         
14650         if (this.disabled) {
14651             combobox.disabled = true;
14652         }
14653         
14654         var settings = this;
14655         
14656         ['xs','sm','md','lg'].map(function(size){
14657             if (settings[size]) {
14658                 cfg.cls += ' col-' + size + '-' + settings[size];
14659             }
14660         });
14661         
14662         cfg.cn = combobox;
14663         
14664         return cfg;
14665         
14666     },
14667     
14668     initIOSView : function()
14669     {
14670         this.store.on('load', this.onIOSViewLoad, this);
14671         
14672         return;
14673     },
14674     
14675     onIOSViewLoad : function()
14676     {
14677         if(this.store.getCount() < 1){
14678             return;
14679         }
14680         
14681         this.clearIOSView();
14682         
14683         if(this.allowBlank) {
14684             
14685             var default_text = '-- SELECT --';
14686             
14687             var opt = this.inputEl().createChild({
14688                 tag: 'option',
14689                 value : 0,
14690                 html : default_text
14691             });
14692             
14693             var o = {};
14694             o[this.valueField] = 0;
14695             o[this.displayField] = default_text;
14696             
14697             this.ios_options.push({
14698                 data : o,
14699                 el : opt
14700             });
14701             
14702         }
14703         
14704         this.store.data.each(function(d, rowIndex){
14705             
14706             var html = '';
14707             
14708             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14709                 html = d.data[this.displayField];
14710             }
14711             
14712             var value = '';
14713             
14714             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14715                 value = d.data[this.valueField];
14716             }
14717             
14718             var option = {
14719                 tag: 'option',
14720                 value : value,
14721                 html : html
14722             };
14723             
14724             if(this.value == d.data[this.valueField]){
14725                 option['selected'] = true;
14726             }
14727             
14728             var opt = this.inputEl().createChild(option);
14729             
14730             this.ios_options.push({
14731                 data : d.data,
14732                 el : opt
14733             });
14734             
14735         }, this);
14736         
14737         this.inputEl().on('change', function(){
14738            this.fireEvent('select', this);
14739         }, this);
14740         
14741     },
14742     
14743     clearIOSView: function()
14744     {
14745         this.inputEl().dom.innerHTML = '';
14746         
14747         this.ios_options = [];
14748     },
14749     
14750     setIOSValue: function(v)
14751     {
14752         this.value = v;
14753         
14754         if(!this.ios_options){
14755             return;
14756         }
14757         
14758         Roo.each(this.ios_options, function(opts){
14759            
14760            opts.el.dom.removeAttribute('selected');
14761            
14762            if(opts.data[this.valueField] != v){
14763                return;
14764            }
14765            
14766            opts.el.dom.setAttribute('selected', true);
14767            
14768         }, this);
14769     }
14770
14771     /** 
14772     * @cfg {Boolean} grow 
14773     * @hide 
14774     */
14775     /** 
14776     * @cfg {Number} growMin 
14777     * @hide 
14778     */
14779     /** 
14780     * @cfg {Number} growMax 
14781     * @hide 
14782     */
14783     /**
14784      * @hide
14785      * @method autoSize
14786      */
14787 });
14788
14789 Roo.apply(Roo.bootstrap.ComboBox,  {
14790     
14791     header : {
14792         tag: 'div',
14793         cls: 'modal-header',
14794         cn: [
14795             {
14796                 tag: 'h4',
14797                 cls: 'modal-title'
14798             }
14799         ]
14800     },
14801     
14802     body : {
14803         tag: 'div',
14804         cls: 'modal-body',
14805         cn: [
14806             {
14807                 tag: 'ul',
14808                 cls: 'list-group'
14809             }
14810         ]
14811     },
14812     
14813     listItemRadio : {
14814         tag: 'li',
14815         cls: 'list-group-item',
14816         cn: [
14817             {
14818                 tag: 'span',
14819                 cls: 'roo-combobox-list-group-item-value'
14820             },
14821             {
14822                 tag: 'div',
14823                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14824                 cn: [
14825                     {
14826                         tag: 'input',
14827                         type: 'radio'
14828                     },
14829                     {
14830                         tag: 'label'
14831                     }
14832                 ]
14833             }
14834         ]
14835     },
14836     
14837     listItemCheckbox : {
14838         tag: 'li',
14839         cls: 'list-group-item',
14840         cn: [
14841             {
14842                 tag: 'span',
14843                 cls: 'roo-combobox-list-group-item-value'
14844             },
14845             {
14846                 tag: 'div',
14847                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14848                 cn: [
14849                     {
14850                         tag: 'input',
14851                         type: 'checkbox'
14852                     },
14853                     {
14854                         tag: 'label'
14855                     }
14856                 ]
14857             }
14858         ]
14859     },
14860     
14861     emptyResult : {
14862         tag: 'div',
14863         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14864     },
14865     
14866     footer : {
14867         tag: 'div',
14868         cls: 'modal-footer',
14869         cn: [
14870             {
14871                 tag: 'div',
14872                 cls: 'row',
14873                 cn: [
14874                     {
14875                         tag: 'div',
14876                         cls: 'col-xs-6 text-left',
14877                         cn: {
14878                             tag: 'button',
14879                             cls: 'btn btn-danger roo-touch-view-cancel',
14880                             html: 'Cancel'
14881                         }
14882                     },
14883                     {
14884                         tag: 'div',
14885                         cls: 'col-xs-6 text-right',
14886                         cn: {
14887                             tag: 'button',
14888                             cls: 'btn btn-success roo-touch-view-ok',
14889                             html: 'OK'
14890                         }
14891                     }
14892                 ]
14893             }
14894         ]
14895         
14896     }
14897 });
14898
14899 Roo.apply(Roo.bootstrap.ComboBox,  {
14900     
14901     touchViewTemplate : {
14902         tag: 'div',
14903         cls: 'modal fade roo-combobox-touch-view',
14904         cn: [
14905             {
14906                 tag: 'div',
14907                 cls: 'modal-dialog',
14908                 style : 'position:fixed', // we have to fix position....
14909                 cn: [
14910                     {
14911                         tag: 'div',
14912                         cls: 'modal-content',
14913                         cn: [
14914                             Roo.bootstrap.ComboBox.header,
14915                             Roo.bootstrap.ComboBox.body,
14916                             Roo.bootstrap.ComboBox.footer
14917                         ]
14918                     }
14919                 ]
14920             }
14921         ]
14922     }
14923 });/*
14924  * Based on:
14925  * Ext JS Library 1.1.1
14926  * Copyright(c) 2006-2007, Ext JS, LLC.
14927  *
14928  * Originally Released Under LGPL - original licence link has changed is not relivant.
14929  *
14930  * Fork - LGPL
14931  * <script type="text/javascript">
14932  */
14933
14934 /**
14935  * @class Roo.View
14936  * @extends Roo.util.Observable
14937  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14938  * This class also supports single and multi selection modes. <br>
14939  * Create a data model bound view:
14940  <pre><code>
14941  var store = new Roo.data.Store(...);
14942
14943  var view = new Roo.View({
14944     el : "my-element",
14945     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14946  
14947     singleSelect: true,
14948     selectedClass: "ydataview-selected",
14949     store: store
14950  });
14951
14952  // listen for node click?
14953  view.on("click", function(vw, index, node, e){
14954  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14955  });
14956
14957  // load XML data
14958  dataModel.load("foobar.xml");
14959  </code></pre>
14960  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14961  * <br><br>
14962  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14963  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14964  * 
14965  * Note: old style constructor is still suported (container, template, config)
14966  * 
14967  * @constructor
14968  * Create a new View
14969  * @param {Object} config The config object
14970  * 
14971  */
14972 Roo.View = function(config, depreciated_tpl, depreciated_config){
14973     
14974     this.parent = false;
14975     
14976     if (typeof(depreciated_tpl) == 'undefined') {
14977         // new way.. - universal constructor.
14978         Roo.apply(this, config);
14979         this.el  = Roo.get(this.el);
14980     } else {
14981         // old format..
14982         this.el  = Roo.get(config);
14983         this.tpl = depreciated_tpl;
14984         Roo.apply(this, depreciated_config);
14985     }
14986     this.wrapEl  = this.el.wrap().wrap();
14987     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14988     
14989     
14990     if(typeof(this.tpl) == "string"){
14991         this.tpl = new Roo.Template(this.tpl);
14992     } else {
14993         // support xtype ctors..
14994         this.tpl = new Roo.factory(this.tpl, Roo);
14995     }
14996     
14997     
14998     this.tpl.compile();
14999     
15000     /** @private */
15001     this.addEvents({
15002         /**
15003          * @event beforeclick
15004          * Fires before a click is processed. Returns false to cancel the default action.
15005          * @param {Roo.View} this
15006          * @param {Number} index The index of the target node
15007          * @param {HTMLElement} node The target node
15008          * @param {Roo.EventObject} e The raw event object
15009          */
15010             "beforeclick" : true,
15011         /**
15012          * @event click
15013          * Fires when a template node is clicked.
15014          * @param {Roo.View} this
15015          * @param {Number} index The index of the target node
15016          * @param {HTMLElement} node The target node
15017          * @param {Roo.EventObject} e The raw event object
15018          */
15019             "click" : true,
15020         /**
15021          * @event dblclick
15022          * Fires when a template node is double clicked.
15023          * @param {Roo.View} this
15024          * @param {Number} index The index of the target node
15025          * @param {HTMLElement} node The target node
15026          * @param {Roo.EventObject} e The raw event object
15027          */
15028             "dblclick" : true,
15029         /**
15030          * @event contextmenu
15031          * Fires when a template node is right clicked.
15032          * @param {Roo.View} this
15033          * @param {Number} index The index of the target node
15034          * @param {HTMLElement} node The target node
15035          * @param {Roo.EventObject} e The raw event object
15036          */
15037             "contextmenu" : true,
15038         /**
15039          * @event selectionchange
15040          * Fires when the selected nodes change.
15041          * @param {Roo.View} this
15042          * @param {Array} selections Array of the selected nodes
15043          */
15044             "selectionchange" : true,
15045     
15046         /**
15047          * @event beforeselect
15048          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15049          * @param {Roo.View} this
15050          * @param {HTMLElement} node The node to be selected
15051          * @param {Array} selections Array of currently selected nodes
15052          */
15053             "beforeselect" : true,
15054         /**
15055          * @event preparedata
15056          * Fires on every row to render, to allow you to change the data.
15057          * @param {Roo.View} this
15058          * @param {Object} data to be rendered (change this)
15059          */
15060           "preparedata" : true
15061           
15062           
15063         });
15064
15065
15066
15067     this.el.on({
15068         "click": this.onClick,
15069         "dblclick": this.onDblClick,
15070         "contextmenu": this.onContextMenu,
15071         scope:this
15072     });
15073
15074     this.selections = [];
15075     this.nodes = [];
15076     this.cmp = new Roo.CompositeElementLite([]);
15077     if(this.store){
15078         this.store = Roo.factory(this.store, Roo.data);
15079         this.setStore(this.store, true);
15080     }
15081     
15082     if ( this.footer && this.footer.xtype) {
15083            
15084          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15085         
15086         this.footer.dataSource = this.store;
15087         this.footer.container = fctr;
15088         this.footer = Roo.factory(this.footer, Roo);
15089         fctr.insertFirst(this.el);
15090         
15091         // this is a bit insane - as the paging toolbar seems to detach the el..
15092 //        dom.parentNode.parentNode.parentNode
15093          // they get detached?
15094     }
15095     
15096     
15097     Roo.View.superclass.constructor.call(this);
15098     
15099     
15100 };
15101
15102 Roo.extend(Roo.View, Roo.util.Observable, {
15103     
15104      /**
15105      * @cfg {Roo.data.Store} store Data store to load data from.
15106      */
15107     store : false,
15108     
15109     /**
15110      * @cfg {String|Roo.Element} el The container element.
15111      */
15112     el : '',
15113     
15114     /**
15115      * @cfg {String|Roo.Template} tpl The template used by this View 
15116      */
15117     tpl : false,
15118     /**
15119      * @cfg {String} dataName the named area of the template to use as the data area
15120      *                          Works with domtemplates roo-name="name"
15121      */
15122     dataName: false,
15123     /**
15124      * @cfg {String} selectedClass The css class to add to selected nodes
15125      */
15126     selectedClass : "x-view-selected",
15127      /**
15128      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15129      */
15130     emptyText : "",
15131     
15132     /**
15133      * @cfg {String} text to display on mask (default Loading)
15134      */
15135     mask : false,
15136     /**
15137      * @cfg {Boolean} multiSelect Allow multiple selection
15138      */
15139     multiSelect : false,
15140     /**
15141      * @cfg {Boolean} singleSelect Allow single selection
15142      */
15143     singleSelect:  false,
15144     
15145     /**
15146      * @cfg {Boolean} toggleSelect - selecting 
15147      */
15148     toggleSelect : false,
15149     
15150     /**
15151      * @cfg {Boolean} tickable - selecting 
15152      */
15153     tickable : false,
15154     
15155     /**
15156      * Returns the element this view is bound to.
15157      * @return {Roo.Element}
15158      */
15159     getEl : function(){
15160         return this.wrapEl;
15161     },
15162     
15163     
15164
15165     /**
15166      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15167      */
15168     refresh : function(){
15169         //Roo.log('refresh');
15170         var t = this.tpl;
15171         
15172         // if we are using something like 'domtemplate', then
15173         // the what gets used is:
15174         // t.applySubtemplate(NAME, data, wrapping data..)
15175         // the outer template then get' applied with
15176         //     the store 'extra data'
15177         // and the body get's added to the
15178         //      roo-name="data" node?
15179         //      <span class='roo-tpl-{name}'></span> ?????
15180         
15181         
15182         
15183         this.clearSelections();
15184         this.el.update("");
15185         var html = [];
15186         var records = this.store.getRange();
15187         if(records.length < 1) {
15188             
15189             // is this valid??  = should it render a template??
15190             
15191             this.el.update(this.emptyText);
15192             return;
15193         }
15194         var el = this.el;
15195         if (this.dataName) {
15196             this.el.update(t.apply(this.store.meta)); //????
15197             el = this.el.child('.roo-tpl-' + this.dataName);
15198         }
15199         
15200         for(var i = 0, len = records.length; i < len; i++){
15201             var data = this.prepareData(records[i].data, i, records[i]);
15202             this.fireEvent("preparedata", this, data, i, records[i]);
15203             
15204             var d = Roo.apply({}, data);
15205             
15206             if(this.tickable){
15207                 Roo.apply(d, {'roo-id' : Roo.id()});
15208                 
15209                 var _this = this;
15210             
15211                 Roo.each(this.parent.item, function(item){
15212                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15213                         return;
15214                     }
15215                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15216                 });
15217             }
15218             
15219             html[html.length] = Roo.util.Format.trim(
15220                 this.dataName ?
15221                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15222                     t.apply(d)
15223             );
15224         }
15225         
15226         
15227         
15228         el.update(html.join(""));
15229         this.nodes = el.dom.childNodes;
15230         this.updateIndexes(0);
15231     },
15232     
15233
15234     /**
15235      * Function to override to reformat the data that is sent to
15236      * the template for each node.
15237      * DEPRICATED - use the preparedata event handler.
15238      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15239      * a JSON object for an UpdateManager bound view).
15240      */
15241     prepareData : function(data, index, record)
15242     {
15243         this.fireEvent("preparedata", this, data, index, record);
15244         return data;
15245     },
15246
15247     onUpdate : function(ds, record){
15248         // Roo.log('on update');   
15249         this.clearSelections();
15250         var index = this.store.indexOf(record);
15251         var n = this.nodes[index];
15252         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15253         n.parentNode.removeChild(n);
15254         this.updateIndexes(index, index);
15255     },
15256
15257     
15258     
15259 // --------- FIXME     
15260     onAdd : function(ds, records, index)
15261     {
15262         //Roo.log(['on Add', ds, records, index] );        
15263         this.clearSelections();
15264         if(this.nodes.length == 0){
15265             this.refresh();
15266             return;
15267         }
15268         var n = this.nodes[index];
15269         for(var i = 0, len = records.length; i < len; i++){
15270             var d = this.prepareData(records[i].data, i, records[i]);
15271             if(n){
15272                 this.tpl.insertBefore(n, d);
15273             }else{
15274                 
15275                 this.tpl.append(this.el, d);
15276             }
15277         }
15278         this.updateIndexes(index);
15279     },
15280
15281     onRemove : function(ds, record, index){
15282        // Roo.log('onRemove');
15283         this.clearSelections();
15284         var el = this.dataName  ?
15285             this.el.child('.roo-tpl-' + this.dataName) :
15286             this.el; 
15287         
15288         el.dom.removeChild(this.nodes[index]);
15289         this.updateIndexes(index);
15290     },
15291
15292     /**
15293      * Refresh an individual node.
15294      * @param {Number} index
15295      */
15296     refreshNode : function(index){
15297         this.onUpdate(this.store, this.store.getAt(index));
15298     },
15299
15300     updateIndexes : function(startIndex, endIndex){
15301         var ns = this.nodes;
15302         startIndex = startIndex || 0;
15303         endIndex = endIndex || ns.length - 1;
15304         for(var i = startIndex; i <= endIndex; i++){
15305             ns[i].nodeIndex = i;
15306         }
15307     },
15308
15309     /**
15310      * Changes the data store this view uses and refresh the view.
15311      * @param {Store} store
15312      */
15313     setStore : function(store, initial){
15314         if(!initial && this.store){
15315             this.store.un("datachanged", this.refresh);
15316             this.store.un("add", this.onAdd);
15317             this.store.un("remove", this.onRemove);
15318             this.store.un("update", this.onUpdate);
15319             this.store.un("clear", this.refresh);
15320             this.store.un("beforeload", this.onBeforeLoad);
15321             this.store.un("load", this.onLoad);
15322             this.store.un("loadexception", this.onLoad);
15323         }
15324         if(store){
15325           
15326             store.on("datachanged", this.refresh, this);
15327             store.on("add", this.onAdd, this);
15328             store.on("remove", this.onRemove, this);
15329             store.on("update", this.onUpdate, this);
15330             store.on("clear", this.refresh, this);
15331             store.on("beforeload", this.onBeforeLoad, this);
15332             store.on("load", this.onLoad, this);
15333             store.on("loadexception", this.onLoad, this);
15334         }
15335         
15336         if(store){
15337             this.refresh();
15338         }
15339     },
15340     /**
15341      * onbeforeLoad - masks the loading area.
15342      *
15343      */
15344     onBeforeLoad : function(store,opts)
15345     {
15346          //Roo.log('onBeforeLoad');   
15347         if (!opts.add) {
15348             this.el.update("");
15349         }
15350         this.el.mask(this.mask ? this.mask : "Loading" ); 
15351     },
15352     onLoad : function ()
15353     {
15354         this.el.unmask();
15355     },
15356     
15357
15358     /**
15359      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15360      * @param {HTMLElement} node
15361      * @return {HTMLElement} The template node
15362      */
15363     findItemFromChild : function(node){
15364         var el = this.dataName  ?
15365             this.el.child('.roo-tpl-' + this.dataName,true) :
15366             this.el.dom; 
15367         
15368         if(!node || node.parentNode == el){
15369                     return node;
15370             }
15371             var p = node.parentNode;
15372             while(p && p != el){
15373             if(p.parentNode == el){
15374                 return p;
15375             }
15376             p = p.parentNode;
15377         }
15378             return null;
15379     },
15380
15381     /** @ignore */
15382     onClick : function(e){
15383         var item = this.findItemFromChild(e.getTarget());
15384         if(item){
15385             var index = this.indexOf(item);
15386             if(this.onItemClick(item, index, e) !== false){
15387                 this.fireEvent("click", this, index, item, e);
15388             }
15389         }else{
15390             this.clearSelections();
15391         }
15392     },
15393
15394     /** @ignore */
15395     onContextMenu : function(e){
15396         var item = this.findItemFromChild(e.getTarget());
15397         if(item){
15398             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15399         }
15400     },
15401
15402     /** @ignore */
15403     onDblClick : function(e){
15404         var item = this.findItemFromChild(e.getTarget());
15405         if(item){
15406             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15407         }
15408     },
15409
15410     onItemClick : function(item, index, e)
15411     {
15412         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15413             return false;
15414         }
15415         if (this.toggleSelect) {
15416             var m = this.isSelected(item) ? 'unselect' : 'select';
15417             //Roo.log(m);
15418             var _t = this;
15419             _t[m](item, true, false);
15420             return true;
15421         }
15422         if(this.multiSelect || this.singleSelect){
15423             if(this.multiSelect && e.shiftKey && this.lastSelection){
15424                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15425             }else{
15426                 this.select(item, this.multiSelect && e.ctrlKey);
15427                 this.lastSelection = item;
15428             }
15429             
15430             if(!this.tickable){
15431                 e.preventDefault();
15432             }
15433             
15434         }
15435         return true;
15436     },
15437
15438     /**
15439      * Get the number of selected nodes.
15440      * @return {Number}
15441      */
15442     getSelectionCount : function(){
15443         return this.selections.length;
15444     },
15445
15446     /**
15447      * Get the currently selected nodes.
15448      * @return {Array} An array of HTMLElements
15449      */
15450     getSelectedNodes : function(){
15451         return this.selections;
15452     },
15453
15454     /**
15455      * Get the indexes of the selected nodes.
15456      * @return {Array}
15457      */
15458     getSelectedIndexes : function(){
15459         var indexes = [], s = this.selections;
15460         for(var i = 0, len = s.length; i < len; i++){
15461             indexes.push(s[i].nodeIndex);
15462         }
15463         return indexes;
15464     },
15465
15466     /**
15467      * Clear all selections
15468      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15469      */
15470     clearSelections : function(suppressEvent){
15471         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15472             this.cmp.elements = this.selections;
15473             this.cmp.removeClass(this.selectedClass);
15474             this.selections = [];
15475             if(!suppressEvent){
15476                 this.fireEvent("selectionchange", this, this.selections);
15477             }
15478         }
15479     },
15480
15481     /**
15482      * Returns true if the passed node is selected
15483      * @param {HTMLElement/Number} node The node or node index
15484      * @return {Boolean}
15485      */
15486     isSelected : function(node){
15487         var s = this.selections;
15488         if(s.length < 1){
15489             return false;
15490         }
15491         node = this.getNode(node);
15492         return s.indexOf(node) !== -1;
15493     },
15494
15495     /**
15496      * Selects nodes.
15497      * @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
15498      * @param {Boolean} keepExisting (optional) true to keep existing selections
15499      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15500      */
15501     select : function(nodeInfo, keepExisting, suppressEvent){
15502         if(nodeInfo instanceof Array){
15503             if(!keepExisting){
15504                 this.clearSelections(true);
15505             }
15506             for(var i = 0, len = nodeInfo.length; i < len; i++){
15507                 this.select(nodeInfo[i], true, true);
15508             }
15509             return;
15510         } 
15511         var node = this.getNode(nodeInfo);
15512         if(!node || this.isSelected(node)){
15513             return; // already selected.
15514         }
15515         if(!keepExisting){
15516             this.clearSelections(true);
15517         }
15518         
15519         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15520             Roo.fly(node).addClass(this.selectedClass);
15521             this.selections.push(node);
15522             if(!suppressEvent){
15523                 this.fireEvent("selectionchange", this, this.selections);
15524             }
15525         }
15526         
15527         
15528     },
15529       /**
15530      * Unselects nodes.
15531      * @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
15532      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15533      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15534      */
15535     unselect : function(nodeInfo, keepExisting, suppressEvent)
15536     {
15537         if(nodeInfo instanceof Array){
15538             Roo.each(this.selections, function(s) {
15539                 this.unselect(s, nodeInfo);
15540             }, this);
15541             return;
15542         }
15543         var node = this.getNode(nodeInfo);
15544         if(!node || !this.isSelected(node)){
15545             //Roo.log("not selected");
15546             return; // not selected.
15547         }
15548         // fireevent???
15549         var ns = [];
15550         Roo.each(this.selections, function(s) {
15551             if (s == node ) {
15552                 Roo.fly(node).removeClass(this.selectedClass);
15553
15554                 return;
15555             }
15556             ns.push(s);
15557         },this);
15558         
15559         this.selections= ns;
15560         this.fireEvent("selectionchange", this, this.selections);
15561     },
15562
15563     /**
15564      * Gets a template node.
15565      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15566      * @return {HTMLElement} The node or null if it wasn't found
15567      */
15568     getNode : function(nodeInfo){
15569         if(typeof nodeInfo == "string"){
15570             return document.getElementById(nodeInfo);
15571         }else if(typeof nodeInfo == "number"){
15572             return this.nodes[nodeInfo];
15573         }
15574         return nodeInfo;
15575     },
15576
15577     /**
15578      * Gets a range template nodes.
15579      * @param {Number} startIndex
15580      * @param {Number} endIndex
15581      * @return {Array} An array of nodes
15582      */
15583     getNodes : function(start, end){
15584         var ns = this.nodes;
15585         start = start || 0;
15586         end = typeof end == "undefined" ? ns.length - 1 : end;
15587         var nodes = [];
15588         if(start <= end){
15589             for(var i = start; i <= end; i++){
15590                 nodes.push(ns[i]);
15591             }
15592         } else{
15593             for(var i = start; i >= end; i--){
15594                 nodes.push(ns[i]);
15595             }
15596         }
15597         return nodes;
15598     },
15599
15600     /**
15601      * Finds the index of the passed node
15602      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15603      * @return {Number} The index of the node or -1
15604      */
15605     indexOf : function(node){
15606         node = this.getNode(node);
15607         if(typeof node.nodeIndex == "number"){
15608             return node.nodeIndex;
15609         }
15610         var ns = this.nodes;
15611         for(var i = 0, len = ns.length; i < len; i++){
15612             if(ns[i] == node){
15613                 return i;
15614             }
15615         }
15616         return -1;
15617     }
15618 });
15619 /*
15620  * - LGPL
15621  *
15622  * based on jquery fullcalendar
15623  * 
15624  */
15625
15626 Roo.bootstrap = Roo.bootstrap || {};
15627 /**
15628  * @class Roo.bootstrap.Calendar
15629  * @extends Roo.bootstrap.Component
15630  * Bootstrap Calendar class
15631  * @cfg {Boolean} loadMask (true|false) default false
15632  * @cfg {Object} header generate the user specific header of the calendar, default false
15633
15634  * @constructor
15635  * Create a new Container
15636  * @param {Object} config The config object
15637  */
15638
15639
15640
15641 Roo.bootstrap.Calendar = function(config){
15642     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15643      this.addEvents({
15644         /**
15645              * @event select
15646              * Fires when a date is selected
15647              * @param {DatePicker} this
15648              * @param {Date} date The selected date
15649              */
15650         'select': true,
15651         /**
15652              * @event monthchange
15653              * Fires when the displayed month changes 
15654              * @param {DatePicker} this
15655              * @param {Date} date The selected month
15656              */
15657         'monthchange': true,
15658         /**
15659              * @event evententer
15660              * Fires when mouse over an event
15661              * @param {Calendar} this
15662              * @param {event} Event
15663              */
15664         'evententer': true,
15665         /**
15666              * @event eventleave
15667              * Fires when the mouse leaves an
15668              * @param {Calendar} this
15669              * @param {event}
15670              */
15671         'eventleave': true,
15672         /**
15673              * @event eventclick
15674              * Fires when the mouse click an
15675              * @param {Calendar} this
15676              * @param {event}
15677              */
15678         'eventclick': true
15679         
15680     });
15681
15682 };
15683
15684 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15685     
15686      /**
15687      * @cfg {Number} startDay
15688      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15689      */
15690     startDay : 0,
15691     
15692     loadMask : false,
15693     
15694     header : false,
15695       
15696     getAutoCreate : function(){
15697         
15698         
15699         var fc_button = function(name, corner, style, content ) {
15700             return Roo.apply({},{
15701                 tag : 'span',
15702                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15703                          (corner.length ?
15704                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15705                             ''
15706                         ),
15707                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15708                 unselectable: 'on'
15709             });
15710         };
15711         
15712         var header = {};
15713         
15714         if(!this.header){
15715             header = {
15716                 tag : 'table',
15717                 cls : 'fc-header',
15718                 style : 'width:100%',
15719                 cn : [
15720                     {
15721                         tag: 'tr',
15722                         cn : [
15723                             {
15724                                 tag : 'td',
15725                                 cls : 'fc-header-left',
15726                                 cn : [
15727                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15728                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15729                                     { tag: 'span', cls: 'fc-header-space' },
15730                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15731
15732
15733                                 ]
15734                             },
15735
15736                             {
15737                                 tag : 'td',
15738                                 cls : 'fc-header-center',
15739                                 cn : [
15740                                     {
15741                                         tag: 'span',
15742                                         cls: 'fc-header-title',
15743                                         cn : {
15744                                             tag: 'H2',
15745                                             html : 'month / year'
15746                                         }
15747                                     }
15748
15749                                 ]
15750                             },
15751                             {
15752                                 tag : 'td',
15753                                 cls : 'fc-header-right',
15754                                 cn : [
15755                               /*      fc_button('month', 'left', '', 'month' ),
15756                                     fc_button('week', '', '', 'week' ),
15757                                     fc_button('day', 'right', '', 'day' )
15758                                 */    
15759
15760                                 ]
15761                             }
15762
15763                         ]
15764                     }
15765                 ]
15766             };
15767         }
15768         
15769         header = this.header;
15770         
15771        
15772         var cal_heads = function() {
15773             var ret = [];
15774             // fixme - handle this.
15775             
15776             for (var i =0; i < Date.dayNames.length; i++) {
15777                 var d = Date.dayNames[i];
15778                 ret.push({
15779                     tag: 'th',
15780                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15781                     html : d.substring(0,3)
15782                 });
15783                 
15784             }
15785             ret[0].cls += ' fc-first';
15786             ret[6].cls += ' fc-last';
15787             return ret;
15788         };
15789         var cal_cell = function(n) {
15790             return  {
15791                 tag: 'td',
15792                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15793                 cn : [
15794                     {
15795                         cn : [
15796                             {
15797                                 cls: 'fc-day-number',
15798                                 html: 'D'
15799                             },
15800                             {
15801                                 cls: 'fc-day-content',
15802                              
15803                                 cn : [
15804                                      {
15805                                         style: 'position: relative;' // height: 17px;
15806                                     }
15807                                 ]
15808                             }
15809                             
15810                             
15811                         ]
15812                     }
15813                 ]
15814                 
15815             }
15816         };
15817         var cal_rows = function() {
15818             
15819             var ret = [];
15820             for (var r = 0; r < 6; r++) {
15821                 var row= {
15822                     tag : 'tr',
15823                     cls : 'fc-week',
15824                     cn : []
15825                 };
15826                 
15827                 for (var i =0; i < Date.dayNames.length; i++) {
15828                     var d = Date.dayNames[i];
15829                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15830
15831                 }
15832                 row.cn[0].cls+=' fc-first';
15833                 row.cn[0].cn[0].style = 'min-height:90px';
15834                 row.cn[6].cls+=' fc-last';
15835                 ret.push(row);
15836                 
15837             }
15838             ret[0].cls += ' fc-first';
15839             ret[4].cls += ' fc-prev-last';
15840             ret[5].cls += ' fc-last';
15841             return ret;
15842             
15843         };
15844         
15845         var cal_table = {
15846             tag: 'table',
15847             cls: 'fc-border-separate',
15848             style : 'width:100%',
15849             cellspacing  : 0,
15850             cn : [
15851                 { 
15852                     tag: 'thead',
15853                     cn : [
15854                         { 
15855                             tag: 'tr',
15856                             cls : 'fc-first fc-last',
15857                             cn : cal_heads()
15858                         }
15859                     ]
15860                 },
15861                 { 
15862                     tag: 'tbody',
15863                     cn : cal_rows()
15864                 }
15865                   
15866             ]
15867         };
15868          
15869          var cfg = {
15870             cls : 'fc fc-ltr',
15871             cn : [
15872                 header,
15873                 {
15874                     cls : 'fc-content',
15875                     style : "position: relative;",
15876                     cn : [
15877                         {
15878                             cls : 'fc-view fc-view-month fc-grid',
15879                             style : 'position: relative',
15880                             unselectable : 'on',
15881                             cn : [
15882                                 {
15883                                     cls : 'fc-event-container',
15884                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15885                                 },
15886                                 cal_table
15887                             ]
15888                         }
15889                     ]
15890     
15891                 }
15892            ] 
15893             
15894         };
15895         
15896          
15897         
15898         return cfg;
15899     },
15900     
15901     
15902     initEvents : function()
15903     {
15904         if(!this.store){
15905             throw "can not find store for calendar";
15906         }
15907         
15908         var mark = {
15909             tag: "div",
15910             cls:"x-dlg-mask",
15911             style: "text-align:center",
15912             cn: [
15913                 {
15914                     tag: "div",
15915                     style: "background-color:white;width:50%;margin:250 auto",
15916                     cn: [
15917                         {
15918                             tag: "img",
15919                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15920                         },
15921                         {
15922                             tag: "span",
15923                             html: "Loading"
15924                         }
15925                         
15926                     ]
15927                 }
15928             ]
15929         };
15930         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15931         
15932         var size = this.el.select('.fc-content', true).first().getSize();
15933         this.maskEl.setSize(size.width, size.height);
15934         this.maskEl.enableDisplayMode("block");
15935         if(!this.loadMask){
15936             this.maskEl.hide();
15937         }
15938         
15939         this.store = Roo.factory(this.store, Roo.data);
15940         this.store.on('load', this.onLoad, this);
15941         this.store.on('beforeload', this.onBeforeLoad, this);
15942         
15943         this.resize();
15944         
15945         this.cells = this.el.select('.fc-day',true);
15946         //Roo.log(this.cells);
15947         this.textNodes = this.el.query('.fc-day-number');
15948         this.cells.addClassOnOver('fc-state-hover');
15949         
15950         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15951         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15952         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15953         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15954         
15955         this.on('monthchange', this.onMonthChange, this);
15956         
15957         this.update(new Date().clearTime());
15958     },
15959     
15960     resize : function() {
15961         var sz  = this.el.getSize();
15962         
15963         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15964         this.el.select('.fc-day-content div',true).setHeight(34);
15965     },
15966     
15967     
15968     // private
15969     showPrevMonth : function(e){
15970         this.update(this.activeDate.add("mo", -1));
15971     },
15972     showToday : function(e){
15973         this.update(new Date().clearTime());
15974     },
15975     // private
15976     showNextMonth : function(e){
15977         this.update(this.activeDate.add("mo", 1));
15978     },
15979
15980     // private
15981     showPrevYear : function(){
15982         this.update(this.activeDate.add("y", -1));
15983     },
15984
15985     // private
15986     showNextYear : function(){
15987         this.update(this.activeDate.add("y", 1));
15988     },
15989
15990     
15991    // private
15992     update : function(date)
15993     {
15994         var vd = this.activeDate;
15995         this.activeDate = date;
15996 //        if(vd && this.el){
15997 //            var t = date.getTime();
15998 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15999 //                Roo.log('using add remove');
16000 //                
16001 //                this.fireEvent('monthchange', this, date);
16002 //                
16003 //                this.cells.removeClass("fc-state-highlight");
16004 //                this.cells.each(function(c){
16005 //                   if(c.dateValue == t){
16006 //                       c.addClass("fc-state-highlight");
16007 //                       setTimeout(function(){
16008 //                            try{c.dom.firstChild.focus();}catch(e){}
16009 //                       }, 50);
16010 //                       return false;
16011 //                   }
16012 //                   return true;
16013 //                });
16014 //                return;
16015 //            }
16016 //        }
16017         
16018         var days = date.getDaysInMonth();
16019         
16020         var firstOfMonth = date.getFirstDateOfMonth();
16021         var startingPos = firstOfMonth.getDay()-this.startDay;
16022         
16023         if(startingPos < this.startDay){
16024             startingPos += 7;
16025         }
16026         
16027         var pm = date.add(Date.MONTH, -1);
16028         var prevStart = pm.getDaysInMonth()-startingPos;
16029 //        
16030         this.cells = this.el.select('.fc-day',true);
16031         this.textNodes = this.el.query('.fc-day-number');
16032         this.cells.addClassOnOver('fc-state-hover');
16033         
16034         var cells = this.cells.elements;
16035         var textEls = this.textNodes;
16036         
16037         Roo.each(cells, function(cell){
16038             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16039         });
16040         
16041         days += startingPos;
16042
16043         // convert everything to numbers so it's fast
16044         var day = 86400000;
16045         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16046         //Roo.log(d);
16047         //Roo.log(pm);
16048         //Roo.log(prevStart);
16049         
16050         var today = new Date().clearTime().getTime();
16051         var sel = date.clearTime().getTime();
16052         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16053         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16054         var ddMatch = this.disabledDatesRE;
16055         var ddText = this.disabledDatesText;
16056         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16057         var ddaysText = this.disabledDaysText;
16058         var format = this.format;
16059         
16060         var setCellClass = function(cal, cell){
16061             cell.row = 0;
16062             cell.events = [];
16063             cell.more = [];
16064             //Roo.log('set Cell Class');
16065             cell.title = "";
16066             var t = d.getTime();
16067             
16068             //Roo.log(d);
16069             
16070             cell.dateValue = t;
16071             if(t == today){
16072                 cell.className += " fc-today";
16073                 cell.className += " fc-state-highlight";
16074                 cell.title = cal.todayText;
16075             }
16076             if(t == sel){
16077                 // disable highlight in other month..
16078                 //cell.className += " fc-state-highlight";
16079                 
16080             }
16081             // disabling
16082             if(t < min) {
16083                 cell.className = " fc-state-disabled";
16084                 cell.title = cal.minText;
16085                 return;
16086             }
16087             if(t > max) {
16088                 cell.className = " fc-state-disabled";
16089                 cell.title = cal.maxText;
16090                 return;
16091             }
16092             if(ddays){
16093                 if(ddays.indexOf(d.getDay()) != -1){
16094                     cell.title = ddaysText;
16095                     cell.className = " fc-state-disabled";
16096                 }
16097             }
16098             if(ddMatch && format){
16099                 var fvalue = d.dateFormat(format);
16100                 if(ddMatch.test(fvalue)){
16101                     cell.title = ddText.replace("%0", fvalue);
16102                     cell.className = " fc-state-disabled";
16103                 }
16104             }
16105             
16106             if (!cell.initialClassName) {
16107                 cell.initialClassName = cell.dom.className;
16108             }
16109             
16110             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16111         };
16112
16113         var i = 0;
16114         
16115         for(; i < startingPos; i++) {
16116             textEls[i].innerHTML = (++prevStart);
16117             d.setDate(d.getDate()+1);
16118             
16119             cells[i].className = "fc-past fc-other-month";
16120             setCellClass(this, cells[i]);
16121         }
16122         
16123         var intDay = 0;
16124         
16125         for(; i < days; i++){
16126             intDay = i - startingPos + 1;
16127             textEls[i].innerHTML = (intDay);
16128             d.setDate(d.getDate()+1);
16129             
16130             cells[i].className = ''; // "x-date-active";
16131             setCellClass(this, cells[i]);
16132         }
16133         var extraDays = 0;
16134         
16135         for(; i < 42; i++) {
16136             textEls[i].innerHTML = (++extraDays);
16137             d.setDate(d.getDate()+1);
16138             
16139             cells[i].className = "fc-future fc-other-month";
16140             setCellClass(this, cells[i]);
16141         }
16142         
16143         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16144         
16145         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16146         
16147         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16148         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16149         
16150         if(totalRows != 6){
16151             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16152             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16153         }
16154         
16155         this.fireEvent('monthchange', this, date);
16156         
16157         
16158         /*
16159         if(!this.internalRender){
16160             var main = this.el.dom.firstChild;
16161             var w = main.offsetWidth;
16162             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16163             Roo.fly(main).setWidth(w);
16164             this.internalRender = true;
16165             // opera does not respect the auto grow header center column
16166             // then, after it gets a width opera refuses to recalculate
16167             // without a second pass
16168             if(Roo.isOpera && !this.secondPass){
16169                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16170                 this.secondPass = true;
16171                 this.update.defer(10, this, [date]);
16172             }
16173         }
16174         */
16175         
16176     },
16177     
16178     findCell : function(dt) {
16179         dt = dt.clearTime().getTime();
16180         var ret = false;
16181         this.cells.each(function(c){
16182             //Roo.log("check " +c.dateValue + '?=' + dt);
16183             if(c.dateValue == dt){
16184                 ret = c;
16185                 return false;
16186             }
16187             return true;
16188         });
16189         
16190         return ret;
16191     },
16192     
16193     findCells : function(ev) {
16194         var s = ev.start.clone().clearTime().getTime();
16195        // Roo.log(s);
16196         var e= ev.end.clone().clearTime().getTime();
16197        // Roo.log(e);
16198         var ret = [];
16199         this.cells.each(function(c){
16200              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16201             
16202             if(c.dateValue > e){
16203                 return ;
16204             }
16205             if(c.dateValue < s){
16206                 return ;
16207             }
16208             ret.push(c);
16209         });
16210         
16211         return ret;    
16212     },
16213     
16214 //    findBestRow: function(cells)
16215 //    {
16216 //        var ret = 0;
16217 //        
16218 //        for (var i =0 ; i < cells.length;i++) {
16219 //            ret  = Math.max(cells[i].rows || 0,ret);
16220 //        }
16221 //        return ret;
16222 //        
16223 //    },
16224     
16225     
16226     addItem : function(ev)
16227     {
16228         // look for vertical location slot in
16229         var cells = this.findCells(ev);
16230         
16231 //        ev.row = this.findBestRow(cells);
16232         
16233         // work out the location.
16234         
16235         var crow = false;
16236         var rows = [];
16237         for(var i =0; i < cells.length; i++) {
16238             
16239             cells[i].row = cells[0].row;
16240             
16241             if(i == 0){
16242                 cells[i].row = cells[i].row + 1;
16243             }
16244             
16245             if (!crow) {
16246                 crow = {
16247                     start : cells[i],
16248                     end :  cells[i]
16249                 };
16250                 continue;
16251             }
16252             if (crow.start.getY() == cells[i].getY()) {
16253                 // on same row.
16254                 crow.end = cells[i];
16255                 continue;
16256             }
16257             // different row.
16258             rows.push(crow);
16259             crow = {
16260                 start: cells[i],
16261                 end : cells[i]
16262             };
16263             
16264         }
16265         
16266         rows.push(crow);
16267         ev.els = [];
16268         ev.rows = rows;
16269         ev.cells = cells;
16270         
16271         cells[0].events.push(ev);
16272         
16273         this.calevents.push(ev);
16274     },
16275     
16276     clearEvents: function() {
16277         
16278         if(!this.calevents){
16279             return;
16280         }
16281         
16282         Roo.each(this.cells.elements, function(c){
16283             c.row = 0;
16284             c.events = [];
16285             c.more = [];
16286         });
16287         
16288         Roo.each(this.calevents, function(e) {
16289             Roo.each(e.els, function(el) {
16290                 el.un('mouseenter' ,this.onEventEnter, this);
16291                 el.un('mouseleave' ,this.onEventLeave, this);
16292                 el.remove();
16293             },this);
16294         },this);
16295         
16296         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16297             e.remove();
16298         });
16299         
16300     },
16301     
16302     renderEvents: function()
16303     {   
16304         var _this = this;
16305         
16306         this.cells.each(function(c) {
16307             
16308             if(c.row < 5){
16309                 return;
16310             }
16311             
16312             var ev = c.events;
16313             
16314             var r = 4;
16315             if(c.row != c.events.length){
16316                 r = 4 - (4 - (c.row - c.events.length));
16317             }
16318             
16319             c.events = ev.slice(0, r);
16320             c.more = ev.slice(r);
16321             
16322             if(c.more.length && c.more.length == 1){
16323                 c.events.push(c.more.pop());
16324             }
16325             
16326             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16327             
16328         });
16329             
16330         this.cells.each(function(c) {
16331             
16332             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16333             
16334             
16335             for (var e = 0; e < c.events.length; e++){
16336                 var ev = c.events[e];
16337                 var rows = ev.rows;
16338                 
16339                 for(var i = 0; i < rows.length; i++) {
16340                 
16341                     // how many rows should it span..
16342
16343                     var  cfg = {
16344                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16345                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16346
16347                         unselectable : "on",
16348                         cn : [
16349                             {
16350                                 cls: 'fc-event-inner',
16351                                 cn : [
16352     //                                {
16353     //                                  tag:'span',
16354     //                                  cls: 'fc-event-time',
16355     //                                  html : cells.length > 1 ? '' : ev.time
16356     //                                },
16357                                     {
16358                                       tag:'span',
16359                                       cls: 'fc-event-title',
16360                                       html : String.format('{0}', ev.title)
16361                                     }
16362
16363
16364                                 ]
16365                             },
16366                             {
16367                                 cls: 'ui-resizable-handle ui-resizable-e',
16368                                 html : '&nbsp;&nbsp;&nbsp'
16369                             }
16370
16371                         ]
16372                     };
16373
16374                     if (i == 0) {
16375                         cfg.cls += ' fc-event-start';
16376                     }
16377                     if ((i+1) == rows.length) {
16378                         cfg.cls += ' fc-event-end';
16379                     }
16380
16381                     var ctr = _this.el.select('.fc-event-container',true).first();
16382                     var cg = ctr.createChild(cfg);
16383
16384                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16385                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16386
16387                     var r = (c.more.length) ? 1 : 0;
16388                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16389                     cg.setWidth(ebox.right - sbox.x -2);
16390
16391                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16392                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16393                     cg.on('click', _this.onEventClick, _this, ev);
16394
16395                     ev.els.push(cg);
16396                     
16397                 }
16398                 
16399             }
16400             
16401             
16402             if(c.more.length){
16403                 var  cfg = {
16404                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16405                     style : 'position: absolute',
16406                     unselectable : "on",
16407                     cn : [
16408                         {
16409                             cls: 'fc-event-inner',
16410                             cn : [
16411                                 {
16412                                   tag:'span',
16413                                   cls: 'fc-event-title',
16414                                   html : 'More'
16415                                 }
16416
16417
16418                             ]
16419                         },
16420                         {
16421                             cls: 'ui-resizable-handle ui-resizable-e',
16422                             html : '&nbsp;&nbsp;&nbsp'
16423                         }
16424
16425                     ]
16426                 };
16427
16428                 var ctr = _this.el.select('.fc-event-container',true).first();
16429                 var cg = ctr.createChild(cfg);
16430
16431                 var sbox = c.select('.fc-day-content',true).first().getBox();
16432                 var ebox = c.select('.fc-day-content',true).first().getBox();
16433                 //Roo.log(cg);
16434                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16435                 cg.setWidth(ebox.right - sbox.x -2);
16436
16437                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16438                 
16439             }
16440             
16441         });
16442         
16443         
16444         
16445     },
16446     
16447     onEventEnter: function (e, el,event,d) {
16448         this.fireEvent('evententer', this, el, event);
16449     },
16450     
16451     onEventLeave: function (e, el,event,d) {
16452         this.fireEvent('eventleave', this, el, event);
16453     },
16454     
16455     onEventClick: function (e, el,event,d) {
16456         this.fireEvent('eventclick', this, el, event);
16457     },
16458     
16459     onMonthChange: function () {
16460         this.store.load();
16461     },
16462     
16463     onMoreEventClick: function(e, el, more)
16464     {
16465         var _this = this;
16466         
16467         this.calpopover.placement = 'right';
16468         this.calpopover.setTitle('More');
16469         
16470         this.calpopover.setContent('');
16471         
16472         var ctr = this.calpopover.el.select('.popover-content', true).first();
16473         
16474         Roo.each(more, function(m){
16475             var cfg = {
16476                 cls : 'fc-event-hori fc-event-draggable',
16477                 html : m.title
16478             };
16479             var cg = ctr.createChild(cfg);
16480             
16481             cg.on('click', _this.onEventClick, _this, m);
16482         });
16483         
16484         this.calpopover.show(el);
16485         
16486         
16487     },
16488     
16489     onLoad: function () 
16490     {   
16491         this.calevents = [];
16492         var cal = this;
16493         
16494         if(this.store.getCount() > 0){
16495             this.store.data.each(function(d){
16496                cal.addItem({
16497                     id : d.data.id,
16498                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16499                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16500                     time : d.data.start_time,
16501                     title : d.data.title,
16502                     description : d.data.description,
16503                     venue : d.data.venue
16504                 });
16505             });
16506         }
16507         
16508         this.renderEvents();
16509         
16510         if(this.calevents.length && this.loadMask){
16511             this.maskEl.hide();
16512         }
16513     },
16514     
16515     onBeforeLoad: function()
16516     {
16517         this.clearEvents();
16518         if(this.loadMask){
16519             this.maskEl.show();
16520         }
16521     }
16522 });
16523
16524  
16525  /*
16526  * - LGPL
16527  *
16528  * element
16529  * 
16530  */
16531
16532 /**
16533  * @class Roo.bootstrap.Popover
16534  * @extends Roo.bootstrap.Component
16535  * Bootstrap Popover class
16536  * @cfg {String} html contents of the popover   (or false to use children..)
16537  * @cfg {String} title of popover (or false to hide)
16538  * @cfg {String} placement how it is placed
16539  * @cfg {String} trigger click || hover (or false to trigger manually)
16540  * @cfg {String} over what (parent or false to trigger manually.)
16541  * @cfg {Number} delay - delay before showing
16542  
16543  * @constructor
16544  * Create a new Popover
16545  * @param {Object} config The config object
16546  */
16547
16548 Roo.bootstrap.Popover = function(config){
16549     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16550     
16551     this.addEvents({
16552         // raw events
16553          /**
16554          * @event show
16555          * After the popover show
16556          * 
16557          * @param {Roo.bootstrap.Popover} this
16558          */
16559         "show" : true,
16560         /**
16561          * @event hide
16562          * After the popover hide
16563          * 
16564          * @param {Roo.bootstrap.Popover} this
16565          */
16566         "hide" : true
16567     });
16568 };
16569
16570 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16571     
16572     title: 'Fill in a title',
16573     html: false,
16574     
16575     placement : 'right',
16576     trigger : 'hover', // hover
16577     
16578     delay : 0,
16579     
16580     over: 'parent',
16581     
16582     can_build_overlaid : false,
16583     
16584     getChildContainer : function()
16585     {
16586         return this.el.select('.popover-content',true).first();
16587     },
16588     
16589     getAutoCreate : function(){
16590          
16591         var cfg = {
16592            cls : 'popover roo-dynamic',
16593            style: 'display:block',
16594            cn : [
16595                 {
16596                     cls : 'arrow'
16597                 },
16598                 {
16599                     cls : 'popover-inner',
16600                     cn : [
16601                         {
16602                             tag: 'h3',
16603                             cls: 'popover-title',
16604                             html : this.title
16605                         },
16606                         {
16607                             cls : 'popover-content',
16608                             html : this.html
16609                         }
16610                     ]
16611                     
16612                 }
16613            ]
16614         };
16615         
16616         return cfg;
16617     },
16618     setTitle: function(str)
16619     {
16620         this.title = str;
16621         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16622     },
16623     setContent: function(str)
16624     {
16625         this.html = str;
16626         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16627     },
16628     // as it get's added to the bottom of the page.
16629     onRender : function(ct, position)
16630     {
16631         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16632         if(!this.el){
16633             var cfg = Roo.apply({},  this.getAutoCreate());
16634             cfg.id = Roo.id();
16635             
16636             if (this.cls) {
16637                 cfg.cls += ' ' + this.cls;
16638             }
16639             if (this.style) {
16640                 cfg.style = this.style;
16641             }
16642             //Roo.log("adding to ");
16643             this.el = Roo.get(document.body).createChild(cfg, position);
16644 //            Roo.log(this.el);
16645         }
16646         this.initEvents();
16647     },
16648     
16649     initEvents : function()
16650     {
16651         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16652         this.el.enableDisplayMode('block');
16653         this.el.hide();
16654         if (this.over === false) {
16655             return; 
16656         }
16657         if (this.triggers === false) {
16658             return;
16659         }
16660         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16661         var triggers = this.trigger ? this.trigger.split(' ') : [];
16662         Roo.each(triggers, function(trigger) {
16663         
16664             if (trigger == 'click') {
16665                 on_el.on('click', this.toggle, this);
16666             } else if (trigger != 'manual') {
16667                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16668                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16669       
16670                 on_el.on(eventIn  ,this.enter, this);
16671                 on_el.on(eventOut, this.leave, this);
16672             }
16673         }, this);
16674         
16675     },
16676     
16677     
16678     // private
16679     timeout : null,
16680     hoverState : null,
16681     
16682     toggle : function () {
16683         this.hoverState == 'in' ? this.leave() : this.enter();
16684     },
16685     
16686     enter : function () {
16687         
16688         clearTimeout(this.timeout);
16689     
16690         this.hoverState = 'in';
16691     
16692         if (!this.delay || !this.delay.show) {
16693             this.show();
16694             return;
16695         }
16696         var _t = this;
16697         this.timeout = setTimeout(function () {
16698             if (_t.hoverState == 'in') {
16699                 _t.show();
16700             }
16701         }, this.delay.show)
16702     },
16703     
16704     leave : function() {
16705         clearTimeout(this.timeout);
16706     
16707         this.hoverState = 'out';
16708     
16709         if (!this.delay || !this.delay.hide) {
16710             this.hide();
16711             return;
16712         }
16713         var _t = this;
16714         this.timeout = setTimeout(function () {
16715             if (_t.hoverState == 'out') {
16716                 _t.hide();
16717             }
16718         }, this.delay.hide)
16719     },
16720     
16721     show : function (on_el)
16722     {
16723         if (!on_el) {
16724             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16725         }
16726         
16727         // set content.
16728         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16729         if (this.html !== false) {
16730             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16731         }
16732         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16733         if (!this.title.length) {
16734             this.el.select('.popover-title',true).hide();
16735         }
16736         
16737         var placement = typeof this.placement == 'function' ?
16738             this.placement.call(this, this.el, on_el) :
16739             this.placement;
16740             
16741         var autoToken = /\s?auto?\s?/i;
16742         var autoPlace = autoToken.test(placement);
16743         if (autoPlace) {
16744             placement = placement.replace(autoToken, '') || 'top';
16745         }
16746         
16747         //this.el.detach()
16748         //this.el.setXY([0,0]);
16749         this.el.show();
16750         this.el.dom.style.display='block';
16751         this.el.addClass(placement);
16752         
16753         //this.el.appendTo(on_el);
16754         
16755         var p = this.getPosition();
16756         var box = this.el.getBox();
16757         
16758         if (autoPlace) {
16759             // fixme..
16760         }
16761         var align = Roo.bootstrap.Popover.alignment[placement];
16762         this.el.alignTo(on_el, align[0],align[1]);
16763         //var arrow = this.el.select('.arrow',true).first();
16764         //arrow.set(align[2], 
16765         
16766         this.el.addClass('in');
16767         
16768         
16769         if (this.el.hasClass('fade')) {
16770             // fade it?
16771         }
16772         
16773         this.hoverState = 'in';
16774         
16775         this.fireEvent('show', this);
16776         
16777     },
16778     hide : function()
16779     {
16780         this.el.setXY([0,0]);
16781         this.el.removeClass('in');
16782         this.el.hide();
16783         this.hoverState = null;
16784         
16785         this.fireEvent('hide', this);
16786     }
16787     
16788 });
16789
16790 Roo.bootstrap.Popover.alignment = {
16791     'left' : ['r-l', [-10,0], 'right'],
16792     'right' : ['l-r', [10,0], 'left'],
16793     'bottom' : ['t-b', [0,10], 'top'],
16794     'top' : [ 'b-t', [0,-10], 'bottom']
16795 };
16796
16797  /*
16798  * - LGPL
16799  *
16800  * Progress
16801  * 
16802  */
16803
16804 /**
16805  * @class Roo.bootstrap.Progress
16806  * @extends Roo.bootstrap.Component
16807  * Bootstrap Progress class
16808  * @cfg {Boolean} striped striped of the progress bar
16809  * @cfg {Boolean} active animated of the progress bar
16810  * 
16811  * 
16812  * @constructor
16813  * Create a new Progress
16814  * @param {Object} config The config object
16815  */
16816
16817 Roo.bootstrap.Progress = function(config){
16818     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16819 };
16820
16821 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16822     
16823     striped : false,
16824     active: false,
16825     
16826     getAutoCreate : function(){
16827         var cfg = {
16828             tag: 'div',
16829             cls: 'progress'
16830         };
16831         
16832         
16833         if(this.striped){
16834             cfg.cls += ' progress-striped';
16835         }
16836       
16837         if(this.active){
16838             cfg.cls += ' active';
16839         }
16840         
16841         
16842         return cfg;
16843     }
16844    
16845 });
16846
16847  
16848
16849  /*
16850  * - LGPL
16851  *
16852  * ProgressBar
16853  * 
16854  */
16855
16856 /**
16857  * @class Roo.bootstrap.ProgressBar
16858  * @extends Roo.bootstrap.Component
16859  * Bootstrap ProgressBar class
16860  * @cfg {Number} aria_valuenow aria-value now
16861  * @cfg {Number} aria_valuemin aria-value min
16862  * @cfg {Number} aria_valuemax aria-value max
16863  * @cfg {String} label label for the progress bar
16864  * @cfg {String} panel (success | info | warning | danger )
16865  * @cfg {String} role role of the progress bar
16866  * @cfg {String} sr_only text
16867  * 
16868  * 
16869  * @constructor
16870  * Create a new ProgressBar
16871  * @param {Object} config The config object
16872  */
16873
16874 Roo.bootstrap.ProgressBar = function(config){
16875     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16876 };
16877
16878 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16879     
16880     aria_valuenow : 0,
16881     aria_valuemin : 0,
16882     aria_valuemax : 100,
16883     label : false,
16884     panel : false,
16885     role : false,
16886     sr_only: false,
16887     
16888     getAutoCreate : function()
16889     {
16890         
16891         var cfg = {
16892             tag: 'div',
16893             cls: 'progress-bar',
16894             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16895         };
16896         
16897         if(this.sr_only){
16898             cfg.cn = {
16899                 tag: 'span',
16900                 cls: 'sr-only',
16901                 html: this.sr_only
16902             }
16903         }
16904         
16905         if(this.role){
16906             cfg.role = this.role;
16907         }
16908         
16909         if(this.aria_valuenow){
16910             cfg['aria-valuenow'] = this.aria_valuenow;
16911         }
16912         
16913         if(this.aria_valuemin){
16914             cfg['aria-valuemin'] = this.aria_valuemin;
16915         }
16916         
16917         if(this.aria_valuemax){
16918             cfg['aria-valuemax'] = this.aria_valuemax;
16919         }
16920         
16921         if(this.label && !this.sr_only){
16922             cfg.html = this.label;
16923         }
16924         
16925         if(this.panel){
16926             cfg.cls += ' progress-bar-' + this.panel;
16927         }
16928         
16929         return cfg;
16930     },
16931     
16932     update : function(aria_valuenow)
16933     {
16934         this.aria_valuenow = aria_valuenow;
16935         
16936         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16937     }
16938    
16939 });
16940
16941  
16942
16943  /*
16944  * - LGPL
16945  *
16946  * column
16947  * 
16948  */
16949
16950 /**
16951  * @class Roo.bootstrap.TabGroup
16952  * @extends Roo.bootstrap.Column
16953  * Bootstrap Column class
16954  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16955  * @cfg {Boolean} carousel true to make the group behave like a carousel
16956  * @cfg {Boolean} bullets show bullets for the panels
16957  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16958  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16959  * @cfg {Boolean} showarrow (true|false) show arrow default true
16960  * 
16961  * @constructor
16962  * Create a new TabGroup
16963  * @param {Object} config The config object
16964  */
16965
16966 Roo.bootstrap.TabGroup = function(config){
16967     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16968     if (!this.navId) {
16969         this.navId = Roo.id();
16970     }
16971     this.tabs = [];
16972     Roo.bootstrap.TabGroup.register(this);
16973     
16974 };
16975
16976 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16977     
16978     carousel : false,
16979     transition : false,
16980     bullets : 0,
16981     timer : 0,
16982     autoslide : false,
16983     slideFn : false,
16984     slideOnTouch : false,
16985     showarrow : true,
16986     
16987     getAutoCreate : function()
16988     {
16989         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16990         
16991         cfg.cls += ' tab-content';
16992         
16993         if (this.carousel) {
16994             cfg.cls += ' carousel slide';
16995             
16996             cfg.cn = [{
16997                cls : 'carousel-inner',
16998                cn : []
16999             }];
17000         
17001             if(this.bullets  && !Roo.isTouch){
17002                 
17003                 var bullets = {
17004                     cls : 'carousel-bullets',
17005                     cn : []
17006                 };
17007                
17008                 if(this.bullets_cls){
17009                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17010                 }
17011                 
17012                 bullets.cn.push({
17013                     cls : 'clear'
17014                 });
17015                 
17016                 cfg.cn[0].cn.push(bullets);
17017             }
17018             
17019             if(this.showarrow){
17020                 cfg.cn[0].cn.push({
17021                     tag : 'div',
17022                     class : 'carousel-arrow',
17023                     cn : [
17024                         {
17025                             tag : 'div',
17026                             class : 'carousel-prev',
17027                             cn : [
17028                                 {
17029                                     tag : 'i',
17030                                     class : 'fa fa-chevron-left'
17031                                 }
17032                             ]
17033                         },
17034                         {
17035                             tag : 'div',
17036                             class : 'carousel-next',
17037                             cn : [
17038                                 {
17039                                     tag : 'i',
17040                                     class : 'fa fa-chevron-right'
17041                                 }
17042                             ]
17043                         }
17044                     ]
17045                 });
17046             }
17047             
17048         }
17049         
17050         return cfg;
17051     },
17052     
17053     initEvents:  function()
17054     {
17055 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17056 //            this.el.on("touchstart", this.onTouchStart, this);
17057 //        }
17058         
17059         if(this.autoslide){
17060             var _this = this;
17061             
17062             this.slideFn = window.setInterval(function() {
17063                 _this.showPanelNext();
17064             }, this.timer);
17065         }
17066         
17067         if(this.showarrow){
17068             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17069             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17070         }
17071         
17072         
17073     },
17074     
17075 //    onTouchStart : function(e, el, o)
17076 //    {
17077 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17078 //            return;
17079 //        }
17080 //        
17081 //        this.showPanelNext();
17082 //    },
17083     
17084     
17085     getChildContainer : function()
17086     {
17087         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17088     },
17089     
17090     /**
17091     * register a Navigation item
17092     * @param {Roo.bootstrap.NavItem} the navitem to add
17093     */
17094     register : function(item)
17095     {
17096         this.tabs.push( item);
17097         item.navId = this.navId; // not really needed..
17098         this.addBullet();
17099     
17100     },
17101     
17102     getActivePanel : function()
17103     {
17104         var r = false;
17105         Roo.each(this.tabs, function(t) {
17106             if (t.active) {
17107                 r = t;
17108                 return false;
17109             }
17110             return null;
17111         });
17112         return r;
17113         
17114     },
17115     getPanelByName : function(n)
17116     {
17117         var r = false;
17118         Roo.each(this.tabs, function(t) {
17119             if (t.tabId == n) {
17120                 r = t;
17121                 return false;
17122             }
17123             return null;
17124         });
17125         return r;
17126     },
17127     indexOfPanel : function(p)
17128     {
17129         var r = false;
17130         Roo.each(this.tabs, function(t,i) {
17131             if (t.tabId == p.tabId) {
17132                 r = i;
17133                 return false;
17134             }
17135             return null;
17136         });
17137         return r;
17138     },
17139     /**
17140      * show a specific panel
17141      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17142      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17143      */
17144     showPanel : function (pan)
17145     {
17146         if(this.transition || typeof(pan) == 'undefined'){
17147             Roo.log("waiting for the transitionend");
17148             return;
17149         }
17150         
17151         if (typeof(pan) == 'number') {
17152             pan = this.tabs[pan];
17153         }
17154         
17155         if (typeof(pan) == 'string') {
17156             pan = this.getPanelByName(pan);
17157         }
17158         
17159         var cur = this.getActivePanel();
17160         
17161         if(!pan || !cur){
17162             Roo.log('pan or acitve pan is undefined');
17163             return false;
17164         }
17165         
17166         if (pan.tabId == this.getActivePanel().tabId) {
17167             return true;
17168         }
17169         
17170         if (false === cur.fireEvent('beforedeactivate')) {
17171             return false;
17172         }
17173         
17174         if(this.bullets > 0 && !Roo.isTouch){
17175             this.setActiveBullet(this.indexOfPanel(pan));
17176         }
17177         
17178         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17179             
17180             this.transition = true;
17181             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17182             var lr = dir == 'next' ? 'left' : 'right';
17183             pan.el.addClass(dir); // or prev
17184             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17185             cur.el.addClass(lr); // or right
17186             pan.el.addClass(lr);
17187             
17188             var _this = this;
17189             cur.el.on('transitionend', function() {
17190                 Roo.log("trans end?");
17191                 
17192                 pan.el.removeClass([lr,dir]);
17193                 pan.setActive(true);
17194                 
17195                 cur.el.removeClass([lr]);
17196                 cur.setActive(false);
17197                 
17198                 _this.transition = false;
17199                 
17200             }, this, { single:  true } );
17201             
17202             return true;
17203         }
17204         
17205         cur.setActive(false);
17206         pan.setActive(true);
17207         
17208         return true;
17209         
17210     },
17211     showPanelNext : function()
17212     {
17213         var i = this.indexOfPanel(this.getActivePanel());
17214         
17215         if (i >= this.tabs.length - 1 && !this.autoslide) {
17216             return;
17217         }
17218         
17219         if (i >= this.tabs.length - 1 && this.autoslide) {
17220             i = -1;
17221         }
17222         
17223         this.showPanel(this.tabs[i+1]);
17224     },
17225     
17226     showPanelPrev : function()
17227     {
17228         var i = this.indexOfPanel(this.getActivePanel());
17229         
17230         if (i  < 1 && !this.autoslide) {
17231             return;
17232         }
17233         
17234         if (i < 1 && this.autoslide) {
17235             i = this.tabs.length;
17236         }
17237         
17238         this.showPanel(this.tabs[i-1]);
17239     },
17240     
17241     
17242     addBullet: function()
17243     {
17244         if(!this.bullets || Roo.isTouch){
17245             return;
17246         }
17247         var ctr = this.el.select('.carousel-bullets',true).first();
17248         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17249         var bullet = ctr.createChild({
17250             cls : 'bullet bullet-' + i
17251         },ctr.dom.lastChild);
17252         
17253         
17254         var _this = this;
17255         
17256         bullet.on('click', (function(e, el, o, ii, t){
17257
17258             e.preventDefault();
17259
17260             this.showPanel(ii);
17261
17262             if(this.autoslide && this.slideFn){
17263                 clearInterval(this.slideFn);
17264                 this.slideFn = window.setInterval(function() {
17265                     _this.showPanelNext();
17266                 }, this.timer);
17267             }
17268
17269         }).createDelegate(this, [i, bullet], true));
17270                 
17271         
17272     },
17273      
17274     setActiveBullet : function(i)
17275     {
17276         if(Roo.isTouch){
17277             return;
17278         }
17279         
17280         Roo.each(this.el.select('.bullet', true).elements, function(el){
17281             el.removeClass('selected');
17282         });
17283
17284         var bullet = this.el.select('.bullet-' + i, true).first();
17285         
17286         if(!bullet){
17287             return;
17288         }
17289         
17290         bullet.addClass('selected');
17291     }
17292     
17293     
17294   
17295 });
17296
17297  
17298
17299  
17300  
17301 Roo.apply(Roo.bootstrap.TabGroup, {
17302     
17303     groups: {},
17304      /**
17305     * register a Navigation Group
17306     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17307     */
17308     register : function(navgrp)
17309     {
17310         this.groups[navgrp.navId] = navgrp;
17311         
17312     },
17313     /**
17314     * fetch a Navigation Group based on the navigation ID
17315     * if one does not exist , it will get created.
17316     * @param {string} the navgroup to add
17317     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17318     */
17319     get: function(navId) {
17320         if (typeof(this.groups[navId]) == 'undefined') {
17321             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17322         }
17323         return this.groups[navId] ;
17324     }
17325     
17326     
17327     
17328 });
17329
17330  /*
17331  * - LGPL
17332  *
17333  * TabPanel
17334  * 
17335  */
17336
17337 /**
17338  * @class Roo.bootstrap.TabPanel
17339  * @extends Roo.bootstrap.Component
17340  * Bootstrap TabPanel class
17341  * @cfg {Boolean} active panel active
17342  * @cfg {String} html panel content
17343  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17344  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17345  * @cfg {String} href click to link..
17346  * 
17347  * 
17348  * @constructor
17349  * Create a new TabPanel
17350  * @param {Object} config The config object
17351  */
17352
17353 Roo.bootstrap.TabPanel = function(config){
17354     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17355     this.addEvents({
17356         /**
17357              * @event changed
17358              * Fires when the active status changes
17359              * @param {Roo.bootstrap.TabPanel} this
17360              * @param {Boolean} state the new state
17361             
17362          */
17363         'changed': true,
17364         /**
17365              * @event beforedeactivate
17366              * Fires before a tab is de-activated - can be used to do validation on a form.
17367              * @param {Roo.bootstrap.TabPanel} this
17368              * @return {Boolean} false if there is an error
17369             
17370          */
17371         'beforedeactivate': true
17372      });
17373     
17374     this.tabId = this.tabId || Roo.id();
17375   
17376 };
17377
17378 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17379     
17380     active: false,
17381     html: false,
17382     tabId: false,
17383     navId : false,
17384     href : '',
17385     
17386     getAutoCreate : function(){
17387         var cfg = {
17388             tag: 'div',
17389             // item is needed for carousel - not sure if it has any effect otherwise
17390             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17391             html: this.html || ''
17392         };
17393         
17394         if(this.active){
17395             cfg.cls += ' active';
17396         }
17397         
17398         if(this.tabId){
17399             cfg.tabId = this.tabId;
17400         }
17401         
17402         
17403         return cfg;
17404     },
17405     
17406     initEvents:  function()
17407     {
17408         var p = this.parent();
17409         
17410         this.navId = this.navId || p.navId;
17411         
17412         if (typeof(this.navId) != 'undefined') {
17413             // not really needed.. but just in case.. parent should be a NavGroup.
17414             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17415             
17416             tg.register(this);
17417             
17418             var i = tg.tabs.length - 1;
17419             
17420             if(this.active && tg.bullets > 0 && i < tg.bullets){
17421                 tg.setActiveBullet(i);
17422             }
17423         }
17424         
17425         this.el.on('click', this.onClick, this);
17426         
17427         if(Roo.isTouch){
17428             this.el.on("touchstart", this.onTouchStart, this);
17429             this.el.on("touchmove", this.onTouchMove, this);
17430             this.el.on("touchend", this.onTouchEnd, this);
17431         }
17432         
17433     },
17434     
17435     onRender : function(ct, position)
17436     {
17437         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17438     },
17439     
17440     setActive : function(state)
17441     {
17442         Roo.log("panel - set active " + this.tabId + "=" + state);
17443         
17444         this.active = state;
17445         if (!state) {
17446             this.el.removeClass('active');
17447             
17448         } else  if (!this.el.hasClass('active')) {
17449             this.el.addClass('active');
17450         }
17451         
17452         this.fireEvent('changed', this, state);
17453     },
17454     
17455     onClick : function(e)
17456     {
17457         e.preventDefault();
17458         
17459         if(!this.href.length){
17460             return;
17461         }
17462         
17463         window.location.href = this.href;
17464     },
17465     
17466     startX : 0,
17467     startY : 0,
17468     endX : 0,
17469     endY : 0,
17470     swiping : false,
17471     
17472     onTouchStart : function(e)
17473     {
17474         this.swiping = false;
17475         
17476         this.startX = e.browserEvent.touches[0].clientX;
17477         this.startY = e.browserEvent.touches[0].clientY;
17478     },
17479     
17480     onTouchMove : function(e)
17481     {
17482         this.swiping = true;
17483         
17484         this.endX = e.browserEvent.touches[0].clientX;
17485         this.endY = e.browserEvent.touches[0].clientY;
17486     },
17487     
17488     onTouchEnd : function(e)
17489     {
17490         if(!this.swiping){
17491             this.onClick(e);
17492             return;
17493         }
17494         
17495         var tabGroup = this.parent();
17496         
17497         if(this.endX > this.startX){ // swiping right
17498             tabGroup.showPanelPrev();
17499             return;
17500         }
17501         
17502         if(this.startX > this.endX){ // swiping left
17503             tabGroup.showPanelNext();
17504             return;
17505         }
17506     }
17507     
17508     
17509 });
17510  
17511
17512  
17513
17514  /*
17515  * - LGPL
17516  *
17517  * DateField
17518  * 
17519  */
17520
17521 /**
17522  * @class Roo.bootstrap.DateField
17523  * @extends Roo.bootstrap.Input
17524  * Bootstrap DateField class
17525  * @cfg {Number} weekStart default 0
17526  * @cfg {String} viewMode default empty, (months|years)
17527  * @cfg {String} minViewMode default empty, (months|years)
17528  * @cfg {Number} startDate default -Infinity
17529  * @cfg {Number} endDate default Infinity
17530  * @cfg {Boolean} todayHighlight default false
17531  * @cfg {Boolean} todayBtn default false
17532  * @cfg {Boolean} calendarWeeks default false
17533  * @cfg {Object} daysOfWeekDisabled default empty
17534  * @cfg {Boolean} singleMode default false (true | false)
17535  * 
17536  * @cfg {Boolean} keyboardNavigation default true
17537  * @cfg {String} language default en
17538  * 
17539  * @constructor
17540  * Create a new DateField
17541  * @param {Object} config The config object
17542  */
17543
17544 Roo.bootstrap.DateField = function(config){
17545     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17546      this.addEvents({
17547             /**
17548              * @event show
17549              * Fires when this field show.
17550              * @param {Roo.bootstrap.DateField} this
17551              * @param {Mixed} date The date value
17552              */
17553             show : true,
17554             /**
17555              * @event show
17556              * Fires when this field hide.
17557              * @param {Roo.bootstrap.DateField} this
17558              * @param {Mixed} date The date value
17559              */
17560             hide : true,
17561             /**
17562              * @event select
17563              * Fires when select a date.
17564              * @param {Roo.bootstrap.DateField} this
17565              * @param {Mixed} date The date value
17566              */
17567             select : true,
17568             /**
17569              * @event beforeselect
17570              * Fires when before select a date.
17571              * @param {Roo.bootstrap.DateField} this
17572              * @param {Mixed} date The date value
17573              */
17574             beforeselect : true
17575         });
17576 };
17577
17578 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17579     
17580     /**
17581      * @cfg {String} format
17582      * The default date format string which can be overriden for localization support.  The format must be
17583      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17584      */
17585     format : "m/d/y",
17586     /**
17587      * @cfg {String} altFormats
17588      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17589      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17590      */
17591     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17592     
17593     weekStart : 0,
17594     
17595     viewMode : '',
17596     
17597     minViewMode : '',
17598     
17599     todayHighlight : false,
17600     
17601     todayBtn: false,
17602     
17603     language: 'en',
17604     
17605     keyboardNavigation: true,
17606     
17607     calendarWeeks: false,
17608     
17609     startDate: -Infinity,
17610     
17611     endDate: Infinity,
17612     
17613     daysOfWeekDisabled: [],
17614     
17615     _events: [],
17616     
17617     singleMode : false,
17618     
17619     UTCDate: function()
17620     {
17621         return new Date(Date.UTC.apply(Date, arguments));
17622     },
17623     
17624     UTCToday: function()
17625     {
17626         var today = new Date();
17627         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17628     },
17629     
17630     getDate: function() {
17631             var d = this.getUTCDate();
17632             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17633     },
17634     
17635     getUTCDate: function() {
17636             return this.date;
17637     },
17638     
17639     setDate: function(d) {
17640             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17641     },
17642     
17643     setUTCDate: function(d) {
17644             this.date = d;
17645             this.setValue(this.formatDate(this.date));
17646     },
17647         
17648     onRender: function(ct, position)
17649     {
17650         
17651         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17652         
17653         this.language = this.language || 'en';
17654         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17655         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17656         
17657         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17658         this.format = this.format || 'm/d/y';
17659         this.isInline = false;
17660         this.isInput = true;
17661         this.component = this.el.select('.add-on', true).first() || false;
17662         this.component = (this.component && this.component.length === 0) ? false : this.component;
17663         this.hasInput = this.component && this.inputEl().length;
17664         
17665         if (typeof(this.minViewMode === 'string')) {
17666             switch (this.minViewMode) {
17667                 case 'months':
17668                     this.minViewMode = 1;
17669                     break;
17670                 case 'years':
17671                     this.minViewMode = 2;
17672                     break;
17673                 default:
17674                     this.minViewMode = 0;
17675                     break;
17676             }
17677         }
17678         
17679         if (typeof(this.viewMode === 'string')) {
17680             switch (this.viewMode) {
17681                 case 'months':
17682                     this.viewMode = 1;
17683                     break;
17684                 case 'years':
17685                     this.viewMode = 2;
17686                     break;
17687                 default:
17688                     this.viewMode = 0;
17689                     break;
17690             }
17691         }
17692                 
17693         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17694         
17695 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17696         
17697         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17698         
17699         this.picker().on('mousedown', this.onMousedown, this);
17700         this.picker().on('click', this.onClick, this);
17701         
17702         this.picker().addClass('datepicker-dropdown');
17703         
17704         this.startViewMode = this.viewMode;
17705         
17706         if(this.singleMode){
17707             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17708                 v.setVisibilityMode(Roo.Element.DISPLAY);
17709                 v.hide();
17710             });
17711             
17712             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17713                 v.setStyle('width', '189px');
17714             });
17715         }
17716         
17717         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17718             if(!this.calendarWeeks){
17719                 v.remove();
17720                 return;
17721             }
17722             
17723             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17724             v.attr('colspan', function(i, val){
17725                 return parseInt(val) + 1;
17726             });
17727         });
17728                         
17729         
17730         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17731         
17732         this.setStartDate(this.startDate);
17733         this.setEndDate(this.endDate);
17734         
17735         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17736         
17737         this.fillDow();
17738         this.fillMonths();
17739         this.update();
17740         this.showMode();
17741         
17742         if(this.isInline) {
17743             this.show();
17744         }
17745     },
17746     
17747     picker : function()
17748     {
17749         return this.pickerEl;
17750 //        return this.el.select('.datepicker', true).first();
17751     },
17752     
17753     fillDow: function()
17754     {
17755         var dowCnt = this.weekStart;
17756         
17757         var dow = {
17758             tag: 'tr',
17759             cn: [
17760                 
17761             ]
17762         };
17763         
17764         if(this.calendarWeeks){
17765             dow.cn.push({
17766                 tag: 'th',
17767                 cls: 'cw',
17768                 html: '&nbsp;'
17769             })
17770         }
17771         
17772         while (dowCnt < this.weekStart + 7) {
17773             dow.cn.push({
17774                 tag: 'th',
17775                 cls: 'dow',
17776                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17777             });
17778         }
17779         
17780         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17781     },
17782     
17783     fillMonths: function()
17784     {    
17785         var i = 0;
17786         var months = this.picker().select('>.datepicker-months td', true).first();
17787         
17788         months.dom.innerHTML = '';
17789         
17790         while (i < 12) {
17791             var month = {
17792                 tag: 'span',
17793                 cls: 'month',
17794                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17795             };
17796             
17797             months.createChild(month);
17798         }
17799         
17800     },
17801     
17802     update: function()
17803     {
17804         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;
17805         
17806         if (this.date < this.startDate) {
17807             this.viewDate = new Date(this.startDate);
17808         } else if (this.date > this.endDate) {
17809             this.viewDate = new Date(this.endDate);
17810         } else {
17811             this.viewDate = new Date(this.date);
17812         }
17813         
17814         this.fill();
17815     },
17816     
17817     fill: function() 
17818     {
17819         var d = new Date(this.viewDate),
17820                 year = d.getUTCFullYear(),
17821                 month = d.getUTCMonth(),
17822                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17823                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17824                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17825                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17826                 currentDate = this.date && this.date.valueOf(),
17827                 today = this.UTCToday();
17828         
17829         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17830         
17831 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17832         
17833 //        this.picker.select('>tfoot th.today').
17834 //                                              .text(dates[this.language].today)
17835 //                                              .toggle(this.todayBtn !== false);
17836     
17837         this.updateNavArrows();
17838         this.fillMonths();
17839                                                 
17840         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17841         
17842         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17843          
17844         prevMonth.setUTCDate(day);
17845         
17846         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17847         
17848         var nextMonth = new Date(prevMonth);
17849         
17850         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17851         
17852         nextMonth = nextMonth.valueOf();
17853         
17854         var fillMonths = false;
17855         
17856         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17857         
17858         while(prevMonth.valueOf() < nextMonth) {
17859             var clsName = '';
17860             
17861             if (prevMonth.getUTCDay() === this.weekStart) {
17862                 if(fillMonths){
17863                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17864                 }
17865                     
17866                 fillMonths = {
17867                     tag: 'tr',
17868                     cn: []
17869                 };
17870                 
17871                 if(this.calendarWeeks){
17872                     // ISO 8601: First week contains first thursday.
17873                     // ISO also states week starts on Monday, but we can be more abstract here.
17874                     var
17875                     // Start of current week: based on weekstart/current date
17876                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17877                     // Thursday of this week
17878                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17879                     // First Thursday of year, year from thursday
17880                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17881                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17882                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17883                     
17884                     fillMonths.cn.push({
17885                         tag: 'td',
17886                         cls: 'cw',
17887                         html: calWeek
17888                     });
17889                 }
17890             }
17891             
17892             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17893                 clsName += ' old';
17894             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17895                 clsName += ' new';
17896             }
17897             if (this.todayHighlight &&
17898                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17899                 prevMonth.getUTCMonth() == today.getMonth() &&
17900                 prevMonth.getUTCDate() == today.getDate()) {
17901                 clsName += ' today';
17902             }
17903             
17904             if (currentDate && prevMonth.valueOf() === currentDate) {
17905                 clsName += ' active';
17906             }
17907             
17908             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17909                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17910                     clsName += ' disabled';
17911             }
17912             
17913             fillMonths.cn.push({
17914                 tag: 'td',
17915                 cls: 'day ' + clsName,
17916                 html: prevMonth.getDate()
17917             });
17918             
17919             prevMonth.setDate(prevMonth.getDate()+1);
17920         }
17921           
17922         var currentYear = this.date && this.date.getUTCFullYear();
17923         var currentMonth = this.date && this.date.getUTCMonth();
17924         
17925         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17926         
17927         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17928             v.removeClass('active');
17929             
17930             if(currentYear === year && k === currentMonth){
17931                 v.addClass('active');
17932             }
17933             
17934             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17935                 v.addClass('disabled');
17936             }
17937             
17938         });
17939         
17940         
17941         year = parseInt(year/10, 10) * 10;
17942         
17943         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17944         
17945         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17946         
17947         year -= 1;
17948         for (var i = -1; i < 11; i++) {
17949             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17950                 tag: 'span',
17951                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17952                 html: year
17953             });
17954             
17955             year += 1;
17956         }
17957     },
17958     
17959     showMode: function(dir) 
17960     {
17961         if (dir) {
17962             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17963         }
17964         
17965         Roo.each(this.picker().select('>div',true).elements, function(v){
17966             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17967             v.hide();
17968         });
17969         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17970     },
17971     
17972     place: function()
17973     {
17974         if(this.isInline) {
17975             return;
17976         }
17977         
17978         this.picker().removeClass(['bottom', 'top']);
17979         
17980         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17981             /*
17982              * place to the top of element!
17983              *
17984              */
17985             
17986             this.picker().addClass('top');
17987             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17988             
17989             return;
17990         }
17991         
17992         this.picker().addClass('bottom');
17993         
17994         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17995     },
17996     
17997     parseDate : function(value)
17998     {
17999         if(!value || value instanceof Date){
18000             return value;
18001         }
18002         var v = Date.parseDate(value, this.format);
18003         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18004             v = Date.parseDate(value, 'Y-m-d');
18005         }
18006         if(!v && this.altFormats){
18007             if(!this.altFormatsArray){
18008                 this.altFormatsArray = this.altFormats.split("|");
18009             }
18010             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18011                 v = Date.parseDate(value, this.altFormatsArray[i]);
18012             }
18013         }
18014         return v;
18015     },
18016     
18017     formatDate : function(date, fmt)
18018     {   
18019         return (!date || !(date instanceof Date)) ?
18020         date : date.dateFormat(fmt || this.format);
18021     },
18022     
18023     onFocus : function()
18024     {
18025         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18026         this.show();
18027     },
18028     
18029     onBlur : function()
18030     {
18031         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18032         
18033         var d = this.inputEl().getValue();
18034         
18035         this.setValue(d);
18036                 
18037         this.hide();
18038     },
18039     
18040     show : function()
18041     {
18042         this.picker().show();
18043         this.update();
18044         this.place();
18045         
18046         this.fireEvent('show', this, this.date);
18047     },
18048     
18049     hide : function()
18050     {
18051         if(this.isInline) {
18052             return;
18053         }
18054         this.picker().hide();
18055         this.viewMode = this.startViewMode;
18056         this.showMode();
18057         
18058         this.fireEvent('hide', this, this.date);
18059         
18060     },
18061     
18062     onMousedown: function(e)
18063     {
18064         e.stopPropagation();
18065         e.preventDefault();
18066     },
18067     
18068     keyup: function(e)
18069     {
18070         Roo.bootstrap.DateField.superclass.keyup.call(this);
18071         this.update();
18072     },
18073
18074     setValue: function(v)
18075     {
18076         if(this.fireEvent('beforeselect', this, v) !== false){
18077             var d = new Date(this.parseDate(v) ).clearTime();
18078         
18079             if(isNaN(d.getTime())){
18080                 this.date = this.viewDate = '';
18081                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18082                 return;
18083             }
18084
18085             v = this.formatDate(d);
18086
18087             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18088
18089             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18090
18091             this.update();
18092
18093             this.fireEvent('select', this, this.date);
18094         }
18095     },
18096     
18097     getValue: function()
18098     {
18099         return this.formatDate(this.date);
18100     },
18101     
18102     fireKey: function(e)
18103     {
18104         if (!this.picker().isVisible()){
18105             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18106                 this.show();
18107             }
18108             return;
18109         }
18110         
18111         var dateChanged = false,
18112         dir, day, month,
18113         newDate, newViewDate;
18114         
18115         switch(e.keyCode){
18116             case 27: // escape
18117                 this.hide();
18118                 e.preventDefault();
18119                 break;
18120             case 37: // left
18121             case 39: // right
18122                 if (!this.keyboardNavigation) {
18123                     break;
18124                 }
18125                 dir = e.keyCode == 37 ? -1 : 1;
18126                 
18127                 if (e.ctrlKey){
18128                     newDate = this.moveYear(this.date, dir);
18129                     newViewDate = this.moveYear(this.viewDate, dir);
18130                 } else if (e.shiftKey){
18131                     newDate = this.moveMonth(this.date, dir);
18132                     newViewDate = this.moveMonth(this.viewDate, dir);
18133                 } else {
18134                     newDate = new Date(this.date);
18135                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18136                     newViewDate = new Date(this.viewDate);
18137                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18138                 }
18139                 if (this.dateWithinRange(newDate)){
18140                     this.date = newDate;
18141                     this.viewDate = newViewDate;
18142                     this.setValue(this.formatDate(this.date));
18143 //                    this.update();
18144                     e.preventDefault();
18145                     dateChanged = true;
18146                 }
18147                 break;
18148             case 38: // up
18149             case 40: // down
18150                 if (!this.keyboardNavigation) {
18151                     break;
18152                 }
18153                 dir = e.keyCode == 38 ? -1 : 1;
18154                 if (e.ctrlKey){
18155                     newDate = this.moveYear(this.date, dir);
18156                     newViewDate = this.moveYear(this.viewDate, dir);
18157                 } else if (e.shiftKey){
18158                     newDate = this.moveMonth(this.date, dir);
18159                     newViewDate = this.moveMonth(this.viewDate, dir);
18160                 } else {
18161                     newDate = new Date(this.date);
18162                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18163                     newViewDate = new Date(this.viewDate);
18164                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18165                 }
18166                 if (this.dateWithinRange(newDate)){
18167                     this.date = newDate;
18168                     this.viewDate = newViewDate;
18169                     this.setValue(this.formatDate(this.date));
18170 //                    this.update();
18171                     e.preventDefault();
18172                     dateChanged = true;
18173                 }
18174                 break;
18175             case 13: // enter
18176                 this.setValue(this.formatDate(this.date));
18177                 this.hide();
18178                 e.preventDefault();
18179                 break;
18180             case 9: // tab
18181                 this.setValue(this.formatDate(this.date));
18182                 this.hide();
18183                 break;
18184             case 16: // shift
18185             case 17: // ctrl
18186             case 18: // alt
18187                 break;
18188             default :
18189                 this.hide();
18190                 
18191         }
18192     },
18193     
18194     
18195     onClick: function(e) 
18196     {
18197         e.stopPropagation();
18198         e.preventDefault();
18199         
18200         var target = e.getTarget();
18201         
18202         if(target.nodeName.toLowerCase() === 'i'){
18203             target = Roo.get(target).dom.parentNode;
18204         }
18205         
18206         var nodeName = target.nodeName;
18207         var className = target.className;
18208         var html = target.innerHTML;
18209         //Roo.log(nodeName);
18210         
18211         switch(nodeName.toLowerCase()) {
18212             case 'th':
18213                 switch(className) {
18214                     case 'switch':
18215                         this.showMode(1);
18216                         break;
18217                     case 'prev':
18218                     case 'next':
18219                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18220                         switch(this.viewMode){
18221                                 case 0:
18222                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18223                                         break;
18224                                 case 1:
18225                                 case 2:
18226                                         this.viewDate = this.moveYear(this.viewDate, dir);
18227                                         break;
18228                         }
18229                         this.fill();
18230                         break;
18231                     case 'today':
18232                         var date = new Date();
18233                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18234 //                        this.fill()
18235                         this.setValue(this.formatDate(this.date));
18236                         
18237                         this.hide();
18238                         break;
18239                 }
18240                 break;
18241             case 'span':
18242                 if (className.indexOf('disabled') < 0) {
18243                     this.viewDate.setUTCDate(1);
18244                     if (className.indexOf('month') > -1) {
18245                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18246                     } else {
18247                         var year = parseInt(html, 10) || 0;
18248                         this.viewDate.setUTCFullYear(year);
18249                         
18250                     }
18251                     
18252                     if(this.singleMode){
18253                         this.setValue(this.formatDate(this.viewDate));
18254                         this.hide();
18255                         return;
18256                     }
18257                     
18258                     this.showMode(-1);
18259                     this.fill();
18260                 }
18261                 break;
18262                 
18263             case 'td':
18264                 //Roo.log(className);
18265                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18266                     var day = parseInt(html, 10) || 1;
18267                     var year = this.viewDate.getUTCFullYear(),
18268                         month = this.viewDate.getUTCMonth();
18269
18270                     if (className.indexOf('old') > -1) {
18271                         if(month === 0 ){
18272                             month = 11;
18273                             year -= 1;
18274                         }else{
18275                             month -= 1;
18276                         }
18277                     } else if (className.indexOf('new') > -1) {
18278                         if (month == 11) {
18279                             month = 0;
18280                             year += 1;
18281                         } else {
18282                             month += 1;
18283                         }
18284                     }
18285                     //Roo.log([year,month,day]);
18286                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18287                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18288 //                    this.fill();
18289                     //Roo.log(this.formatDate(this.date));
18290                     this.setValue(this.formatDate(this.date));
18291                     this.hide();
18292                 }
18293                 break;
18294         }
18295     },
18296     
18297     setStartDate: function(startDate)
18298     {
18299         this.startDate = startDate || -Infinity;
18300         if (this.startDate !== -Infinity) {
18301             this.startDate = this.parseDate(this.startDate);
18302         }
18303         this.update();
18304         this.updateNavArrows();
18305     },
18306
18307     setEndDate: function(endDate)
18308     {
18309         this.endDate = endDate || Infinity;
18310         if (this.endDate !== Infinity) {
18311             this.endDate = this.parseDate(this.endDate);
18312         }
18313         this.update();
18314         this.updateNavArrows();
18315     },
18316     
18317     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18318     {
18319         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18320         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18321             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18322         }
18323         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18324             return parseInt(d, 10);
18325         });
18326         this.update();
18327         this.updateNavArrows();
18328     },
18329     
18330     updateNavArrows: function() 
18331     {
18332         if(this.singleMode){
18333             return;
18334         }
18335         
18336         var d = new Date(this.viewDate),
18337         year = d.getUTCFullYear(),
18338         month = d.getUTCMonth();
18339         
18340         Roo.each(this.picker().select('.prev', true).elements, function(v){
18341             v.show();
18342             switch (this.viewMode) {
18343                 case 0:
18344
18345                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18346                         v.hide();
18347                     }
18348                     break;
18349                 case 1:
18350                 case 2:
18351                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18352                         v.hide();
18353                     }
18354                     break;
18355             }
18356         });
18357         
18358         Roo.each(this.picker().select('.next', true).elements, function(v){
18359             v.show();
18360             switch (this.viewMode) {
18361                 case 0:
18362
18363                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18364                         v.hide();
18365                     }
18366                     break;
18367                 case 1:
18368                 case 2:
18369                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18370                         v.hide();
18371                     }
18372                     break;
18373             }
18374         })
18375     },
18376     
18377     moveMonth: function(date, dir)
18378     {
18379         if (!dir) {
18380             return date;
18381         }
18382         var new_date = new Date(date.valueOf()),
18383         day = new_date.getUTCDate(),
18384         month = new_date.getUTCMonth(),
18385         mag = Math.abs(dir),
18386         new_month, test;
18387         dir = dir > 0 ? 1 : -1;
18388         if (mag == 1){
18389             test = dir == -1
18390             // If going back one month, make sure month is not current month
18391             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18392             ? function(){
18393                 return new_date.getUTCMonth() == month;
18394             }
18395             // If going forward one month, make sure month is as expected
18396             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18397             : function(){
18398                 return new_date.getUTCMonth() != new_month;
18399             };
18400             new_month = month + dir;
18401             new_date.setUTCMonth(new_month);
18402             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18403             if (new_month < 0 || new_month > 11) {
18404                 new_month = (new_month + 12) % 12;
18405             }
18406         } else {
18407             // For magnitudes >1, move one month at a time...
18408             for (var i=0; i<mag; i++) {
18409                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18410                 new_date = this.moveMonth(new_date, dir);
18411             }
18412             // ...then reset the day, keeping it in the new month
18413             new_month = new_date.getUTCMonth();
18414             new_date.setUTCDate(day);
18415             test = function(){
18416                 return new_month != new_date.getUTCMonth();
18417             };
18418         }
18419         // Common date-resetting loop -- if date is beyond end of month, make it
18420         // end of month
18421         while (test()){
18422             new_date.setUTCDate(--day);
18423             new_date.setUTCMonth(new_month);
18424         }
18425         return new_date;
18426     },
18427
18428     moveYear: function(date, dir)
18429     {
18430         return this.moveMonth(date, dir*12);
18431     },
18432
18433     dateWithinRange: function(date)
18434     {
18435         return date >= this.startDate && date <= this.endDate;
18436     },
18437
18438     
18439     remove: function() 
18440     {
18441         this.picker().remove();
18442     },
18443     
18444     validateValue : function(value)
18445     {
18446         if(value.length < 1)  {
18447             if(this.allowBlank){
18448                 return true;
18449             }
18450             return false;
18451         }
18452         
18453         if(value.length < this.minLength){
18454             return false;
18455         }
18456         if(value.length > this.maxLength){
18457             return false;
18458         }
18459         if(this.vtype){
18460             var vt = Roo.form.VTypes;
18461             if(!vt[this.vtype](value, this)){
18462                 return false;
18463             }
18464         }
18465         if(typeof this.validator == "function"){
18466             var msg = this.validator(value);
18467             if(msg !== true){
18468                 return false;
18469             }
18470         }
18471         
18472         if(this.regex && !this.regex.test(value)){
18473             return false;
18474         }
18475         
18476         if(typeof(this.parseDate(value)) == 'undefined'){
18477             return false;
18478         }
18479         
18480         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18481             return false;
18482         }      
18483         
18484         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18485             return false;
18486         } 
18487         
18488         
18489         return true;
18490     }
18491    
18492 });
18493
18494 Roo.apply(Roo.bootstrap.DateField,  {
18495     
18496     head : {
18497         tag: 'thead',
18498         cn: [
18499         {
18500             tag: 'tr',
18501             cn: [
18502             {
18503                 tag: 'th',
18504                 cls: 'prev',
18505                 html: '<i class="fa fa-arrow-left"/>'
18506             },
18507             {
18508                 tag: 'th',
18509                 cls: 'switch',
18510                 colspan: '5'
18511             },
18512             {
18513                 tag: 'th',
18514                 cls: 'next',
18515                 html: '<i class="fa fa-arrow-right"/>'
18516             }
18517
18518             ]
18519         }
18520         ]
18521     },
18522     
18523     content : {
18524         tag: 'tbody',
18525         cn: [
18526         {
18527             tag: 'tr',
18528             cn: [
18529             {
18530                 tag: 'td',
18531                 colspan: '7'
18532             }
18533             ]
18534         }
18535         ]
18536     },
18537     
18538     footer : {
18539         tag: 'tfoot',
18540         cn: [
18541         {
18542             tag: 'tr',
18543             cn: [
18544             {
18545                 tag: 'th',
18546                 colspan: '7',
18547                 cls: 'today'
18548             }
18549                     
18550             ]
18551         }
18552         ]
18553     },
18554     
18555     dates:{
18556         en: {
18557             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18558             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18559             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18560             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18561             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18562             today: "Today"
18563         }
18564     },
18565     
18566     modes: [
18567     {
18568         clsName: 'days',
18569         navFnc: 'Month',
18570         navStep: 1
18571     },
18572     {
18573         clsName: 'months',
18574         navFnc: 'FullYear',
18575         navStep: 1
18576     },
18577     {
18578         clsName: 'years',
18579         navFnc: 'FullYear',
18580         navStep: 10
18581     }]
18582 });
18583
18584 Roo.apply(Roo.bootstrap.DateField,  {
18585   
18586     template : {
18587         tag: 'div',
18588         cls: 'datepicker dropdown-menu roo-dynamic',
18589         cn: [
18590         {
18591             tag: 'div',
18592             cls: 'datepicker-days',
18593             cn: [
18594             {
18595                 tag: 'table',
18596                 cls: 'table-condensed',
18597                 cn:[
18598                 Roo.bootstrap.DateField.head,
18599                 {
18600                     tag: 'tbody'
18601                 },
18602                 Roo.bootstrap.DateField.footer
18603                 ]
18604             }
18605             ]
18606         },
18607         {
18608             tag: 'div',
18609             cls: 'datepicker-months',
18610             cn: [
18611             {
18612                 tag: 'table',
18613                 cls: 'table-condensed',
18614                 cn:[
18615                 Roo.bootstrap.DateField.head,
18616                 Roo.bootstrap.DateField.content,
18617                 Roo.bootstrap.DateField.footer
18618                 ]
18619             }
18620             ]
18621         },
18622         {
18623             tag: 'div',
18624             cls: 'datepicker-years',
18625             cn: [
18626             {
18627                 tag: 'table',
18628                 cls: 'table-condensed',
18629                 cn:[
18630                 Roo.bootstrap.DateField.head,
18631                 Roo.bootstrap.DateField.content,
18632                 Roo.bootstrap.DateField.footer
18633                 ]
18634             }
18635             ]
18636         }
18637         ]
18638     }
18639 });
18640
18641  
18642
18643  /*
18644  * - LGPL
18645  *
18646  * TimeField
18647  * 
18648  */
18649
18650 /**
18651  * @class Roo.bootstrap.TimeField
18652  * @extends Roo.bootstrap.Input
18653  * Bootstrap DateField class
18654  * 
18655  * 
18656  * @constructor
18657  * Create a new TimeField
18658  * @param {Object} config The config object
18659  */
18660
18661 Roo.bootstrap.TimeField = function(config){
18662     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18663     this.addEvents({
18664             /**
18665              * @event show
18666              * Fires when this field show.
18667              * @param {Roo.bootstrap.DateField} thisthis
18668              * @param {Mixed} date The date value
18669              */
18670             show : true,
18671             /**
18672              * @event show
18673              * Fires when this field hide.
18674              * @param {Roo.bootstrap.DateField} this
18675              * @param {Mixed} date The date value
18676              */
18677             hide : true,
18678             /**
18679              * @event select
18680              * Fires when select a date.
18681              * @param {Roo.bootstrap.DateField} this
18682              * @param {Mixed} date The date value
18683              */
18684             select : true
18685         });
18686 };
18687
18688 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18689     
18690     /**
18691      * @cfg {String} format
18692      * The default time format string which can be overriden for localization support.  The format must be
18693      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18694      */
18695     format : "H:i",
18696        
18697     onRender: function(ct, position)
18698     {
18699         
18700         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18701                 
18702         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18703         
18704         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18705         
18706         this.pop = this.picker().select('>.datepicker-time',true).first();
18707         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18708         
18709         this.picker().on('mousedown', this.onMousedown, this);
18710         this.picker().on('click', this.onClick, this);
18711         
18712         this.picker().addClass('datepicker-dropdown');
18713     
18714         this.fillTime();
18715         this.update();
18716             
18717         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18718         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18719         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18720         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18721         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18722         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18723
18724     },
18725     
18726     fireKey: function(e){
18727         if (!this.picker().isVisible()){
18728             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18729                 this.show();
18730             }
18731             return;
18732         }
18733
18734         e.preventDefault();
18735         
18736         switch(e.keyCode){
18737             case 27: // escape
18738                 this.hide();
18739                 break;
18740             case 37: // left
18741             case 39: // right
18742                 this.onTogglePeriod();
18743                 break;
18744             case 38: // up
18745                 this.onIncrementMinutes();
18746                 break;
18747             case 40: // down
18748                 this.onDecrementMinutes();
18749                 break;
18750             case 13: // enter
18751             case 9: // tab
18752                 this.setTime();
18753                 break;
18754         }
18755     },
18756     
18757     onClick: function(e) {
18758         e.stopPropagation();
18759         e.preventDefault();
18760     },
18761     
18762     picker : function()
18763     {
18764         return this.el.select('.datepicker', true).first();
18765     },
18766     
18767     fillTime: function()
18768     {    
18769         var time = this.pop.select('tbody', true).first();
18770         
18771         time.dom.innerHTML = '';
18772         
18773         time.createChild({
18774             tag: 'tr',
18775             cn: [
18776                 {
18777                     tag: 'td',
18778                     cn: [
18779                         {
18780                             tag: 'a',
18781                             href: '#',
18782                             cls: 'btn',
18783                             cn: [
18784                                 {
18785                                     tag: 'span',
18786                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18787                                 }
18788                             ]
18789                         } 
18790                     ]
18791                 },
18792                 {
18793                     tag: 'td',
18794                     cls: 'separator'
18795                 },
18796                 {
18797                     tag: 'td',
18798                     cn: [
18799                         {
18800                             tag: 'a',
18801                             href: '#',
18802                             cls: 'btn',
18803                             cn: [
18804                                 {
18805                                     tag: 'span',
18806                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18807                                 }
18808                             ]
18809                         }
18810                     ]
18811                 },
18812                 {
18813                     tag: 'td',
18814                     cls: 'separator'
18815                 }
18816             ]
18817         });
18818         
18819         time.createChild({
18820             tag: 'tr',
18821             cn: [
18822                 {
18823                     tag: 'td',
18824                     cn: [
18825                         {
18826                             tag: 'span',
18827                             cls: 'timepicker-hour',
18828                             html: '00'
18829                         }  
18830                     ]
18831                 },
18832                 {
18833                     tag: 'td',
18834                     cls: 'separator',
18835                     html: ':'
18836                 },
18837                 {
18838                     tag: 'td',
18839                     cn: [
18840                         {
18841                             tag: 'span',
18842                             cls: 'timepicker-minute',
18843                             html: '00'
18844                         }  
18845                     ]
18846                 },
18847                 {
18848                     tag: 'td',
18849                     cls: 'separator'
18850                 },
18851                 {
18852                     tag: 'td',
18853                     cn: [
18854                         {
18855                             tag: 'button',
18856                             type: 'button',
18857                             cls: 'btn btn-primary period',
18858                             html: 'AM'
18859                             
18860                         }
18861                     ]
18862                 }
18863             ]
18864         });
18865         
18866         time.createChild({
18867             tag: 'tr',
18868             cn: [
18869                 {
18870                     tag: 'td',
18871                     cn: [
18872                         {
18873                             tag: 'a',
18874                             href: '#',
18875                             cls: 'btn',
18876                             cn: [
18877                                 {
18878                                     tag: 'span',
18879                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18880                                 }
18881                             ]
18882                         }
18883                     ]
18884                 },
18885                 {
18886                     tag: 'td',
18887                     cls: 'separator'
18888                 },
18889                 {
18890                     tag: 'td',
18891                     cn: [
18892                         {
18893                             tag: 'a',
18894                             href: '#',
18895                             cls: 'btn',
18896                             cn: [
18897                                 {
18898                                     tag: 'span',
18899                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18900                                 }
18901                             ]
18902                         }
18903                     ]
18904                 },
18905                 {
18906                     tag: 'td',
18907                     cls: 'separator'
18908                 }
18909             ]
18910         });
18911         
18912     },
18913     
18914     update: function()
18915     {
18916         
18917         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18918         
18919         this.fill();
18920     },
18921     
18922     fill: function() 
18923     {
18924         var hours = this.time.getHours();
18925         var minutes = this.time.getMinutes();
18926         var period = 'AM';
18927         
18928         if(hours > 11){
18929             period = 'PM';
18930         }
18931         
18932         if(hours == 0){
18933             hours = 12;
18934         }
18935         
18936         
18937         if(hours > 12){
18938             hours = hours - 12;
18939         }
18940         
18941         if(hours < 10){
18942             hours = '0' + hours;
18943         }
18944         
18945         if(minutes < 10){
18946             minutes = '0' + minutes;
18947         }
18948         
18949         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18950         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18951         this.pop.select('button', true).first().dom.innerHTML = period;
18952         
18953     },
18954     
18955     place: function()
18956     {   
18957         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18958         
18959         var cls = ['bottom'];
18960         
18961         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18962             cls.pop();
18963             cls.push('top');
18964         }
18965         
18966         cls.push('right');
18967         
18968         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18969             cls.pop();
18970             cls.push('left');
18971         }
18972         
18973         this.picker().addClass(cls.join('-'));
18974         
18975         var _this = this;
18976         
18977         Roo.each(cls, function(c){
18978             if(c == 'bottom'){
18979                 _this.picker().setTop(_this.inputEl().getHeight());
18980                 return;
18981             }
18982             if(c == 'top'){
18983                 _this.picker().setTop(0 - _this.picker().getHeight());
18984                 return;
18985             }
18986             
18987             if(c == 'left'){
18988                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18989                 return;
18990             }
18991             if(c == 'right'){
18992                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18993                 return;
18994             }
18995         });
18996         
18997     },
18998   
18999     onFocus : function()
19000     {
19001         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19002         this.show();
19003     },
19004     
19005     onBlur : function()
19006     {
19007         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19008         this.hide();
19009     },
19010     
19011     show : function()
19012     {
19013         this.picker().show();
19014         this.pop.show();
19015         this.update();
19016         this.place();
19017         
19018         this.fireEvent('show', this, this.date);
19019     },
19020     
19021     hide : function()
19022     {
19023         this.picker().hide();
19024         this.pop.hide();
19025         
19026         this.fireEvent('hide', this, this.date);
19027     },
19028     
19029     setTime : function()
19030     {
19031         this.hide();
19032         this.setValue(this.time.format(this.format));
19033         
19034         this.fireEvent('select', this, this.date);
19035         
19036         
19037     },
19038     
19039     onMousedown: function(e){
19040         e.stopPropagation();
19041         e.preventDefault();
19042     },
19043     
19044     onIncrementHours: function()
19045     {
19046         Roo.log('onIncrementHours');
19047         this.time = this.time.add(Date.HOUR, 1);
19048         this.update();
19049         
19050     },
19051     
19052     onDecrementHours: function()
19053     {
19054         Roo.log('onDecrementHours');
19055         this.time = this.time.add(Date.HOUR, -1);
19056         this.update();
19057     },
19058     
19059     onIncrementMinutes: function()
19060     {
19061         Roo.log('onIncrementMinutes');
19062         this.time = this.time.add(Date.MINUTE, 1);
19063         this.update();
19064     },
19065     
19066     onDecrementMinutes: function()
19067     {
19068         Roo.log('onDecrementMinutes');
19069         this.time = this.time.add(Date.MINUTE, -1);
19070         this.update();
19071     },
19072     
19073     onTogglePeriod: function()
19074     {
19075         Roo.log('onTogglePeriod');
19076         this.time = this.time.add(Date.HOUR, 12);
19077         this.update();
19078     }
19079     
19080    
19081 });
19082
19083 Roo.apply(Roo.bootstrap.TimeField,  {
19084     
19085     content : {
19086         tag: 'tbody',
19087         cn: [
19088             {
19089                 tag: 'tr',
19090                 cn: [
19091                 {
19092                     tag: 'td',
19093                     colspan: '7'
19094                 }
19095                 ]
19096             }
19097         ]
19098     },
19099     
19100     footer : {
19101         tag: 'tfoot',
19102         cn: [
19103             {
19104                 tag: 'tr',
19105                 cn: [
19106                 {
19107                     tag: 'th',
19108                     colspan: '7',
19109                     cls: '',
19110                     cn: [
19111                         {
19112                             tag: 'button',
19113                             cls: 'btn btn-info ok',
19114                             html: 'OK'
19115                         }
19116                     ]
19117                 }
19118
19119                 ]
19120             }
19121         ]
19122     }
19123 });
19124
19125 Roo.apply(Roo.bootstrap.TimeField,  {
19126   
19127     template : {
19128         tag: 'div',
19129         cls: 'datepicker dropdown-menu',
19130         cn: [
19131             {
19132                 tag: 'div',
19133                 cls: 'datepicker-time',
19134                 cn: [
19135                 {
19136                     tag: 'table',
19137                     cls: 'table-condensed',
19138                     cn:[
19139                     Roo.bootstrap.TimeField.content,
19140                     Roo.bootstrap.TimeField.footer
19141                     ]
19142                 }
19143                 ]
19144             }
19145         ]
19146     }
19147 });
19148
19149  
19150
19151  /*
19152  * - LGPL
19153  *
19154  * MonthField
19155  * 
19156  */
19157
19158 /**
19159  * @class Roo.bootstrap.MonthField
19160  * @extends Roo.bootstrap.Input
19161  * Bootstrap MonthField class
19162  * 
19163  * @cfg {String} language default en
19164  * 
19165  * @constructor
19166  * Create a new MonthField
19167  * @param {Object} config The config object
19168  */
19169
19170 Roo.bootstrap.MonthField = function(config){
19171     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19172     
19173     this.addEvents({
19174         /**
19175          * @event show
19176          * Fires when this field show.
19177          * @param {Roo.bootstrap.MonthField} this
19178          * @param {Mixed} date The date value
19179          */
19180         show : true,
19181         /**
19182          * @event show
19183          * Fires when this field hide.
19184          * @param {Roo.bootstrap.MonthField} this
19185          * @param {Mixed} date The date value
19186          */
19187         hide : true,
19188         /**
19189          * @event select
19190          * Fires when select a date.
19191          * @param {Roo.bootstrap.MonthField} this
19192          * @param {String} oldvalue The old value
19193          * @param {String} newvalue The new value
19194          */
19195         select : true
19196     });
19197 };
19198
19199 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19200     
19201     onRender: function(ct, position)
19202     {
19203         
19204         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19205         
19206         this.language = this.language || 'en';
19207         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19208         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19209         
19210         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19211         this.isInline = false;
19212         this.isInput = true;
19213         this.component = this.el.select('.add-on', true).first() || false;
19214         this.component = (this.component && this.component.length === 0) ? false : this.component;
19215         this.hasInput = this.component && this.inputEL().length;
19216         
19217         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19218         
19219         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19220         
19221         this.picker().on('mousedown', this.onMousedown, this);
19222         this.picker().on('click', this.onClick, this);
19223         
19224         this.picker().addClass('datepicker-dropdown');
19225         
19226         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19227             v.setStyle('width', '189px');
19228         });
19229         
19230         this.fillMonths();
19231         
19232         this.update();
19233         
19234         if(this.isInline) {
19235             this.show();
19236         }
19237         
19238     },
19239     
19240     setValue: function(v, suppressEvent)
19241     {   
19242         var o = this.getValue();
19243         
19244         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19245         
19246         this.update();
19247
19248         if(suppressEvent !== true){
19249             this.fireEvent('select', this, o, v);
19250         }
19251         
19252     },
19253     
19254     getValue: function()
19255     {
19256         return this.value;
19257     },
19258     
19259     onClick: function(e) 
19260     {
19261         e.stopPropagation();
19262         e.preventDefault();
19263         
19264         var target = e.getTarget();
19265         
19266         if(target.nodeName.toLowerCase() === 'i'){
19267             target = Roo.get(target).dom.parentNode;
19268         }
19269         
19270         var nodeName = target.nodeName;
19271         var className = target.className;
19272         var html = target.innerHTML;
19273         
19274         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19275             return;
19276         }
19277         
19278         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19279         
19280         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19281         
19282         this.hide();
19283                         
19284     },
19285     
19286     picker : function()
19287     {
19288         return this.pickerEl;
19289     },
19290     
19291     fillMonths: function()
19292     {    
19293         var i = 0;
19294         var months = this.picker().select('>.datepicker-months td', true).first();
19295         
19296         months.dom.innerHTML = '';
19297         
19298         while (i < 12) {
19299             var month = {
19300                 tag: 'span',
19301                 cls: 'month',
19302                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19303             };
19304             
19305             months.createChild(month);
19306         }
19307         
19308     },
19309     
19310     update: function()
19311     {
19312         var _this = this;
19313         
19314         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19315             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19316         }
19317         
19318         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19319             e.removeClass('active');
19320             
19321             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19322                 e.addClass('active');
19323             }
19324         })
19325     },
19326     
19327     place: function()
19328     {
19329         if(this.isInline) {
19330             return;
19331         }
19332         
19333         this.picker().removeClass(['bottom', 'top']);
19334         
19335         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19336             /*
19337              * place to the top of element!
19338              *
19339              */
19340             
19341             this.picker().addClass('top');
19342             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19343             
19344             return;
19345         }
19346         
19347         this.picker().addClass('bottom');
19348         
19349         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19350     },
19351     
19352     onFocus : function()
19353     {
19354         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19355         this.show();
19356     },
19357     
19358     onBlur : function()
19359     {
19360         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19361         
19362         var d = this.inputEl().getValue();
19363         
19364         this.setValue(d);
19365                 
19366         this.hide();
19367     },
19368     
19369     show : function()
19370     {
19371         this.picker().show();
19372         this.picker().select('>.datepicker-months', true).first().show();
19373         this.update();
19374         this.place();
19375         
19376         this.fireEvent('show', this, this.date);
19377     },
19378     
19379     hide : function()
19380     {
19381         if(this.isInline) {
19382             return;
19383         }
19384         this.picker().hide();
19385         this.fireEvent('hide', this, this.date);
19386         
19387     },
19388     
19389     onMousedown: function(e)
19390     {
19391         e.stopPropagation();
19392         e.preventDefault();
19393     },
19394     
19395     keyup: function(e)
19396     {
19397         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19398         this.update();
19399     },
19400
19401     fireKey: function(e)
19402     {
19403         if (!this.picker().isVisible()){
19404             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19405                 this.show();
19406             }
19407             return;
19408         }
19409         
19410         var dir;
19411         
19412         switch(e.keyCode){
19413             case 27: // escape
19414                 this.hide();
19415                 e.preventDefault();
19416                 break;
19417             case 37: // left
19418             case 39: // right
19419                 dir = e.keyCode == 37 ? -1 : 1;
19420                 
19421                 this.vIndex = this.vIndex + dir;
19422                 
19423                 if(this.vIndex < 0){
19424                     this.vIndex = 0;
19425                 }
19426                 
19427                 if(this.vIndex > 11){
19428                     this.vIndex = 11;
19429                 }
19430                 
19431                 if(isNaN(this.vIndex)){
19432                     this.vIndex = 0;
19433                 }
19434                 
19435                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19436                 
19437                 break;
19438             case 38: // up
19439             case 40: // down
19440                 
19441                 dir = e.keyCode == 38 ? -1 : 1;
19442                 
19443                 this.vIndex = this.vIndex + dir * 4;
19444                 
19445                 if(this.vIndex < 0){
19446                     this.vIndex = 0;
19447                 }
19448                 
19449                 if(this.vIndex > 11){
19450                     this.vIndex = 11;
19451                 }
19452                 
19453                 if(isNaN(this.vIndex)){
19454                     this.vIndex = 0;
19455                 }
19456                 
19457                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19458                 break;
19459                 
19460             case 13: // enter
19461                 
19462                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19463                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19464                 }
19465                 
19466                 this.hide();
19467                 e.preventDefault();
19468                 break;
19469             case 9: // tab
19470                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19471                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19472                 }
19473                 this.hide();
19474                 break;
19475             case 16: // shift
19476             case 17: // ctrl
19477             case 18: // alt
19478                 break;
19479             default :
19480                 this.hide();
19481                 
19482         }
19483     },
19484     
19485     remove: function() 
19486     {
19487         this.picker().remove();
19488     }
19489    
19490 });
19491
19492 Roo.apply(Roo.bootstrap.MonthField,  {
19493     
19494     content : {
19495         tag: 'tbody',
19496         cn: [
19497         {
19498             tag: 'tr',
19499             cn: [
19500             {
19501                 tag: 'td',
19502                 colspan: '7'
19503             }
19504             ]
19505         }
19506         ]
19507     },
19508     
19509     dates:{
19510         en: {
19511             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19512             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19513         }
19514     }
19515 });
19516
19517 Roo.apply(Roo.bootstrap.MonthField,  {
19518   
19519     template : {
19520         tag: 'div',
19521         cls: 'datepicker dropdown-menu roo-dynamic',
19522         cn: [
19523             {
19524                 tag: 'div',
19525                 cls: 'datepicker-months',
19526                 cn: [
19527                 {
19528                     tag: 'table',
19529                     cls: 'table-condensed',
19530                     cn:[
19531                         Roo.bootstrap.DateField.content
19532                     ]
19533                 }
19534                 ]
19535             }
19536         ]
19537     }
19538 });
19539
19540  
19541
19542  
19543  /*
19544  * - LGPL
19545  *
19546  * CheckBox
19547  * 
19548  */
19549
19550 /**
19551  * @class Roo.bootstrap.CheckBox
19552  * @extends Roo.bootstrap.Input
19553  * Bootstrap CheckBox class
19554  * 
19555  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19556  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19557  * @cfg {String} boxLabel The text that appears beside the checkbox
19558  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19559  * @cfg {Boolean} checked initnal the element
19560  * @cfg {Boolean} inline inline the element (default false)
19561  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19562  * 
19563  * @constructor
19564  * Create a new CheckBox
19565  * @param {Object} config The config object
19566  */
19567
19568 Roo.bootstrap.CheckBox = function(config){
19569     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19570    
19571     this.addEvents({
19572         /**
19573         * @event check
19574         * Fires when the element is checked or unchecked.
19575         * @param {Roo.bootstrap.CheckBox} this This input
19576         * @param {Boolean} checked The new checked value
19577         */
19578        check : true
19579     });
19580     
19581 };
19582
19583 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19584   
19585     inputType: 'checkbox',
19586     inputValue: 1,
19587     valueOff: 0,
19588     boxLabel: false,
19589     checked: false,
19590     weight : false,
19591     inline: false,
19592     
19593     getAutoCreate : function()
19594     {
19595         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19596         
19597         var id = Roo.id();
19598         
19599         var cfg = {};
19600         
19601         cfg.cls = 'form-group ' + this.inputType; //input-group
19602         
19603         if(this.inline){
19604             cfg.cls += ' ' + this.inputType + '-inline';
19605         }
19606         
19607         var input =  {
19608             tag: 'input',
19609             id : id,
19610             type : this.inputType,
19611             value : this.inputValue,
19612             cls : 'roo-' + this.inputType, //'form-box',
19613             placeholder : this.placeholder || ''
19614             
19615         };
19616         
19617         if(this.inputType != 'radio'){
19618             var hidden =  {
19619                 tag: 'input',
19620                 type : 'hidden',
19621                 cls : 'roo-hidden-value',
19622                 value : this.checked ? this.valueOff : this.inputValue
19623             };
19624         }
19625         
19626             
19627         if (this.weight) { // Validity check?
19628             cfg.cls += " " + this.inputType + "-" + this.weight;
19629         }
19630         
19631         if (this.disabled) {
19632             input.disabled=true;
19633         }
19634         
19635         if(this.checked){
19636             input.checked = this.checked;
19637             
19638         }
19639         
19640         
19641         if (this.name) {
19642             
19643             input.name = this.name;
19644             
19645             if(this.inputType != 'radio'){
19646                 hidden.name = this.name;
19647                 input.name = '_hidden_' + this.name;
19648             }
19649         }
19650         
19651         if (this.size) {
19652             input.cls += ' input-' + this.size;
19653         }
19654         
19655         var settings=this;
19656         
19657         ['xs','sm','md','lg'].map(function(size){
19658             if (settings[size]) {
19659                 cfg.cls += ' col-' + size + '-' + settings[size];
19660             }
19661         });
19662         
19663         var inputblock = input;
19664          
19665         if (this.before || this.after) {
19666             
19667             inputblock = {
19668                 cls : 'input-group',
19669                 cn :  [] 
19670             };
19671             
19672             if (this.before) {
19673                 inputblock.cn.push({
19674                     tag :'span',
19675                     cls : 'input-group-addon',
19676                     html : this.before
19677                 });
19678             }
19679             
19680             inputblock.cn.push(input);
19681             
19682             if(this.inputType != 'radio'){
19683                 inputblock.cn.push(hidden);
19684             }
19685             
19686             if (this.after) {
19687                 inputblock.cn.push({
19688                     tag :'span',
19689                     cls : 'input-group-addon',
19690                     html : this.after
19691                 });
19692             }
19693             
19694         }
19695         
19696         if (align ==='left' && this.fieldLabel.length) {
19697 //                Roo.log("left and has label");
19698                 cfg.cn = [
19699                     
19700                     {
19701                         tag: 'label',
19702                         'for' :  id,
19703                         cls : 'control-label col-md-' + this.labelWidth,
19704                         html : this.fieldLabel
19705                         
19706                     },
19707                     {
19708                         cls : "col-md-" + (12 - this.labelWidth), 
19709                         cn: [
19710                             inputblock
19711                         ]
19712                     }
19713                     
19714                 ];
19715         } else if ( this.fieldLabel.length) {
19716 //                Roo.log(" label");
19717                 cfg.cn = [
19718                    
19719                     {
19720                         tag: this.boxLabel ? 'span' : 'label',
19721                         'for': id,
19722                         cls: 'control-label box-input-label',
19723                         //cls : 'input-group-addon',
19724                         html : this.fieldLabel
19725                         
19726                     },
19727                     
19728                     inputblock
19729                     
19730                 ];
19731
19732         } else {
19733             
19734 //                Roo.log(" no label && no align");
19735                 cfg.cn = [  inputblock ] ;
19736                 
19737                 
19738         }
19739         
19740         if(this.boxLabel){
19741              var boxLabelCfg = {
19742                 tag: 'label',
19743                 //'for': id, // box label is handled by onclick - so no for...
19744                 cls: 'box-label',
19745                 html: this.boxLabel
19746             };
19747             
19748             if(this.tooltip){
19749                 boxLabelCfg.tooltip = this.tooltip;
19750             }
19751              
19752             cfg.cn.push(boxLabelCfg);
19753         }
19754         
19755         if(this.inputType != 'radio'){
19756             cfg.cn.push(hidden);
19757         }
19758         
19759         return cfg;
19760         
19761     },
19762     
19763     /**
19764      * return the real input element.
19765      */
19766     inputEl: function ()
19767     {
19768         return this.el.select('input.roo-' + this.inputType,true).first();
19769     },
19770     hiddenEl: function ()
19771     {
19772         return this.el.select('input.roo-hidden-value',true).first();
19773     },
19774     
19775     labelEl: function()
19776     {
19777         return this.el.select('label.control-label',true).first();
19778     },
19779     /* depricated... */
19780     
19781     label: function()
19782     {
19783         return this.labelEl();
19784     },
19785     
19786     boxLabelEl: function()
19787     {
19788         return this.el.select('label.box-label',true).first();
19789     },
19790     
19791     initEvents : function()
19792     {
19793 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19794         
19795         this.inputEl().on('click', this.onClick,  this);
19796         
19797         if (this.boxLabel) { 
19798             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19799         }
19800         
19801         this.startValue = this.getValue();
19802         
19803         if(this.groupId){
19804             Roo.bootstrap.CheckBox.register(this);
19805         }
19806     },
19807     
19808     onClick : function()
19809     {   
19810         this.setChecked(!this.checked);
19811     },
19812     
19813     setChecked : function(state,suppressEvent)
19814     {
19815         this.startValue = this.getValue();
19816
19817         if(this.inputType == 'radio'){
19818             
19819             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19820                 e.dom.checked = false;
19821             });
19822             
19823             this.inputEl().dom.checked = true;
19824             
19825             this.inputEl().dom.value = this.inputValue;
19826             
19827             if(suppressEvent !== true){
19828                 this.fireEvent('check', this, true);
19829             }
19830             
19831             this.validate();
19832             
19833             return;
19834         }
19835         
19836         this.checked = state;
19837         
19838         this.inputEl().dom.checked = state;
19839         
19840         
19841         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19842         
19843         if(suppressEvent !== true){
19844             this.fireEvent('check', this, state);
19845         }
19846         
19847         this.validate();
19848     },
19849     
19850     getValue : function()
19851     {
19852         if(this.inputType == 'radio'){
19853             return this.getGroupValue();
19854         }
19855         
19856         return this.hiddenEl().dom.value;
19857         
19858     },
19859     
19860     getGroupValue : function()
19861     {
19862         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19863             return '';
19864         }
19865         
19866         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19867     },
19868     
19869     setValue : function(v,suppressEvent)
19870     {
19871         if(this.inputType == 'radio'){
19872             this.setGroupValue(v, suppressEvent);
19873             return;
19874         }
19875         
19876         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19877         
19878         this.validate();
19879     },
19880     
19881     setGroupValue : function(v, suppressEvent)
19882     {
19883         this.startValue = this.getValue();
19884         
19885         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19886             e.dom.checked = false;
19887             
19888             if(e.dom.value == v){
19889                 e.dom.checked = true;
19890             }
19891         });
19892         
19893         if(suppressEvent !== true){
19894             this.fireEvent('check', this, true);
19895         }
19896
19897         this.validate();
19898         
19899         return;
19900     },
19901     
19902     validate : function()
19903     {
19904         if(
19905                 this.disabled || 
19906                 (this.inputType == 'radio' && this.validateRadio()) ||
19907                 (this.inputType == 'checkbox' && this.validateCheckbox())
19908         ){
19909             this.markValid();
19910             return true;
19911         }
19912         
19913         this.markInvalid();
19914         return false;
19915     },
19916     
19917     validateRadio : function()
19918     {
19919         if(this.allowBlank){
19920             return true;
19921         }
19922         
19923         var valid = false;
19924         
19925         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19926             if(!e.dom.checked){
19927                 return;
19928             }
19929             
19930             valid = true;
19931             
19932             return false;
19933         });
19934         
19935         return valid;
19936     },
19937     
19938     validateCheckbox : function()
19939     {
19940         if(!this.groupId){
19941             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19942         }
19943         
19944         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19945         
19946         if(!group){
19947             return false;
19948         }
19949         
19950         var r = false;
19951         
19952         for(var i in group){
19953             if(r){
19954                 break;
19955             }
19956             
19957             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19958         }
19959         
19960         return r;
19961     },
19962     
19963     /**
19964      * Mark this field as valid
19965      */
19966     markValid : function()
19967     {
19968         var _this = this;
19969         
19970         this.fireEvent('valid', this);
19971         
19972         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19973         
19974         if(this.groupId){
19975             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19976         }
19977         
19978         if(label){
19979             label.markValid();
19980         }
19981
19982         if(this.inputType == 'radio'){
19983             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19984                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19985                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19986             });
19987             
19988             return;
19989         }
19990         
19991         if(!this.groupId){
19992             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19993             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19994             return;
19995         }
19996         
19997         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19998             
19999         if(!group){
20000             return;
20001         }
20002         
20003         for(var i in group){
20004             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20005             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20006         }
20007     },
20008     
20009      /**
20010      * Mark this field as invalid
20011      * @param {String} msg The validation message
20012      */
20013     markInvalid : function(msg)
20014     {
20015         if(this.allowBlank){
20016             return;
20017         }
20018         
20019         var _this = this;
20020         
20021         this.fireEvent('invalid', this, msg);
20022         
20023         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20024         
20025         if(this.groupId){
20026             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20027         }
20028         
20029         if(label){
20030             label.markInvalid();
20031         }
20032             
20033         if(this.inputType == 'radio'){
20034             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20035                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20036                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20037             });
20038             
20039             return;
20040         }
20041         
20042         if(!this.groupId){
20043             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20044             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20045             return;
20046         }
20047         
20048         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20049         
20050         if(!group){
20051             return;
20052         }
20053         
20054         for(var i in group){
20055             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20056             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20057         }
20058         
20059     },
20060     
20061     clearInvalid : function()
20062     {
20063         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20064         
20065         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20066         
20067         if (label) {
20068             label.iconEl.removeClass(label.validClass);
20069             label.iconEl.removeClass(label.invalidClass);
20070         }
20071     },
20072     
20073     disable : function()
20074     {
20075         if(this.inputType != 'radio'){
20076             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20077             return;
20078         }
20079         
20080         var _this = this;
20081         
20082         if(this.rendered){
20083             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20084                 _this.getActionEl().addClass(this.disabledClass);
20085                 e.dom.disabled = true;
20086             });
20087         }
20088         
20089         this.disabled = true;
20090         this.fireEvent("disable", this);
20091         return this;
20092     },
20093
20094     enable : function()
20095     {
20096         if(this.inputType != 'radio'){
20097             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20098             return;
20099         }
20100         
20101         var _this = this;
20102         
20103         if(this.rendered){
20104             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20105                 _this.getActionEl().removeClass(this.disabledClass);
20106                 e.dom.disabled = false;
20107             });
20108         }
20109         
20110         this.disabled = false;
20111         this.fireEvent("enable", this);
20112         return this;
20113     }
20114
20115 });
20116
20117 Roo.apply(Roo.bootstrap.CheckBox, {
20118     
20119     groups: {},
20120     
20121      /**
20122     * register a CheckBox Group
20123     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20124     */
20125     register : function(checkbox)
20126     {
20127         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20128             this.groups[checkbox.groupId] = {};
20129         }
20130         
20131         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20132             return;
20133         }
20134         
20135         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20136         
20137     },
20138     /**
20139     * fetch a CheckBox Group based on the group ID
20140     * @param {string} the group ID
20141     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20142     */
20143     get: function(groupId) {
20144         if (typeof(this.groups[groupId]) == 'undefined') {
20145             return false;
20146         }
20147         
20148         return this.groups[groupId] ;
20149     }
20150     
20151     
20152 });
20153 /*
20154  * - LGPL
20155  *
20156  * Radio
20157  *
20158  *
20159  * not inline
20160  *<div class="radio">
20161   <label>
20162     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20163     Option one is this and that&mdash;be sure to include why it's great
20164   </label>
20165 </div>
20166  *
20167  *
20168  *inline
20169  *<span>
20170  *<label class="radio-inline">fieldLabel</label>
20171  *<label class="radio-inline">
20172   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20173 </label>
20174 <span>
20175  *
20176  *
20177  */
20178
20179 /**
20180  * @class Roo.bootstrap.Radio
20181  * @extends Roo.bootstrap.CheckBox
20182  * Bootstrap Radio class
20183
20184  * @constructor
20185  * Create a new Radio
20186  * @param {Object} config The config object
20187  */
20188
20189 Roo.bootstrap.Radio = function(config){
20190     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20191
20192 };
20193
20194 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20195
20196     inputType: 'radio',
20197     inputValue: '',
20198     valueOff: '',
20199
20200     getAutoCreate : function()
20201     {
20202         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20203         align = align || 'left'; // default...
20204
20205
20206
20207         var id = Roo.id();
20208
20209         var cfg = {
20210                 tag : this.inline ? 'span' : 'div',
20211                 cls : 'form-group',
20212                 cn : []
20213         };
20214
20215         var inline = this.inline ? ' radio-inline' : '';
20216
20217         var lbl = {
20218                 tag: 'label' ,
20219                 // does not need for, as we wrap the input with it..
20220                 'for' : id,
20221                 cls : 'control-label box-label' + inline,
20222                 cn : []
20223         };
20224         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20225
20226         var fieldLabel = {
20227             tag: 'label' ,
20228             //cls : 'control-label' + inline,
20229             html : this.fieldLabel,
20230             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20231         };
20232
20233         var input =  {
20234             tag: 'input',
20235             id : id,
20236             type : this.inputType,
20237             //value : (!this.checked) ? this.valueOff : this.inputValue,
20238             value : this.inputValue,
20239             cls : 'roo-radio',
20240             placeholder : this.placeholder || '' // ?? needed????
20241
20242         };
20243         if (this.weight) { // Validity check?
20244             input.cls += " radio-" + this.weight;
20245         }
20246         if (this.disabled) {
20247             input.disabled=true;
20248         }
20249
20250         if(this.checked){
20251             input.checked = this.checked;
20252         }
20253
20254         if (this.name) {
20255             input.name = this.name;
20256         }
20257
20258         if (this.size) {
20259             input.cls += ' input-' + this.size;
20260         }
20261
20262         //?? can span's inline have a width??
20263
20264         var settings=this;
20265         ['xs','sm','md','lg'].map(function(size){
20266             if (settings[size]) {
20267                 cfg.cls += ' col-' + size + '-' + settings[size];
20268             }
20269         });
20270
20271         var inputblock = input;
20272
20273         if (this.before || this.after) {
20274
20275             inputblock = {
20276                 cls : 'input-group',
20277                 tag : 'span',
20278                 cn :  []
20279             };
20280             if (this.before) {
20281                 inputblock.cn.push({
20282                     tag :'span',
20283                     cls : 'input-group-addon',
20284                     html : this.before
20285                 });
20286             }
20287             inputblock.cn.push(input);
20288             if (this.after) {
20289                 inputblock.cn.push({
20290                     tag :'span',
20291                     cls : 'input-group-addon',
20292                     html : this.after
20293                 });
20294             }
20295
20296         };
20297
20298
20299         if (this.fieldLabel && this.fieldLabel.length) {
20300             cfg.cn.push(fieldLabel);
20301         }
20302
20303         // normal bootstrap puts the input inside the label.
20304         // however with our styled version - it has to go after the input.
20305
20306         //lbl.cn.push(inputblock);
20307
20308         var lblwrap =  {
20309             tag: 'span',
20310             cls: 'radio' + inline,
20311             cn: [
20312                 inputblock,
20313                 lbl
20314             ]
20315         };
20316
20317         cfg.cn.push( lblwrap);
20318
20319         if(this.boxLabel){
20320             lbl.cn.push({
20321                 tag: 'span',
20322                 html: this.boxLabel
20323             })
20324         }
20325
20326
20327         return cfg;
20328
20329     },
20330
20331     initEvents : function()
20332     {
20333 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20334
20335         this.inputEl().on('click', this.onClick,  this);
20336         if (this.boxLabel) {
20337             //Roo.log('find label');
20338             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20339         }
20340
20341     },
20342
20343     inputEl: function ()
20344     {
20345         return this.el.select('input.roo-radio',true).first();
20346     },
20347     onClick : function()
20348     {
20349         Roo.log("click");
20350         this.setChecked(true);
20351     },
20352
20353     setChecked : function(state,suppressEvent)
20354     {
20355         if(state){
20356             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20357                 v.dom.checked = false;
20358             });
20359         }
20360         this.checked = state;
20361         this.inputEl().dom.checked = state;
20362
20363         if(suppressEvent !== true){
20364             this.fireEvent('check', this, state);
20365         }
20366         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20367         this.validate()
20368     },
20369
20370     getGroupValue : function()
20371     {
20372         var value = '';
20373         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20374             if(v.dom.checked == true){
20375                 value = v.dom.value;
20376             }
20377         });
20378
20379         return value;
20380     },
20381
20382     /**
20383      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20384      * @return {Mixed} value The field value
20385      */
20386     getValue : function(){
20387         return this.getGroupValue();
20388     }
20389
20390 });
20391 //<script type="text/javascript">
20392
20393 /*
20394  * Based  Ext JS Library 1.1.1
20395  * Copyright(c) 2006-2007, Ext JS, LLC.
20396  * LGPL
20397  *
20398  */
20399  
20400 /**
20401  * @class Roo.HtmlEditorCore
20402  * @extends Roo.Component
20403  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20404  *
20405  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20406  */
20407
20408 Roo.HtmlEditorCore = function(config){
20409     
20410     
20411     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20412     
20413     
20414     this.addEvents({
20415         /**
20416          * @event initialize
20417          * Fires when the editor is fully initialized (including the iframe)
20418          * @param {Roo.HtmlEditorCore} this
20419          */
20420         initialize: true,
20421         /**
20422          * @event activate
20423          * Fires when the editor is first receives the focus. Any insertion must wait
20424          * until after this event.
20425          * @param {Roo.HtmlEditorCore} this
20426          */
20427         activate: true,
20428          /**
20429          * @event beforesync
20430          * Fires before the textarea is updated with content from the editor iframe. Return false
20431          * to cancel the sync.
20432          * @param {Roo.HtmlEditorCore} this
20433          * @param {String} html
20434          */
20435         beforesync: true,
20436          /**
20437          * @event beforepush
20438          * Fires before the iframe editor is updated with content from the textarea. Return false
20439          * to cancel the push.
20440          * @param {Roo.HtmlEditorCore} this
20441          * @param {String} html
20442          */
20443         beforepush: true,
20444          /**
20445          * @event sync
20446          * Fires when the textarea is updated with content from the editor iframe.
20447          * @param {Roo.HtmlEditorCore} this
20448          * @param {String} html
20449          */
20450         sync: true,
20451          /**
20452          * @event push
20453          * Fires when the iframe editor is updated with content from the textarea.
20454          * @param {Roo.HtmlEditorCore} this
20455          * @param {String} html
20456          */
20457         push: true,
20458         
20459         /**
20460          * @event editorevent
20461          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20462          * @param {Roo.HtmlEditorCore} this
20463          */
20464         editorevent: true
20465         
20466     });
20467     
20468     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20469     
20470     // defaults : white / black...
20471     this.applyBlacklists();
20472     
20473     
20474     
20475 };
20476
20477
20478 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20479
20480
20481      /**
20482      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20483      */
20484     
20485     owner : false,
20486     
20487      /**
20488      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20489      *                        Roo.resizable.
20490      */
20491     resizable : false,
20492      /**
20493      * @cfg {Number} height (in pixels)
20494      */   
20495     height: 300,
20496    /**
20497      * @cfg {Number} width (in pixels)
20498      */   
20499     width: 500,
20500     
20501     /**
20502      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20503      * 
20504      */
20505     stylesheets: false,
20506     
20507     // id of frame..
20508     frameId: false,
20509     
20510     // private properties
20511     validationEvent : false,
20512     deferHeight: true,
20513     initialized : false,
20514     activated : false,
20515     sourceEditMode : false,
20516     onFocus : Roo.emptyFn,
20517     iframePad:3,
20518     hideMode:'offsets',
20519     
20520     clearUp: true,
20521     
20522     // blacklist + whitelisted elements..
20523     black: false,
20524     white: false,
20525      
20526     
20527
20528     /**
20529      * Protected method that will not generally be called directly. It
20530      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20531      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20532      */
20533     getDocMarkup : function(){
20534         // body styles..
20535         var st = '';
20536         
20537         // inherit styels from page...?? 
20538         if (this.stylesheets === false) {
20539             
20540             Roo.get(document.head).select('style').each(function(node) {
20541                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20542             });
20543             
20544             Roo.get(document.head).select('link').each(function(node) { 
20545                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20546             });
20547             
20548         } else if (!this.stylesheets.length) {
20549                 // simple..
20550                 st = '<style type="text/css">' +
20551                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20552                    '</style>';
20553         } else { 
20554             
20555         }
20556         
20557         st +=  '<style type="text/css">' +
20558             'IMG { cursor: pointer } ' +
20559         '</style>';
20560
20561         
20562         return '<html><head>' + st  +
20563             //<style type="text/css">' +
20564             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20565             //'</style>' +
20566             ' </head><body class="roo-htmleditor-body"></body></html>';
20567     },
20568
20569     // private
20570     onRender : function(ct, position)
20571     {
20572         var _t = this;
20573         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20574         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20575         
20576         
20577         this.el.dom.style.border = '0 none';
20578         this.el.dom.setAttribute('tabIndex', -1);
20579         this.el.addClass('x-hidden hide');
20580         
20581         
20582         
20583         if(Roo.isIE){ // fix IE 1px bogus margin
20584             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20585         }
20586        
20587         
20588         this.frameId = Roo.id();
20589         
20590          
20591         
20592         var iframe = this.owner.wrap.createChild({
20593             tag: 'iframe',
20594             cls: 'form-control', // bootstrap..
20595             id: this.frameId,
20596             name: this.frameId,
20597             frameBorder : 'no',
20598             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20599         }, this.el
20600         );
20601         
20602         
20603         this.iframe = iframe.dom;
20604
20605          this.assignDocWin();
20606         
20607         this.doc.designMode = 'on';
20608        
20609         this.doc.open();
20610         this.doc.write(this.getDocMarkup());
20611         this.doc.close();
20612
20613         
20614         var task = { // must defer to wait for browser to be ready
20615             run : function(){
20616                 //console.log("run task?" + this.doc.readyState);
20617                 this.assignDocWin();
20618                 if(this.doc.body || this.doc.readyState == 'complete'){
20619                     try {
20620                         this.doc.designMode="on";
20621                     } catch (e) {
20622                         return;
20623                     }
20624                     Roo.TaskMgr.stop(task);
20625                     this.initEditor.defer(10, this);
20626                 }
20627             },
20628             interval : 10,
20629             duration: 10000,
20630             scope: this
20631         };
20632         Roo.TaskMgr.start(task);
20633
20634     },
20635
20636     // private
20637     onResize : function(w, h)
20638     {
20639          Roo.log('resize: ' +w + ',' + h );
20640         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20641         if(!this.iframe){
20642             return;
20643         }
20644         if(typeof w == 'number'){
20645             
20646             this.iframe.style.width = w + 'px';
20647         }
20648         if(typeof h == 'number'){
20649             
20650             this.iframe.style.height = h + 'px';
20651             if(this.doc){
20652                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20653             }
20654         }
20655         
20656     },
20657
20658     /**
20659      * Toggles the editor between standard and source edit mode.
20660      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20661      */
20662     toggleSourceEdit : function(sourceEditMode){
20663         
20664         this.sourceEditMode = sourceEditMode === true;
20665         
20666         if(this.sourceEditMode){
20667  
20668             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20669             
20670         }else{
20671             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20672             //this.iframe.className = '';
20673             this.deferFocus();
20674         }
20675         //this.setSize(this.owner.wrap.getSize());
20676         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20677     },
20678
20679     
20680   
20681
20682     /**
20683      * Protected method that will not generally be called directly. If you need/want
20684      * custom HTML cleanup, this is the method you should override.
20685      * @param {String} html The HTML to be cleaned
20686      * return {String} The cleaned HTML
20687      */
20688     cleanHtml : function(html){
20689         html = String(html);
20690         if(html.length > 5){
20691             if(Roo.isSafari){ // strip safari nonsense
20692                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20693             }
20694         }
20695         if(html == '&nbsp;'){
20696             html = '';
20697         }
20698         return html;
20699     },
20700
20701     /**
20702      * HTML Editor -> Textarea
20703      * Protected method that will not generally be called directly. Syncs the contents
20704      * of the editor iframe with the textarea.
20705      */
20706     syncValue : function(){
20707         if(this.initialized){
20708             var bd = (this.doc.body || this.doc.documentElement);
20709             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20710             var html = bd.innerHTML;
20711             if(Roo.isSafari){
20712                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20713                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20714                 if(m && m[1]){
20715                     html = '<div style="'+m[0]+'">' + html + '</div>';
20716                 }
20717             }
20718             html = this.cleanHtml(html);
20719             // fix up the special chars.. normaly like back quotes in word...
20720             // however we do not want to do this with chinese..
20721             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20722                 var cc = b.charCodeAt();
20723                 if (
20724                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20725                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20726                     (cc >= 0xf900 && cc < 0xfb00 )
20727                 ) {
20728                         return b;
20729                 }
20730                 return "&#"+cc+";" 
20731             });
20732             if(this.owner.fireEvent('beforesync', this, html) !== false){
20733                 this.el.dom.value = html;
20734                 this.owner.fireEvent('sync', this, html);
20735             }
20736         }
20737     },
20738
20739     /**
20740      * Protected method that will not generally be called directly. Pushes the value of the textarea
20741      * into the iframe editor.
20742      */
20743     pushValue : function(){
20744         if(this.initialized){
20745             var v = this.el.dom.value.trim();
20746             
20747 //            if(v.length < 1){
20748 //                v = '&#160;';
20749 //            }
20750             
20751             if(this.owner.fireEvent('beforepush', this, v) !== false){
20752                 var d = (this.doc.body || this.doc.documentElement);
20753                 d.innerHTML = v;
20754                 this.cleanUpPaste();
20755                 this.el.dom.value = d.innerHTML;
20756                 this.owner.fireEvent('push', this, v);
20757             }
20758         }
20759     },
20760
20761     // private
20762     deferFocus : function(){
20763         this.focus.defer(10, this);
20764     },
20765
20766     // doc'ed in Field
20767     focus : function(){
20768         if(this.win && !this.sourceEditMode){
20769             this.win.focus();
20770         }else{
20771             this.el.focus();
20772         }
20773     },
20774     
20775     assignDocWin: function()
20776     {
20777         var iframe = this.iframe;
20778         
20779          if(Roo.isIE){
20780             this.doc = iframe.contentWindow.document;
20781             this.win = iframe.contentWindow;
20782         } else {
20783 //            if (!Roo.get(this.frameId)) {
20784 //                return;
20785 //            }
20786 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20787 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20788             
20789             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20790                 return;
20791             }
20792             
20793             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20794             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20795         }
20796     },
20797     
20798     // private
20799     initEditor : function(){
20800         //console.log("INIT EDITOR");
20801         this.assignDocWin();
20802         
20803         
20804         
20805         this.doc.designMode="on";
20806         this.doc.open();
20807         this.doc.write(this.getDocMarkup());
20808         this.doc.close();
20809         
20810         var dbody = (this.doc.body || this.doc.documentElement);
20811         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20812         // this copies styles from the containing element into thsi one..
20813         // not sure why we need all of this..
20814         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20815         
20816         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20817         //ss['background-attachment'] = 'fixed'; // w3c
20818         dbody.bgProperties = 'fixed'; // ie
20819         //Roo.DomHelper.applyStyles(dbody, ss);
20820         Roo.EventManager.on(this.doc, {
20821             //'mousedown': this.onEditorEvent,
20822             'mouseup': this.onEditorEvent,
20823             'dblclick': this.onEditorEvent,
20824             'click': this.onEditorEvent,
20825             'keyup': this.onEditorEvent,
20826             buffer:100,
20827             scope: this
20828         });
20829         if(Roo.isGecko){
20830             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20831         }
20832         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20833             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20834         }
20835         this.initialized = true;
20836
20837         this.owner.fireEvent('initialize', this);
20838         this.pushValue();
20839     },
20840
20841     // private
20842     onDestroy : function(){
20843         
20844         
20845         
20846         if(this.rendered){
20847             
20848             //for (var i =0; i < this.toolbars.length;i++) {
20849             //    // fixme - ask toolbars for heights?
20850             //    this.toolbars[i].onDestroy();
20851            // }
20852             
20853             //this.wrap.dom.innerHTML = '';
20854             //this.wrap.remove();
20855         }
20856     },
20857
20858     // private
20859     onFirstFocus : function(){
20860         
20861         this.assignDocWin();
20862         
20863         
20864         this.activated = true;
20865          
20866     
20867         if(Roo.isGecko){ // prevent silly gecko errors
20868             this.win.focus();
20869             var s = this.win.getSelection();
20870             if(!s.focusNode || s.focusNode.nodeType != 3){
20871                 var r = s.getRangeAt(0);
20872                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20873                 r.collapse(true);
20874                 this.deferFocus();
20875             }
20876             try{
20877                 this.execCmd('useCSS', true);
20878                 this.execCmd('styleWithCSS', false);
20879             }catch(e){}
20880         }
20881         this.owner.fireEvent('activate', this);
20882     },
20883
20884     // private
20885     adjustFont: function(btn){
20886         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20887         //if(Roo.isSafari){ // safari
20888         //    adjust *= 2;
20889        // }
20890         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20891         if(Roo.isSafari){ // safari
20892             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20893             v =  (v < 10) ? 10 : v;
20894             v =  (v > 48) ? 48 : v;
20895             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20896             
20897         }
20898         
20899         
20900         v = Math.max(1, v+adjust);
20901         
20902         this.execCmd('FontSize', v  );
20903     },
20904
20905     onEditorEvent : function(e)
20906     {
20907         this.owner.fireEvent('editorevent', this, e);
20908       //  this.updateToolbar();
20909         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20910     },
20911
20912     insertTag : function(tg)
20913     {
20914         // could be a bit smarter... -> wrap the current selected tRoo..
20915         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20916             
20917             range = this.createRange(this.getSelection());
20918             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20919             wrappingNode.appendChild(range.extractContents());
20920             range.insertNode(wrappingNode);
20921
20922             return;
20923             
20924             
20925             
20926         }
20927         this.execCmd("formatblock",   tg);
20928         
20929     },
20930     
20931     insertText : function(txt)
20932     {
20933         
20934         
20935         var range = this.createRange();
20936         range.deleteContents();
20937                //alert(Sender.getAttribute('label'));
20938                
20939         range.insertNode(this.doc.createTextNode(txt));
20940     } ,
20941     
20942      
20943
20944     /**
20945      * Executes a Midas editor command on the editor document and performs necessary focus and
20946      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20947      * @param {String} cmd The Midas command
20948      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20949      */
20950     relayCmd : function(cmd, value){
20951         this.win.focus();
20952         this.execCmd(cmd, value);
20953         this.owner.fireEvent('editorevent', this);
20954         //this.updateToolbar();
20955         this.owner.deferFocus();
20956     },
20957
20958     /**
20959      * Executes a Midas editor command directly on the editor document.
20960      * For visual commands, you should use {@link #relayCmd} instead.
20961      * <b>This should only be called after the editor is initialized.</b>
20962      * @param {String} cmd The Midas command
20963      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20964      */
20965     execCmd : function(cmd, value){
20966         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20967         this.syncValue();
20968     },
20969  
20970  
20971    
20972     /**
20973      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20974      * to insert tRoo.
20975      * @param {String} text | dom node.. 
20976      */
20977     insertAtCursor : function(text)
20978     {
20979         
20980         
20981         
20982         if(!this.activated){
20983             return;
20984         }
20985         /*
20986         if(Roo.isIE){
20987             this.win.focus();
20988             var r = this.doc.selection.createRange();
20989             if(r){
20990                 r.collapse(true);
20991                 r.pasteHTML(text);
20992                 this.syncValue();
20993                 this.deferFocus();
20994             
20995             }
20996             return;
20997         }
20998         */
20999         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21000             this.win.focus();
21001             
21002             
21003             // from jquery ui (MIT licenced)
21004             var range, node;
21005             var win = this.win;
21006             
21007             if (win.getSelection && win.getSelection().getRangeAt) {
21008                 range = win.getSelection().getRangeAt(0);
21009                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21010                 range.insertNode(node);
21011             } else if (win.document.selection && win.document.selection.createRange) {
21012                 // no firefox support
21013                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21014                 win.document.selection.createRange().pasteHTML(txt);
21015             } else {
21016                 // no firefox support
21017                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21018                 this.execCmd('InsertHTML', txt);
21019             } 
21020             
21021             this.syncValue();
21022             
21023             this.deferFocus();
21024         }
21025     },
21026  // private
21027     mozKeyPress : function(e){
21028         if(e.ctrlKey){
21029             var c = e.getCharCode(), cmd;
21030           
21031             if(c > 0){
21032                 c = String.fromCharCode(c).toLowerCase();
21033                 switch(c){
21034                     case 'b':
21035                         cmd = 'bold';
21036                         break;
21037                     case 'i':
21038                         cmd = 'italic';
21039                         break;
21040                     
21041                     case 'u':
21042                         cmd = 'underline';
21043                         break;
21044                     
21045                     case 'v':
21046                         this.cleanUpPaste.defer(100, this);
21047                         return;
21048                         
21049                 }
21050                 if(cmd){
21051                     this.win.focus();
21052                     this.execCmd(cmd);
21053                     this.deferFocus();
21054                     e.preventDefault();
21055                 }
21056                 
21057             }
21058         }
21059     },
21060
21061     // private
21062     fixKeys : function(){ // load time branching for fastest keydown performance
21063         if(Roo.isIE){
21064             return function(e){
21065                 var k = e.getKey(), r;
21066                 if(k == e.TAB){
21067                     e.stopEvent();
21068                     r = this.doc.selection.createRange();
21069                     if(r){
21070                         r.collapse(true);
21071                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21072                         this.deferFocus();
21073                     }
21074                     return;
21075                 }
21076                 
21077                 if(k == e.ENTER){
21078                     r = this.doc.selection.createRange();
21079                     if(r){
21080                         var target = r.parentElement();
21081                         if(!target || target.tagName.toLowerCase() != 'li'){
21082                             e.stopEvent();
21083                             r.pasteHTML('<br />');
21084                             r.collapse(false);
21085                             r.select();
21086                         }
21087                     }
21088                 }
21089                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21090                     this.cleanUpPaste.defer(100, this);
21091                     return;
21092                 }
21093                 
21094                 
21095             };
21096         }else if(Roo.isOpera){
21097             return function(e){
21098                 var k = e.getKey();
21099                 if(k == e.TAB){
21100                     e.stopEvent();
21101                     this.win.focus();
21102                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21103                     this.deferFocus();
21104                 }
21105                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21106                     this.cleanUpPaste.defer(100, this);
21107                     return;
21108                 }
21109                 
21110             };
21111         }else if(Roo.isSafari){
21112             return function(e){
21113                 var k = e.getKey();
21114                 
21115                 if(k == e.TAB){
21116                     e.stopEvent();
21117                     this.execCmd('InsertText','\t');
21118                     this.deferFocus();
21119                     return;
21120                 }
21121                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21122                     this.cleanUpPaste.defer(100, this);
21123                     return;
21124                 }
21125                 
21126              };
21127         }
21128     }(),
21129     
21130     getAllAncestors: function()
21131     {
21132         var p = this.getSelectedNode();
21133         var a = [];
21134         if (!p) {
21135             a.push(p); // push blank onto stack..
21136             p = this.getParentElement();
21137         }
21138         
21139         
21140         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21141             a.push(p);
21142             p = p.parentNode;
21143         }
21144         a.push(this.doc.body);
21145         return a;
21146     },
21147     lastSel : false,
21148     lastSelNode : false,
21149     
21150     
21151     getSelection : function() 
21152     {
21153         this.assignDocWin();
21154         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21155     },
21156     
21157     getSelectedNode: function() 
21158     {
21159         // this may only work on Gecko!!!
21160         
21161         // should we cache this!!!!
21162         
21163         
21164         
21165          
21166         var range = this.createRange(this.getSelection()).cloneRange();
21167         
21168         if (Roo.isIE) {
21169             var parent = range.parentElement();
21170             while (true) {
21171                 var testRange = range.duplicate();
21172                 testRange.moveToElementText(parent);
21173                 if (testRange.inRange(range)) {
21174                     break;
21175                 }
21176                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21177                     break;
21178                 }
21179                 parent = parent.parentElement;
21180             }
21181             return parent;
21182         }
21183         
21184         // is ancestor a text element.
21185         var ac =  range.commonAncestorContainer;
21186         if (ac.nodeType == 3) {
21187             ac = ac.parentNode;
21188         }
21189         
21190         var ar = ac.childNodes;
21191          
21192         var nodes = [];
21193         var other_nodes = [];
21194         var has_other_nodes = false;
21195         for (var i=0;i<ar.length;i++) {
21196             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21197                 continue;
21198             }
21199             // fullly contained node.
21200             
21201             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21202                 nodes.push(ar[i]);
21203                 continue;
21204             }
21205             
21206             // probably selected..
21207             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21208                 other_nodes.push(ar[i]);
21209                 continue;
21210             }
21211             // outer..
21212             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21213                 continue;
21214             }
21215             
21216             
21217             has_other_nodes = true;
21218         }
21219         if (!nodes.length && other_nodes.length) {
21220             nodes= other_nodes;
21221         }
21222         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21223             return false;
21224         }
21225         
21226         return nodes[0];
21227     },
21228     createRange: function(sel)
21229     {
21230         // this has strange effects when using with 
21231         // top toolbar - not sure if it's a great idea.
21232         //this.editor.contentWindow.focus();
21233         if (typeof sel != "undefined") {
21234             try {
21235                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21236             } catch(e) {
21237                 return this.doc.createRange();
21238             }
21239         } else {
21240             return this.doc.createRange();
21241         }
21242     },
21243     getParentElement: function()
21244     {
21245         
21246         this.assignDocWin();
21247         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21248         
21249         var range = this.createRange(sel);
21250          
21251         try {
21252             var p = range.commonAncestorContainer;
21253             while (p.nodeType == 3) { // text node
21254                 p = p.parentNode;
21255             }
21256             return p;
21257         } catch (e) {
21258             return null;
21259         }
21260     
21261     },
21262     /***
21263      *
21264      * Range intersection.. the hard stuff...
21265      *  '-1' = before
21266      *  '0' = hits..
21267      *  '1' = after.
21268      *         [ -- selected range --- ]
21269      *   [fail]                        [fail]
21270      *
21271      *    basically..
21272      *      if end is before start or  hits it. fail.
21273      *      if start is after end or hits it fail.
21274      *
21275      *   if either hits (but other is outside. - then it's not 
21276      *   
21277      *    
21278      **/
21279     
21280     
21281     // @see http://www.thismuchiknow.co.uk/?p=64.
21282     rangeIntersectsNode : function(range, node)
21283     {
21284         var nodeRange = node.ownerDocument.createRange();
21285         try {
21286             nodeRange.selectNode(node);
21287         } catch (e) {
21288             nodeRange.selectNodeContents(node);
21289         }
21290     
21291         var rangeStartRange = range.cloneRange();
21292         rangeStartRange.collapse(true);
21293     
21294         var rangeEndRange = range.cloneRange();
21295         rangeEndRange.collapse(false);
21296     
21297         var nodeStartRange = nodeRange.cloneRange();
21298         nodeStartRange.collapse(true);
21299     
21300         var nodeEndRange = nodeRange.cloneRange();
21301         nodeEndRange.collapse(false);
21302     
21303         return rangeStartRange.compareBoundaryPoints(
21304                  Range.START_TO_START, nodeEndRange) == -1 &&
21305                rangeEndRange.compareBoundaryPoints(
21306                  Range.START_TO_START, nodeStartRange) == 1;
21307         
21308          
21309     },
21310     rangeCompareNode : function(range, node)
21311     {
21312         var nodeRange = node.ownerDocument.createRange();
21313         try {
21314             nodeRange.selectNode(node);
21315         } catch (e) {
21316             nodeRange.selectNodeContents(node);
21317         }
21318         
21319         
21320         range.collapse(true);
21321     
21322         nodeRange.collapse(true);
21323      
21324         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21325         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21326          
21327         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21328         
21329         var nodeIsBefore   =  ss == 1;
21330         var nodeIsAfter    = ee == -1;
21331         
21332         if (nodeIsBefore && nodeIsAfter) {
21333             return 0; // outer
21334         }
21335         if (!nodeIsBefore && nodeIsAfter) {
21336             return 1; //right trailed.
21337         }
21338         
21339         if (nodeIsBefore && !nodeIsAfter) {
21340             return 2;  // left trailed.
21341         }
21342         // fully contined.
21343         return 3;
21344     },
21345
21346     // private? - in a new class?
21347     cleanUpPaste :  function()
21348     {
21349         // cleans up the whole document..
21350         Roo.log('cleanuppaste');
21351         
21352         this.cleanUpChildren(this.doc.body);
21353         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21354         if (clean != this.doc.body.innerHTML) {
21355             this.doc.body.innerHTML = clean;
21356         }
21357         
21358     },
21359     
21360     cleanWordChars : function(input) {// change the chars to hex code
21361         var he = Roo.HtmlEditorCore;
21362         
21363         var output = input;
21364         Roo.each(he.swapCodes, function(sw) { 
21365             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21366             
21367             output = output.replace(swapper, sw[1]);
21368         });
21369         
21370         return output;
21371     },
21372     
21373     
21374     cleanUpChildren : function (n)
21375     {
21376         if (!n.childNodes.length) {
21377             return;
21378         }
21379         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21380            this.cleanUpChild(n.childNodes[i]);
21381         }
21382     },
21383     
21384     
21385         
21386     
21387     cleanUpChild : function (node)
21388     {
21389         var ed = this;
21390         //console.log(node);
21391         if (node.nodeName == "#text") {
21392             // clean up silly Windows -- stuff?
21393             return; 
21394         }
21395         if (node.nodeName == "#comment") {
21396             node.parentNode.removeChild(node);
21397             // clean up silly Windows -- stuff?
21398             return; 
21399         }
21400         var lcname = node.tagName.toLowerCase();
21401         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21402         // whitelist of tags..
21403         
21404         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21405             // remove node.
21406             node.parentNode.removeChild(node);
21407             return;
21408             
21409         }
21410         
21411         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21412         
21413         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21414         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21415         
21416         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21417         //    remove_keep_children = true;
21418         //}
21419         
21420         if (remove_keep_children) {
21421             this.cleanUpChildren(node);
21422             // inserts everything just before this node...
21423             while (node.childNodes.length) {
21424                 var cn = node.childNodes[0];
21425                 node.removeChild(cn);
21426                 node.parentNode.insertBefore(cn, node);
21427             }
21428             node.parentNode.removeChild(node);
21429             return;
21430         }
21431         
21432         if (!node.attributes || !node.attributes.length) {
21433             this.cleanUpChildren(node);
21434             return;
21435         }
21436         
21437         function cleanAttr(n,v)
21438         {
21439             
21440             if (v.match(/^\./) || v.match(/^\//)) {
21441                 return;
21442             }
21443             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21444                 return;
21445             }
21446             if (v.match(/^#/)) {
21447                 return;
21448             }
21449 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21450             node.removeAttribute(n);
21451             
21452         }
21453         
21454         var cwhite = this.cwhite;
21455         var cblack = this.cblack;
21456             
21457         function cleanStyle(n,v)
21458         {
21459             if (v.match(/expression/)) { //XSS?? should we even bother..
21460                 node.removeAttribute(n);
21461                 return;
21462             }
21463             
21464             var parts = v.split(/;/);
21465             var clean = [];
21466             
21467             Roo.each(parts, function(p) {
21468                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21469                 if (!p.length) {
21470                     return true;
21471                 }
21472                 var l = p.split(':').shift().replace(/\s+/g,'');
21473                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21474                 
21475                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21476 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21477                     //node.removeAttribute(n);
21478                     return true;
21479                 }
21480                 //Roo.log()
21481                 // only allow 'c whitelisted system attributes'
21482                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21483 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21484                     //node.removeAttribute(n);
21485                     return true;
21486                 }
21487                 
21488                 
21489                  
21490                 
21491                 clean.push(p);
21492                 return true;
21493             });
21494             if (clean.length) { 
21495                 node.setAttribute(n, clean.join(';'));
21496             } else {
21497                 node.removeAttribute(n);
21498             }
21499             
21500         }
21501         
21502         
21503         for (var i = node.attributes.length-1; i > -1 ; i--) {
21504             var a = node.attributes[i];
21505             //console.log(a);
21506             
21507             if (a.name.toLowerCase().substr(0,2)=='on')  {
21508                 node.removeAttribute(a.name);
21509                 continue;
21510             }
21511             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21512                 node.removeAttribute(a.name);
21513                 continue;
21514             }
21515             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21516                 cleanAttr(a.name,a.value); // fixme..
21517                 continue;
21518             }
21519             if (a.name == 'style') {
21520                 cleanStyle(a.name,a.value);
21521                 continue;
21522             }
21523             /// clean up MS crap..
21524             // tecnically this should be a list of valid class'es..
21525             
21526             
21527             if (a.name == 'class') {
21528                 if (a.value.match(/^Mso/)) {
21529                     node.className = '';
21530                 }
21531                 
21532                 if (a.value.match(/body/)) {
21533                     node.className = '';
21534                 }
21535                 continue;
21536             }
21537             
21538             // style cleanup!?
21539             // class cleanup?
21540             
21541         }
21542         
21543         
21544         this.cleanUpChildren(node);
21545         
21546         
21547     },
21548     
21549     /**
21550      * Clean up MS wordisms...
21551      */
21552     cleanWord : function(node)
21553     {
21554         
21555         
21556         if (!node) {
21557             this.cleanWord(this.doc.body);
21558             return;
21559         }
21560         if (node.nodeName == "#text") {
21561             // clean up silly Windows -- stuff?
21562             return; 
21563         }
21564         if (node.nodeName == "#comment") {
21565             node.parentNode.removeChild(node);
21566             // clean up silly Windows -- stuff?
21567             return; 
21568         }
21569         
21570         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21571             node.parentNode.removeChild(node);
21572             return;
21573         }
21574         
21575         // remove - but keep children..
21576         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21577             while (node.childNodes.length) {
21578                 var cn = node.childNodes[0];
21579                 node.removeChild(cn);
21580                 node.parentNode.insertBefore(cn, node);
21581             }
21582             node.parentNode.removeChild(node);
21583             this.iterateChildren(node, this.cleanWord);
21584             return;
21585         }
21586         // clean styles
21587         if (node.className.length) {
21588             
21589             var cn = node.className.split(/\W+/);
21590             var cna = [];
21591             Roo.each(cn, function(cls) {
21592                 if (cls.match(/Mso[a-zA-Z]+/)) {
21593                     return;
21594                 }
21595                 cna.push(cls);
21596             });
21597             node.className = cna.length ? cna.join(' ') : '';
21598             if (!cna.length) {
21599                 node.removeAttribute("class");
21600             }
21601         }
21602         
21603         if (node.hasAttribute("lang")) {
21604             node.removeAttribute("lang");
21605         }
21606         
21607         if (node.hasAttribute("style")) {
21608             
21609             var styles = node.getAttribute("style").split(";");
21610             var nstyle = [];
21611             Roo.each(styles, function(s) {
21612                 if (!s.match(/:/)) {
21613                     return;
21614                 }
21615                 var kv = s.split(":");
21616                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21617                     return;
21618                 }
21619                 // what ever is left... we allow.
21620                 nstyle.push(s);
21621             });
21622             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21623             if (!nstyle.length) {
21624                 node.removeAttribute('style');
21625             }
21626         }
21627         this.iterateChildren(node, this.cleanWord);
21628         
21629         
21630         
21631     },
21632     /**
21633      * iterateChildren of a Node, calling fn each time, using this as the scole..
21634      * @param {DomNode} node node to iterate children of.
21635      * @param {Function} fn method of this class to call on each item.
21636      */
21637     iterateChildren : function(node, fn)
21638     {
21639         if (!node.childNodes.length) {
21640                 return;
21641         }
21642         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21643            fn.call(this, node.childNodes[i])
21644         }
21645     },
21646     
21647     
21648     /**
21649      * cleanTableWidths.
21650      *
21651      * Quite often pasting from word etc.. results in tables with column and widths.
21652      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21653      *
21654      */
21655     cleanTableWidths : function(node)
21656     {
21657          
21658          
21659         if (!node) {
21660             this.cleanTableWidths(this.doc.body);
21661             return;
21662         }
21663         
21664         // ignore list...
21665         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21666             return; 
21667         }
21668         Roo.log(node.tagName);
21669         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21670             this.iterateChildren(node, this.cleanTableWidths);
21671             return;
21672         }
21673         if (node.hasAttribute('width')) {
21674             node.removeAttribute('width');
21675         }
21676         
21677          
21678         if (node.hasAttribute("style")) {
21679             // pretty basic...
21680             
21681             var styles = node.getAttribute("style").split(";");
21682             var nstyle = [];
21683             Roo.each(styles, function(s) {
21684                 if (!s.match(/:/)) {
21685                     return;
21686                 }
21687                 var kv = s.split(":");
21688                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21689                     return;
21690                 }
21691                 // what ever is left... we allow.
21692                 nstyle.push(s);
21693             });
21694             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21695             if (!nstyle.length) {
21696                 node.removeAttribute('style');
21697             }
21698         }
21699         
21700         this.iterateChildren(node, this.cleanTableWidths);
21701         
21702         
21703     },
21704     
21705     
21706     
21707     
21708     domToHTML : function(currentElement, depth, nopadtext) {
21709         
21710         depth = depth || 0;
21711         nopadtext = nopadtext || false;
21712     
21713         if (!currentElement) {
21714             return this.domToHTML(this.doc.body);
21715         }
21716         
21717         //Roo.log(currentElement);
21718         var j;
21719         var allText = false;
21720         var nodeName = currentElement.nodeName;
21721         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21722         
21723         if  (nodeName == '#text') {
21724             
21725             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21726         }
21727         
21728         
21729         var ret = '';
21730         if (nodeName != 'BODY') {
21731              
21732             var i = 0;
21733             // Prints the node tagName, such as <A>, <IMG>, etc
21734             if (tagName) {
21735                 var attr = [];
21736                 for(i = 0; i < currentElement.attributes.length;i++) {
21737                     // quoting?
21738                     var aname = currentElement.attributes.item(i).name;
21739                     if (!currentElement.attributes.item(i).value.length) {
21740                         continue;
21741                     }
21742                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21743                 }
21744                 
21745                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21746             } 
21747             else {
21748                 
21749                 // eack
21750             }
21751         } else {
21752             tagName = false;
21753         }
21754         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21755             return ret;
21756         }
21757         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21758             nopadtext = true;
21759         }
21760         
21761         
21762         // Traverse the tree
21763         i = 0;
21764         var currentElementChild = currentElement.childNodes.item(i);
21765         var allText = true;
21766         var innerHTML  = '';
21767         lastnode = '';
21768         while (currentElementChild) {
21769             // Formatting code (indent the tree so it looks nice on the screen)
21770             var nopad = nopadtext;
21771             if (lastnode == 'SPAN') {
21772                 nopad  = true;
21773             }
21774             // text
21775             if  (currentElementChild.nodeName == '#text') {
21776                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21777                 toadd = nopadtext ? toadd : toadd.trim();
21778                 if (!nopad && toadd.length > 80) {
21779                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21780                 }
21781                 innerHTML  += toadd;
21782                 
21783                 i++;
21784                 currentElementChild = currentElement.childNodes.item(i);
21785                 lastNode = '';
21786                 continue;
21787             }
21788             allText = false;
21789             
21790             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21791                 
21792             // Recursively traverse the tree structure of the child node
21793             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21794             lastnode = currentElementChild.nodeName;
21795             i++;
21796             currentElementChild=currentElement.childNodes.item(i);
21797         }
21798         
21799         ret += innerHTML;
21800         
21801         if (!allText) {
21802                 // The remaining code is mostly for formatting the tree
21803             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21804         }
21805         
21806         
21807         if (tagName) {
21808             ret+= "</"+tagName+">";
21809         }
21810         return ret;
21811         
21812     },
21813         
21814     applyBlacklists : function()
21815     {
21816         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21817         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21818         
21819         this.white = [];
21820         this.black = [];
21821         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21822             if (b.indexOf(tag) > -1) {
21823                 return;
21824             }
21825             this.white.push(tag);
21826             
21827         }, this);
21828         
21829         Roo.each(w, function(tag) {
21830             if (b.indexOf(tag) > -1) {
21831                 return;
21832             }
21833             if (this.white.indexOf(tag) > -1) {
21834                 return;
21835             }
21836             this.white.push(tag);
21837             
21838         }, this);
21839         
21840         
21841         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21842             if (w.indexOf(tag) > -1) {
21843                 return;
21844             }
21845             this.black.push(tag);
21846             
21847         }, this);
21848         
21849         Roo.each(b, function(tag) {
21850             if (w.indexOf(tag) > -1) {
21851                 return;
21852             }
21853             if (this.black.indexOf(tag) > -1) {
21854                 return;
21855             }
21856             this.black.push(tag);
21857             
21858         }, this);
21859         
21860         
21861         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21862         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21863         
21864         this.cwhite = [];
21865         this.cblack = [];
21866         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21867             if (b.indexOf(tag) > -1) {
21868                 return;
21869             }
21870             this.cwhite.push(tag);
21871             
21872         }, this);
21873         
21874         Roo.each(w, function(tag) {
21875             if (b.indexOf(tag) > -1) {
21876                 return;
21877             }
21878             if (this.cwhite.indexOf(tag) > -1) {
21879                 return;
21880             }
21881             this.cwhite.push(tag);
21882             
21883         }, this);
21884         
21885         
21886         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21887             if (w.indexOf(tag) > -1) {
21888                 return;
21889             }
21890             this.cblack.push(tag);
21891             
21892         }, this);
21893         
21894         Roo.each(b, function(tag) {
21895             if (w.indexOf(tag) > -1) {
21896                 return;
21897             }
21898             if (this.cblack.indexOf(tag) > -1) {
21899                 return;
21900             }
21901             this.cblack.push(tag);
21902             
21903         }, this);
21904     },
21905     
21906     setStylesheets : function(stylesheets)
21907     {
21908         if(typeof(stylesheets) == 'string'){
21909             Roo.get(this.iframe.contentDocument.head).createChild({
21910                 tag : 'link',
21911                 rel : 'stylesheet',
21912                 type : 'text/css',
21913                 href : stylesheets
21914             });
21915             
21916             return;
21917         }
21918         var _this = this;
21919      
21920         Roo.each(stylesheets, function(s) {
21921             if(!s.length){
21922                 return;
21923             }
21924             
21925             Roo.get(_this.iframe.contentDocument.head).createChild({
21926                 tag : 'link',
21927                 rel : 'stylesheet',
21928                 type : 'text/css',
21929                 href : s
21930             });
21931         });
21932
21933         
21934     },
21935     
21936     removeStylesheets : function()
21937     {
21938         var _this = this;
21939         
21940         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21941             s.remove();
21942         });
21943     }
21944     
21945     // hide stuff that is not compatible
21946     /**
21947      * @event blur
21948      * @hide
21949      */
21950     /**
21951      * @event change
21952      * @hide
21953      */
21954     /**
21955      * @event focus
21956      * @hide
21957      */
21958     /**
21959      * @event specialkey
21960      * @hide
21961      */
21962     /**
21963      * @cfg {String} fieldClass @hide
21964      */
21965     /**
21966      * @cfg {String} focusClass @hide
21967      */
21968     /**
21969      * @cfg {String} autoCreate @hide
21970      */
21971     /**
21972      * @cfg {String} inputType @hide
21973      */
21974     /**
21975      * @cfg {String} invalidClass @hide
21976      */
21977     /**
21978      * @cfg {String} invalidText @hide
21979      */
21980     /**
21981      * @cfg {String} msgFx @hide
21982      */
21983     /**
21984      * @cfg {String} validateOnBlur @hide
21985      */
21986 });
21987
21988 Roo.HtmlEditorCore.white = [
21989         'area', 'br', 'img', 'input', 'hr', 'wbr',
21990         
21991        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21992        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21993        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21994        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21995        'table',   'ul',         'xmp', 
21996        
21997        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21998       'thead',   'tr', 
21999      
22000       'dir', 'menu', 'ol', 'ul', 'dl',
22001        
22002       'embed',  'object'
22003 ];
22004
22005
22006 Roo.HtmlEditorCore.black = [
22007     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22008         'applet', // 
22009         'base',   'basefont', 'bgsound', 'blink',  'body', 
22010         'frame',  'frameset', 'head',    'html',   'ilayer', 
22011         'iframe', 'layer',  'link',     'meta',    'object',   
22012         'script', 'style' ,'title',  'xml' // clean later..
22013 ];
22014 Roo.HtmlEditorCore.clean = [
22015     'script', 'style', 'title', 'xml'
22016 ];
22017 Roo.HtmlEditorCore.remove = [
22018     'font'
22019 ];
22020 // attributes..
22021
22022 Roo.HtmlEditorCore.ablack = [
22023     'on'
22024 ];
22025     
22026 Roo.HtmlEditorCore.aclean = [ 
22027     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22028 ];
22029
22030 // protocols..
22031 Roo.HtmlEditorCore.pwhite= [
22032         'http',  'https',  'mailto'
22033 ];
22034
22035 // white listed style attributes.
22036 Roo.HtmlEditorCore.cwhite= [
22037       //  'text-align', /// default is to allow most things..
22038       
22039          
22040 //        'font-size'//??
22041 ];
22042
22043 // black listed style attributes.
22044 Roo.HtmlEditorCore.cblack= [
22045       //  'font-size' -- this can be set by the project 
22046 ];
22047
22048
22049 Roo.HtmlEditorCore.swapCodes   =[ 
22050     [    8211, "--" ], 
22051     [    8212, "--" ], 
22052     [    8216,  "'" ],  
22053     [    8217, "'" ],  
22054     [    8220, '"' ],  
22055     [    8221, '"' ],  
22056     [    8226, "*" ],  
22057     [    8230, "..." ]
22058 ]; 
22059
22060     /*
22061  * - LGPL
22062  *
22063  * HtmlEditor
22064  * 
22065  */
22066
22067 /**
22068  * @class Roo.bootstrap.HtmlEditor
22069  * @extends Roo.bootstrap.TextArea
22070  * Bootstrap HtmlEditor class
22071
22072  * @constructor
22073  * Create a new HtmlEditor
22074  * @param {Object} config The config object
22075  */
22076
22077 Roo.bootstrap.HtmlEditor = function(config){
22078     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22079     if (!this.toolbars) {
22080         this.toolbars = [];
22081     }
22082     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22083     this.addEvents({
22084             /**
22085              * @event initialize
22086              * Fires when the editor is fully initialized (including the iframe)
22087              * @param {HtmlEditor} this
22088              */
22089             initialize: true,
22090             /**
22091              * @event activate
22092              * Fires when the editor is first receives the focus. Any insertion must wait
22093              * until after this event.
22094              * @param {HtmlEditor} this
22095              */
22096             activate: true,
22097              /**
22098              * @event beforesync
22099              * Fires before the textarea is updated with content from the editor iframe. Return false
22100              * to cancel the sync.
22101              * @param {HtmlEditor} this
22102              * @param {String} html
22103              */
22104             beforesync: true,
22105              /**
22106              * @event beforepush
22107              * Fires before the iframe editor is updated with content from the textarea. Return false
22108              * to cancel the push.
22109              * @param {HtmlEditor} this
22110              * @param {String} html
22111              */
22112             beforepush: true,
22113              /**
22114              * @event sync
22115              * Fires when the textarea is updated with content from the editor iframe.
22116              * @param {HtmlEditor} this
22117              * @param {String} html
22118              */
22119             sync: true,
22120              /**
22121              * @event push
22122              * Fires when the iframe editor is updated with content from the textarea.
22123              * @param {HtmlEditor} this
22124              * @param {String} html
22125              */
22126             push: true,
22127              /**
22128              * @event editmodechange
22129              * Fires when the editor switches edit modes
22130              * @param {HtmlEditor} this
22131              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22132              */
22133             editmodechange: true,
22134             /**
22135              * @event editorevent
22136              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22137              * @param {HtmlEditor} this
22138              */
22139             editorevent: true,
22140             /**
22141              * @event firstfocus
22142              * Fires when on first focus - needed by toolbars..
22143              * @param {HtmlEditor} this
22144              */
22145             firstfocus: true,
22146             /**
22147              * @event autosave
22148              * Auto save the htmlEditor value as a file into Events
22149              * @param {HtmlEditor} this
22150              */
22151             autosave: true,
22152             /**
22153              * @event savedpreview
22154              * preview the saved version of htmlEditor
22155              * @param {HtmlEditor} this
22156              */
22157             savedpreview: true
22158         });
22159 };
22160
22161
22162 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22163     
22164     
22165       /**
22166      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22167      */
22168     toolbars : false,
22169    
22170      /**
22171      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22172      *                        Roo.resizable.
22173      */
22174     resizable : false,
22175      /**
22176      * @cfg {Number} height (in pixels)
22177      */   
22178     height: 300,
22179    /**
22180      * @cfg {Number} width (in pixels)
22181      */   
22182     width: false,
22183     
22184     /**
22185      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22186      * 
22187      */
22188     stylesheets: false,
22189     
22190     // id of frame..
22191     frameId: false,
22192     
22193     // private properties
22194     validationEvent : false,
22195     deferHeight: true,
22196     initialized : false,
22197     activated : false,
22198     
22199     onFocus : Roo.emptyFn,
22200     iframePad:3,
22201     hideMode:'offsets',
22202     
22203     
22204     tbContainer : false,
22205     
22206     toolbarContainer :function() {
22207         return this.wrap.select('.x-html-editor-tb',true).first();
22208     },
22209
22210     /**
22211      * Protected method that will not generally be called directly. It
22212      * is called when the editor creates its toolbar. Override this method if you need to
22213      * add custom toolbar buttons.
22214      * @param {HtmlEditor} editor
22215      */
22216     createToolbar : function(){
22217         
22218         Roo.log("create toolbars");
22219         
22220         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22221         this.toolbars[0].render(this.toolbarContainer());
22222         
22223         return;
22224         
22225 //        if (!editor.toolbars || !editor.toolbars.length) {
22226 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22227 //        }
22228 //        
22229 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22230 //            editor.toolbars[i] = Roo.factory(
22231 //                    typeof(editor.toolbars[i]) == 'string' ?
22232 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22233 //                Roo.bootstrap.HtmlEditor);
22234 //            editor.toolbars[i].init(editor);
22235 //        }
22236     },
22237
22238      
22239     // private
22240     onRender : function(ct, position)
22241     {
22242        // Roo.log("Call onRender: " + this.xtype);
22243         var _t = this;
22244         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22245       
22246         this.wrap = this.inputEl().wrap({
22247             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22248         });
22249         
22250         this.editorcore.onRender(ct, position);
22251          
22252         if (this.resizable) {
22253             this.resizeEl = new Roo.Resizable(this.wrap, {
22254                 pinned : true,
22255                 wrap: true,
22256                 dynamic : true,
22257                 minHeight : this.height,
22258                 height: this.height,
22259                 handles : this.resizable,
22260                 width: this.width,
22261                 listeners : {
22262                     resize : function(r, w, h) {
22263                         _t.onResize(w,h); // -something
22264                     }
22265                 }
22266             });
22267             
22268         }
22269         this.createToolbar(this);
22270        
22271         
22272         if(!this.width && this.resizable){
22273             this.setSize(this.wrap.getSize());
22274         }
22275         if (this.resizeEl) {
22276             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22277             // should trigger onReize..
22278         }
22279         
22280     },
22281
22282     // private
22283     onResize : function(w, h)
22284     {
22285         Roo.log('resize: ' +w + ',' + h );
22286         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22287         var ew = false;
22288         var eh = false;
22289         
22290         if(this.inputEl() ){
22291             if(typeof w == 'number'){
22292                 var aw = w - this.wrap.getFrameWidth('lr');
22293                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22294                 ew = aw;
22295             }
22296             if(typeof h == 'number'){
22297                  var tbh = -11;  // fixme it needs to tool bar size!
22298                 for (var i =0; i < this.toolbars.length;i++) {
22299                     // fixme - ask toolbars for heights?
22300                     tbh += this.toolbars[i].el.getHeight();
22301                     //if (this.toolbars[i].footer) {
22302                     //    tbh += this.toolbars[i].footer.el.getHeight();
22303                     //}
22304                 }
22305               
22306                 
22307                 
22308                 
22309                 
22310                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22311                 ah -= 5; // knock a few pixes off for look..
22312                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22313                 var eh = ah;
22314             }
22315         }
22316         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22317         this.editorcore.onResize(ew,eh);
22318         
22319     },
22320
22321     /**
22322      * Toggles the editor between standard and source edit mode.
22323      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22324      */
22325     toggleSourceEdit : function(sourceEditMode)
22326     {
22327         this.editorcore.toggleSourceEdit(sourceEditMode);
22328         
22329         if(this.editorcore.sourceEditMode){
22330             Roo.log('editor - showing textarea');
22331             
22332 //            Roo.log('in');
22333 //            Roo.log(this.syncValue());
22334             this.syncValue();
22335             this.inputEl().removeClass(['hide', 'x-hidden']);
22336             this.inputEl().dom.removeAttribute('tabIndex');
22337             this.inputEl().focus();
22338         }else{
22339             Roo.log('editor - hiding textarea');
22340 //            Roo.log('out')
22341 //            Roo.log(this.pushValue()); 
22342             this.pushValue();
22343             
22344             this.inputEl().addClass(['hide', 'x-hidden']);
22345             this.inputEl().dom.setAttribute('tabIndex', -1);
22346             //this.deferFocus();
22347         }
22348          
22349         if(this.resizable){
22350             this.setSize(this.wrap.getSize());
22351         }
22352         
22353         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22354     },
22355  
22356     // private (for BoxComponent)
22357     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22358
22359     // private (for BoxComponent)
22360     getResizeEl : function(){
22361         return this.wrap;
22362     },
22363
22364     // private (for BoxComponent)
22365     getPositionEl : function(){
22366         return this.wrap;
22367     },
22368
22369     // private
22370     initEvents : function(){
22371         this.originalValue = this.getValue();
22372     },
22373
22374 //    /**
22375 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22376 //     * @method
22377 //     */
22378 //    markInvalid : Roo.emptyFn,
22379 //    /**
22380 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22381 //     * @method
22382 //     */
22383 //    clearInvalid : Roo.emptyFn,
22384
22385     setValue : function(v){
22386         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22387         this.editorcore.pushValue();
22388     },
22389
22390      
22391     // private
22392     deferFocus : function(){
22393         this.focus.defer(10, this);
22394     },
22395
22396     // doc'ed in Field
22397     focus : function(){
22398         this.editorcore.focus();
22399         
22400     },
22401       
22402
22403     // private
22404     onDestroy : function(){
22405         
22406         
22407         
22408         if(this.rendered){
22409             
22410             for (var i =0; i < this.toolbars.length;i++) {
22411                 // fixme - ask toolbars for heights?
22412                 this.toolbars[i].onDestroy();
22413             }
22414             
22415             this.wrap.dom.innerHTML = '';
22416             this.wrap.remove();
22417         }
22418     },
22419
22420     // private
22421     onFirstFocus : function(){
22422         //Roo.log("onFirstFocus");
22423         this.editorcore.onFirstFocus();
22424          for (var i =0; i < this.toolbars.length;i++) {
22425             this.toolbars[i].onFirstFocus();
22426         }
22427         
22428     },
22429     
22430     // private
22431     syncValue : function()
22432     {   
22433         this.editorcore.syncValue();
22434     },
22435     
22436     pushValue : function()
22437     {   
22438         this.editorcore.pushValue();
22439     }
22440      
22441     
22442     // hide stuff that is not compatible
22443     /**
22444      * @event blur
22445      * @hide
22446      */
22447     /**
22448      * @event change
22449      * @hide
22450      */
22451     /**
22452      * @event focus
22453      * @hide
22454      */
22455     /**
22456      * @event specialkey
22457      * @hide
22458      */
22459     /**
22460      * @cfg {String} fieldClass @hide
22461      */
22462     /**
22463      * @cfg {String} focusClass @hide
22464      */
22465     /**
22466      * @cfg {String} autoCreate @hide
22467      */
22468     /**
22469      * @cfg {String} inputType @hide
22470      */
22471     /**
22472      * @cfg {String} invalidClass @hide
22473      */
22474     /**
22475      * @cfg {String} invalidText @hide
22476      */
22477     /**
22478      * @cfg {String} msgFx @hide
22479      */
22480     /**
22481      * @cfg {String} validateOnBlur @hide
22482      */
22483 });
22484  
22485     
22486    
22487    
22488    
22489       
22490 Roo.namespace('Roo.bootstrap.htmleditor');
22491 /**
22492  * @class Roo.bootstrap.HtmlEditorToolbar1
22493  * Basic Toolbar
22494  * 
22495  * Usage:
22496  *
22497  new Roo.bootstrap.HtmlEditor({
22498     ....
22499     toolbars : [
22500         new Roo.bootstrap.HtmlEditorToolbar1({
22501             disable : { fonts: 1 , format: 1, ..., ... , ...],
22502             btns : [ .... ]
22503         })
22504     }
22505      
22506  * 
22507  * @cfg {Object} disable List of elements to disable..
22508  * @cfg {Array} btns List of additional buttons.
22509  * 
22510  * 
22511  * NEEDS Extra CSS? 
22512  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22513  */
22514  
22515 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22516 {
22517     
22518     Roo.apply(this, config);
22519     
22520     // default disabled, based on 'good practice'..
22521     this.disable = this.disable || {};
22522     Roo.applyIf(this.disable, {
22523         fontSize : true,
22524         colors : true,
22525         specialElements : true
22526     });
22527     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22528     
22529     this.editor = config.editor;
22530     this.editorcore = config.editor.editorcore;
22531     
22532     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22533     
22534     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22535     // dont call parent... till later.
22536 }
22537 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22538      
22539     bar : true,
22540     
22541     editor : false,
22542     editorcore : false,
22543     
22544     
22545     formats : [
22546         "p" ,  
22547         "h1","h2","h3","h4","h5","h6", 
22548         "pre", "code", 
22549         "abbr", "acronym", "address", "cite", "samp", "var",
22550         'div','span'
22551     ],
22552     
22553     onRender : function(ct, position)
22554     {
22555        // Roo.log("Call onRender: " + this.xtype);
22556         
22557        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22558        Roo.log(this.el);
22559        this.el.dom.style.marginBottom = '0';
22560        var _this = this;
22561        var editorcore = this.editorcore;
22562        var editor= this.editor;
22563        
22564        var children = [];
22565        var btn = function(id,cmd , toggle, handler){
22566        
22567             var  event = toggle ? 'toggle' : 'click';
22568        
22569             var a = {
22570                 size : 'sm',
22571                 xtype: 'Button',
22572                 xns: Roo.bootstrap,
22573                 glyphicon : id,
22574                 cmd : id || cmd,
22575                 enableToggle:toggle !== false,
22576                 //html : 'submit'
22577                 pressed : toggle ? false : null,
22578                 listeners : {}
22579             };
22580             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22581                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22582             };
22583             children.push(a);
22584             return a;
22585        }
22586         
22587         var style = {
22588                 xtype: 'Button',
22589                 size : 'sm',
22590                 xns: Roo.bootstrap,
22591                 glyphicon : 'font',
22592                 //html : 'submit'
22593                 menu : {
22594                     xtype: 'Menu',
22595                     xns: Roo.bootstrap,
22596                     items:  []
22597                 }
22598         };
22599         Roo.each(this.formats, function(f) {
22600             style.menu.items.push({
22601                 xtype :'MenuItem',
22602                 xns: Roo.bootstrap,
22603                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22604                 tagname : f,
22605                 listeners : {
22606                     click : function()
22607                     {
22608                         editorcore.insertTag(this.tagname);
22609                         editor.focus();
22610                     }
22611                 }
22612                 
22613             });
22614         });
22615          children.push(style);   
22616             
22617             
22618         btn('bold',false,true);
22619         btn('italic',false,true);
22620         btn('align-left', 'justifyleft',true);
22621         btn('align-center', 'justifycenter',true);
22622         btn('align-right' , 'justifyright',true);
22623         btn('link', false, false, function(btn) {
22624             //Roo.log("create link?");
22625             var url = prompt(this.createLinkText, this.defaultLinkValue);
22626             if(url && url != 'http:/'+'/'){
22627                 this.editorcore.relayCmd('createlink', url);
22628             }
22629         }),
22630         btn('list','insertunorderedlist',true);
22631         btn('pencil', false,true, function(btn){
22632                 Roo.log(this);
22633                 
22634                 this.toggleSourceEdit(btn.pressed);
22635         });
22636         /*
22637         var cog = {
22638                 xtype: 'Button',
22639                 size : 'sm',
22640                 xns: Roo.bootstrap,
22641                 glyphicon : 'cog',
22642                 //html : 'submit'
22643                 menu : {
22644                     xtype: 'Menu',
22645                     xns: Roo.bootstrap,
22646                     items:  []
22647                 }
22648         };
22649         
22650         cog.menu.items.push({
22651             xtype :'MenuItem',
22652             xns: Roo.bootstrap,
22653             html : Clean styles,
22654             tagname : f,
22655             listeners : {
22656                 click : function()
22657                 {
22658                     editorcore.insertTag(this.tagname);
22659                     editor.focus();
22660                 }
22661             }
22662             
22663         });
22664        */
22665         
22666          
22667        this.xtype = 'NavSimplebar';
22668         
22669         for(var i=0;i< children.length;i++) {
22670             
22671             this.buttons.add(this.addxtypeChild(children[i]));
22672             
22673         }
22674         
22675         editor.on('editorevent', this.updateToolbar, this);
22676     },
22677     onBtnClick : function(id)
22678     {
22679        this.editorcore.relayCmd(id);
22680        this.editorcore.focus();
22681     },
22682     
22683     /**
22684      * Protected method that will not generally be called directly. It triggers
22685      * a toolbar update by reading the markup state of the current selection in the editor.
22686      */
22687     updateToolbar: function(){
22688
22689         if(!this.editorcore.activated){
22690             this.editor.onFirstFocus(); // is this neeed?
22691             return;
22692         }
22693
22694         var btns = this.buttons; 
22695         var doc = this.editorcore.doc;
22696         btns.get('bold').setActive(doc.queryCommandState('bold'));
22697         btns.get('italic').setActive(doc.queryCommandState('italic'));
22698         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22699         
22700         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22701         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22702         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22703         
22704         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22705         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22706          /*
22707         
22708         var ans = this.editorcore.getAllAncestors();
22709         if (this.formatCombo) {
22710             
22711             
22712             var store = this.formatCombo.store;
22713             this.formatCombo.setValue("");
22714             for (var i =0; i < ans.length;i++) {
22715                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22716                     // select it..
22717                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22718                     break;
22719                 }
22720             }
22721         }
22722         
22723         
22724         
22725         // hides menus... - so this cant be on a menu...
22726         Roo.bootstrap.MenuMgr.hideAll();
22727         */
22728         Roo.bootstrap.MenuMgr.hideAll();
22729         //this.editorsyncValue();
22730     },
22731     onFirstFocus: function() {
22732         this.buttons.each(function(item){
22733            item.enable();
22734         });
22735     },
22736     toggleSourceEdit : function(sourceEditMode){
22737         
22738           
22739         if(sourceEditMode){
22740             Roo.log("disabling buttons");
22741            this.buttons.each( function(item){
22742                 if(item.cmd != 'pencil'){
22743                     item.disable();
22744                 }
22745             });
22746           
22747         }else{
22748             Roo.log("enabling buttons");
22749             if(this.editorcore.initialized){
22750                 this.buttons.each( function(item){
22751                     item.enable();
22752                 });
22753             }
22754             
22755         }
22756         Roo.log("calling toggole on editor");
22757         // tell the editor that it's been pressed..
22758         this.editor.toggleSourceEdit(sourceEditMode);
22759        
22760     }
22761 });
22762
22763
22764
22765
22766
22767 /**
22768  * @class Roo.bootstrap.Table.AbstractSelectionModel
22769  * @extends Roo.util.Observable
22770  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22771  * implemented by descendant classes.  This class should not be directly instantiated.
22772  * @constructor
22773  */
22774 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22775     this.locked = false;
22776     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22777 };
22778
22779
22780 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22781     /** @ignore Called by the grid automatically. Do not call directly. */
22782     init : function(grid){
22783         this.grid = grid;
22784         this.initEvents();
22785     },
22786
22787     /**
22788      * Locks the selections.
22789      */
22790     lock : function(){
22791         this.locked = true;
22792     },
22793
22794     /**
22795      * Unlocks the selections.
22796      */
22797     unlock : function(){
22798         this.locked = false;
22799     },
22800
22801     /**
22802      * Returns true if the selections are locked.
22803      * @return {Boolean}
22804      */
22805     isLocked : function(){
22806         return this.locked;
22807     }
22808 });
22809 /**
22810  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22811  * @class Roo.bootstrap.Table.RowSelectionModel
22812  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22813  * It supports multiple selections and keyboard selection/navigation. 
22814  * @constructor
22815  * @param {Object} config
22816  */
22817
22818 Roo.bootstrap.Table.RowSelectionModel = function(config){
22819     Roo.apply(this, config);
22820     this.selections = new Roo.util.MixedCollection(false, function(o){
22821         return o.id;
22822     });
22823
22824     this.last = false;
22825     this.lastActive = false;
22826
22827     this.addEvents({
22828         /**
22829              * @event selectionchange
22830              * Fires when the selection changes
22831              * @param {SelectionModel} this
22832              */
22833             "selectionchange" : true,
22834         /**
22835              * @event afterselectionchange
22836              * Fires after the selection changes (eg. by key press or clicking)
22837              * @param {SelectionModel} this
22838              */
22839             "afterselectionchange" : true,
22840         /**
22841              * @event beforerowselect
22842              * Fires when a row is selected being selected, return false to cancel.
22843              * @param {SelectionModel} this
22844              * @param {Number} rowIndex The selected index
22845              * @param {Boolean} keepExisting False if other selections will be cleared
22846              */
22847             "beforerowselect" : true,
22848         /**
22849              * @event rowselect
22850              * Fires when a row is selected.
22851              * @param {SelectionModel} this
22852              * @param {Number} rowIndex The selected index
22853              * @param {Roo.data.Record} r The record
22854              */
22855             "rowselect" : true,
22856         /**
22857              * @event rowdeselect
22858              * Fires when a row is deselected.
22859              * @param {SelectionModel} this
22860              * @param {Number} rowIndex The selected index
22861              */
22862         "rowdeselect" : true
22863     });
22864     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22865     this.locked = false;
22866  };
22867
22868 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22869     /**
22870      * @cfg {Boolean} singleSelect
22871      * True to allow selection of only one row at a time (defaults to false)
22872      */
22873     singleSelect : false,
22874
22875     // private
22876     initEvents : function()
22877     {
22878
22879         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22880         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22881         //}else{ // allow click to work like normal
22882          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22883         //}
22884         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22885         this.grid.on("rowclick", this.handleMouseDown, this);
22886         
22887         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22888             "up" : function(e){
22889                 if(!e.shiftKey){
22890                     this.selectPrevious(e.shiftKey);
22891                 }else if(this.last !== false && this.lastActive !== false){
22892                     var last = this.last;
22893                     this.selectRange(this.last,  this.lastActive-1);
22894                     this.grid.getView().focusRow(this.lastActive);
22895                     if(last !== false){
22896                         this.last = last;
22897                     }
22898                 }else{
22899                     this.selectFirstRow();
22900                 }
22901                 this.fireEvent("afterselectionchange", this);
22902             },
22903             "down" : function(e){
22904                 if(!e.shiftKey){
22905                     this.selectNext(e.shiftKey);
22906                 }else if(this.last !== false && this.lastActive !== false){
22907                     var last = this.last;
22908                     this.selectRange(this.last,  this.lastActive+1);
22909                     this.grid.getView().focusRow(this.lastActive);
22910                     if(last !== false){
22911                         this.last = last;
22912                     }
22913                 }else{
22914                     this.selectFirstRow();
22915                 }
22916                 this.fireEvent("afterselectionchange", this);
22917             },
22918             scope: this
22919         });
22920         this.grid.store.on('load', function(){
22921             this.selections.clear();
22922         },this);
22923         /*
22924         var view = this.grid.view;
22925         view.on("refresh", this.onRefresh, this);
22926         view.on("rowupdated", this.onRowUpdated, this);
22927         view.on("rowremoved", this.onRemove, this);
22928         */
22929     },
22930
22931     // private
22932     onRefresh : function()
22933     {
22934         var ds = this.grid.store, i, v = this.grid.view;
22935         var s = this.selections;
22936         s.each(function(r){
22937             if((i = ds.indexOfId(r.id)) != -1){
22938                 v.onRowSelect(i);
22939             }else{
22940                 s.remove(r);
22941             }
22942         });
22943     },
22944
22945     // private
22946     onRemove : function(v, index, r){
22947         this.selections.remove(r);
22948     },
22949
22950     // private
22951     onRowUpdated : function(v, index, r){
22952         if(this.isSelected(r)){
22953             v.onRowSelect(index);
22954         }
22955     },
22956
22957     /**
22958      * Select records.
22959      * @param {Array} records The records to select
22960      * @param {Boolean} keepExisting (optional) True to keep existing selections
22961      */
22962     selectRecords : function(records, keepExisting)
22963     {
22964         if(!keepExisting){
22965             this.clearSelections();
22966         }
22967             var ds = this.grid.store;
22968         for(var i = 0, len = records.length; i < len; i++){
22969             this.selectRow(ds.indexOf(records[i]), true);
22970         }
22971     },
22972
22973     /**
22974      * Gets the number of selected rows.
22975      * @return {Number}
22976      */
22977     getCount : function(){
22978         return this.selections.length;
22979     },
22980
22981     /**
22982      * Selects the first row in the grid.
22983      */
22984     selectFirstRow : function(){
22985         this.selectRow(0);
22986     },
22987
22988     /**
22989      * Select the last row.
22990      * @param {Boolean} keepExisting (optional) True to keep existing selections
22991      */
22992     selectLastRow : function(keepExisting){
22993         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22994         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22995     },
22996
22997     /**
22998      * Selects the row immediately following the last selected row.
22999      * @param {Boolean} keepExisting (optional) True to keep existing selections
23000      */
23001     selectNext : function(keepExisting)
23002     {
23003             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23004             this.selectRow(this.last+1, keepExisting);
23005             this.grid.getView().focusRow(this.last);
23006         }
23007     },
23008
23009     /**
23010      * Selects the row that precedes the last selected row.
23011      * @param {Boolean} keepExisting (optional) True to keep existing selections
23012      */
23013     selectPrevious : function(keepExisting){
23014         if(this.last){
23015             this.selectRow(this.last-1, keepExisting);
23016             this.grid.getView().focusRow(this.last);
23017         }
23018     },
23019
23020     /**
23021      * Returns the selected records
23022      * @return {Array} Array of selected records
23023      */
23024     getSelections : function(){
23025         return [].concat(this.selections.items);
23026     },
23027
23028     /**
23029      * Returns the first selected record.
23030      * @return {Record}
23031      */
23032     getSelected : function(){
23033         return this.selections.itemAt(0);
23034     },
23035
23036
23037     /**
23038      * Clears all selections.
23039      */
23040     clearSelections : function(fast)
23041     {
23042         if(this.locked) {
23043             return;
23044         }
23045         if(fast !== true){
23046                 var ds = this.grid.store;
23047             var s = this.selections;
23048             s.each(function(r){
23049                 this.deselectRow(ds.indexOfId(r.id));
23050             }, this);
23051             s.clear();
23052         }else{
23053             this.selections.clear();
23054         }
23055         this.last = false;
23056     },
23057
23058
23059     /**
23060      * Selects all rows.
23061      */
23062     selectAll : function(){
23063         if(this.locked) {
23064             return;
23065         }
23066         this.selections.clear();
23067         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23068             this.selectRow(i, true);
23069         }
23070     },
23071
23072     /**
23073      * Returns True if there is a selection.
23074      * @return {Boolean}
23075      */
23076     hasSelection : function(){
23077         return this.selections.length > 0;
23078     },
23079
23080     /**
23081      * Returns True if the specified row is selected.
23082      * @param {Number/Record} record The record or index of the record to check
23083      * @return {Boolean}
23084      */
23085     isSelected : function(index){
23086             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23087         return (r && this.selections.key(r.id) ? true : false);
23088     },
23089
23090     /**
23091      * Returns True if the specified record id is selected.
23092      * @param {String} id The id of record to check
23093      * @return {Boolean}
23094      */
23095     isIdSelected : function(id){
23096         return (this.selections.key(id) ? true : false);
23097     },
23098
23099
23100     // private
23101     handleMouseDBClick : function(e, t){
23102         
23103     },
23104     // private
23105     handleMouseDown : function(e, t)
23106     {
23107             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23108         if(this.isLocked() || rowIndex < 0 ){
23109             return;
23110         };
23111         if(e.shiftKey && this.last !== false){
23112             var last = this.last;
23113             this.selectRange(last, rowIndex, e.ctrlKey);
23114             this.last = last; // reset the last
23115             t.focus();
23116     
23117         }else{
23118             var isSelected = this.isSelected(rowIndex);
23119             //Roo.log("select row:" + rowIndex);
23120             if(isSelected){
23121                 this.deselectRow(rowIndex);
23122             } else {
23123                         this.selectRow(rowIndex, true);
23124             }
23125     
23126             /*
23127                 if(e.button !== 0 && isSelected){
23128                 alert('rowIndex 2: ' + rowIndex);
23129                     view.focusRow(rowIndex);
23130                 }else if(e.ctrlKey && isSelected){
23131                     this.deselectRow(rowIndex);
23132                 }else if(!isSelected){
23133                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23134                     view.focusRow(rowIndex);
23135                 }
23136             */
23137         }
23138         this.fireEvent("afterselectionchange", this);
23139     },
23140     // private
23141     handleDragableRowClick :  function(grid, rowIndex, e) 
23142     {
23143         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23144             this.selectRow(rowIndex, false);
23145             grid.view.focusRow(rowIndex);
23146              this.fireEvent("afterselectionchange", this);
23147         }
23148     },
23149     
23150     /**
23151      * Selects multiple rows.
23152      * @param {Array} rows Array of the indexes of the row to select
23153      * @param {Boolean} keepExisting (optional) True to keep existing selections
23154      */
23155     selectRows : function(rows, keepExisting){
23156         if(!keepExisting){
23157             this.clearSelections();
23158         }
23159         for(var i = 0, len = rows.length; i < len; i++){
23160             this.selectRow(rows[i], true);
23161         }
23162     },
23163
23164     /**
23165      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23166      * @param {Number} startRow The index of the first row in the range
23167      * @param {Number} endRow The index of the last row in the range
23168      * @param {Boolean} keepExisting (optional) True to retain existing selections
23169      */
23170     selectRange : function(startRow, endRow, keepExisting){
23171         if(this.locked) {
23172             return;
23173         }
23174         if(!keepExisting){
23175             this.clearSelections();
23176         }
23177         if(startRow <= endRow){
23178             for(var i = startRow; i <= endRow; i++){
23179                 this.selectRow(i, true);
23180             }
23181         }else{
23182             for(var i = startRow; i >= endRow; i--){
23183                 this.selectRow(i, true);
23184             }
23185         }
23186     },
23187
23188     /**
23189      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23190      * @param {Number} startRow The index of the first row in the range
23191      * @param {Number} endRow The index of the last row in the range
23192      */
23193     deselectRange : function(startRow, endRow, preventViewNotify){
23194         if(this.locked) {
23195             return;
23196         }
23197         for(var i = startRow; i <= endRow; i++){
23198             this.deselectRow(i, preventViewNotify);
23199         }
23200     },
23201
23202     /**
23203      * Selects a row.
23204      * @param {Number} row The index of the row to select
23205      * @param {Boolean} keepExisting (optional) True to keep existing selections
23206      */
23207     selectRow : function(index, keepExisting, preventViewNotify)
23208     {
23209             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23210             return;
23211         }
23212         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23213             if(!keepExisting || this.singleSelect){
23214                 this.clearSelections();
23215             }
23216             
23217             var r = this.grid.store.getAt(index);
23218             //console.log('selectRow - record id :' + r.id);
23219             
23220             this.selections.add(r);
23221             this.last = this.lastActive = index;
23222             if(!preventViewNotify){
23223                 var proxy = new Roo.Element(
23224                                 this.grid.getRowDom(index)
23225                 );
23226                 proxy.addClass('bg-info info');
23227             }
23228             this.fireEvent("rowselect", this, index, r);
23229             this.fireEvent("selectionchange", this);
23230         }
23231     },
23232
23233     /**
23234      * Deselects a row.
23235      * @param {Number} row The index of the row to deselect
23236      */
23237     deselectRow : function(index, preventViewNotify)
23238     {
23239         if(this.locked) {
23240             return;
23241         }
23242         if(this.last == index){
23243             this.last = false;
23244         }
23245         if(this.lastActive == index){
23246             this.lastActive = false;
23247         }
23248         
23249         var r = this.grid.store.getAt(index);
23250         if (!r) {
23251             return;
23252         }
23253         
23254         this.selections.remove(r);
23255         //.console.log('deselectRow - record id :' + r.id);
23256         if(!preventViewNotify){
23257         
23258             var proxy = new Roo.Element(
23259                 this.grid.getRowDom(index)
23260             );
23261             proxy.removeClass('bg-info info');
23262         }
23263         this.fireEvent("rowdeselect", this, index);
23264         this.fireEvent("selectionchange", this);
23265     },
23266
23267     // private
23268     restoreLast : function(){
23269         if(this._last){
23270             this.last = this._last;
23271         }
23272     },
23273
23274     // private
23275     acceptsNav : function(row, col, cm){
23276         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23277     },
23278
23279     // private
23280     onEditorKey : function(field, e){
23281         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23282         if(k == e.TAB){
23283             e.stopEvent();
23284             ed.completeEdit();
23285             if(e.shiftKey){
23286                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23287             }else{
23288                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23289             }
23290         }else if(k == e.ENTER && !e.ctrlKey){
23291             e.stopEvent();
23292             ed.completeEdit();
23293             if(e.shiftKey){
23294                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23295             }else{
23296                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23297             }
23298         }else if(k == e.ESC){
23299             ed.cancelEdit();
23300         }
23301         if(newCell){
23302             g.startEditing(newCell[0], newCell[1]);
23303         }
23304     }
23305 });
23306 /*
23307  * Based on:
23308  * Ext JS Library 1.1.1
23309  * Copyright(c) 2006-2007, Ext JS, LLC.
23310  *
23311  * Originally Released Under LGPL - original licence link has changed is not relivant.
23312  *
23313  * Fork - LGPL
23314  * <script type="text/javascript">
23315  */
23316  
23317 /**
23318  * @class Roo.bootstrap.PagingToolbar
23319  * @extends Roo.bootstrap.NavSimplebar
23320  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23321  * @constructor
23322  * Create a new PagingToolbar
23323  * @param {Object} config The config object
23324  * @param {Roo.data.Store} store
23325  */
23326 Roo.bootstrap.PagingToolbar = function(config)
23327 {
23328     // old args format still supported... - xtype is prefered..
23329         // created from xtype...
23330     
23331     this.ds = config.dataSource;
23332     
23333     if (config.store && !this.ds) {
23334         this.store= Roo.factory(config.store, Roo.data);
23335         this.ds = this.store;
23336         this.ds.xmodule = this.xmodule || false;
23337     }
23338     
23339     this.toolbarItems = [];
23340     if (config.items) {
23341         this.toolbarItems = config.items;
23342     }
23343     
23344     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23345     
23346     this.cursor = 0;
23347     
23348     if (this.ds) { 
23349         this.bind(this.ds);
23350     }
23351     
23352     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23353     
23354 };
23355
23356 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23357     /**
23358      * @cfg {Roo.data.Store} dataSource
23359      * The underlying data store providing the paged data
23360      */
23361     /**
23362      * @cfg {String/HTMLElement/Element} container
23363      * container The id or element that will contain the toolbar
23364      */
23365     /**
23366      * @cfg {Boolean} displayInfo
23367      * True to display the displayMsg (defaults to false)
23368      */
23369     /**
23370      * @cfg {Number} pageSize
23371      * The number of records to display per page (defaults to 20)
23372      */
23373     pageSize: 20,
23374     /**
23375      * @cfg {String} displayMsg
23376      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23377      */
23378     displayMsg : 'Displaying {0} - {1} of {2}',
23379     /**
23380      * @cfg {String} emptyMsg
23381      * The message to display when no records are found (defaults to "No data to display")
23382      */
23383     emptyMsg : 'No data to display',
23384     /**
23385      * Customizable piece of the default paging text (defaults to "Page")
23386      * @type String
23387      */
23388     beforePageText : "Page",
23389     /**
23390      * Customizable piece of the default paging text (defaults to "of %0")
23391      * @type String
23392      */
23393     afterPageText : "of {0}",
23394     /**
23395      * Customizable piece of the default paging text (defaults to "First Page")
23396      * @type String
23397      */
23398     firstText : "First Page",
23399     /**
23400      * Customizable piece of the default paging text (defaults to "Previous Page")
23401      * @type String
23402      */
23403     prevText : "Previous Page",
23404     /**
23405      * Customizable piece of the default paging text (defaults to "Next Page")
23406      * @type String
23407      */
23408     nextText : "Next Page",
23409     /**
23410      * Customizable piece of the default paging text (defaults to "Last Page")
23411      * @type String
23412      */
23413     lastText : "Last Page",
23414     /**
23415      * Customizable piece of the default paging text (defaults to "Refresh")
23416      * @type String
23417      */
23418     refreshText : "Refresh",
23419
23420     buttons : false,
23421     // private
23422     onRender : function(ct, position) 
23423     {
23424         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23425         this.navgroup.parentId = this.id;
23426         this.navgroup.onRender(this.el, null);
23427         // add the buttons to the navgroup
23428         
23429         if(this.displayInfo){
23430             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23431             this.displayEl = this.el.select('.x-paging-info', true).first();
23432 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23433 //            this.displayEl = navel.el.select('span',true).first();
23434         }
23435         
23436         var _this = this;
23437         
23438         if(this.buttons){
23439             Roo.each(_this.buttons, function(e){ // this might need to use render????
23440                Roo.factory(e).onRender(_this.el, null);
23441             });
23442         }
23443             
23444         Roo.each(_this.toolbarItems, function(e) {
23445             _this.navgroup.addItem(e);
23446         });
23447         
23448         
23449         this.first = this.navgroup.addItem({
23450             tooltip: this.firstText,
23451             cls: "prev",
23452             icon : 'fa fa-backward',
23453             disabled: true,
23454             preventDefault: true,
23455             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23456         });
23457         
23458         this.prev =  this.navgroup.addItem({
23459             tooltip: this.prevText,
23460             cls: "prev",
23461             icon : 'fa fa-step-backward',
23462             disabled: true,
23463             preventDefault: true,
23464             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23465         });
23466     //this.addSeparator();
23467         
23468         
23469         var field = this.navgroup.addItem( {
23470             tagtype : 'span',
23471             cls : 'x-paging-position',
23472             
23473             html : this.beforePageText  +
23474                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23475                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23476          } ); //?? escaped?
23477         
23478         this.field = field.el.select('input', true).first();
23479         this.field.on("keydown", this.onPagingKeydown, this);
23480         this.field.on("focus", function(){this.dom.select();});
23481     
23482     
23483         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23484         //this.field.setHeight(18);
23485         //this.addSeparator();
23486         this.next = this.navgroup.addItem({
23487             tooltip: this.nextText,
23488             cls: "next",
23489             html : ' <i class="fa fa-step-forward">',
23490             disabled: true,
23491             preventDefault: true,
23492             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23493         });
23494         this.last = this.navgroup.addItem({
23495             tooltip: this.lastText,
23496             icon : 'fa fa-forward',
23497             cls: "next",
23498             disabled: true,
23499             preventDefault: true,
23500             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23501         });
23502     //this.addSeparator();
23503         this.loading = this.navgroup.addItem({
23504             tooltip: this.refreshText,
23505             icon: 'fa fa-refresh',
23506             preventDefault: true,
23507             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23508         });
23509         
23510     },
23511
23512     // private
23513     updateInfo : function(){
23514         if(this.displayEl){
23515             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23516             var msg = count == 0 ?
23517                 this.emptyMsg :
23518                 String.format(
23519                     this.displayMsg,
23520                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23521                 );
23522             this.displayEl.update(msg);
23523         }
23524     },
23525
23526     // private
23527     onLoad : function(ds, r, o){
23528        this.cursor = o.params ? o.params.start : 0;
23529        var d = this.getPageData(),
23530             ap = d.activePage,
23531             ps = d.pages;
23532         
23533        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23534        this.field.dom.value = ap;
23535        this.first.setDisabled(ap == 1);
23536        this.prev.setDisabled(ap == 1);
23537        this.next.setDisabled(ap == ps);
23538        this.last.setDisabled(ap == ps);
23539        this.loading.enable();
23540        this.updateInfo();
23541     },
23542
23543     // private
23544     getPageData : function(){
23545         var total = this.ds.getTotalCount();
23546         return {
23547             total : total,
23548             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23549             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23550         };
23551     },
23552
23553     // private
23554     onLoadError : function(){
23555         this.loading.enable();
23556     },
23557
23558     // private
23559     onPagingKeydown : function(e){
23560         var k = e.getKey();
23561         var d = this.getPageData();
23562         if(k == e.RETURN){
23563             var v = this.field.dom.value, pageNum;
23564             if(!v || isNaN(pageNum = parseInt(v, 10))){
23565                 this.field.dom.value = d.activePage;
23566                 return;
23567             }
23568             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23569             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23570             e.stopEvent();
23571         }
23572         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))
23573         {
23574           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23575           this.field.dom.value = pageNum;
23576           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23577           e.stopEvent();
23578         }
23579         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23580         {
23581           var v = this.field.dom.value, pageNum; 
23582           var increment = (e.shiftKey) ? 10 : 1;
23583           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23584                 increment *= -1;
23585           }
23586           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23587             this.field.dom.value = d.activePage;
23588             return;
23589           }
23590           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23591           {
23592             this.field.dom.value = parseInt(v, 10) + increment;
23593             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23594             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23595           }
23596           e.stopEvent();
23597         }
23598     },
23599
23600     // private
23601     beforeLoad : function(){
23602         if(this.loading){
23603             this.loading.disable();
23604         }
23605     },
23606
23607     // private
23608     onClick : function(which){
23609         
23610         var ds = this.ds;
23611         if (!ds) {
23612             return;
23613         }
23614         
23615         switch(which){
23616             case "first":
23617                 ds.load({params:{start: 0, limit: this.pageSize}});
23618             break;
23619             case "prev":
23620                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23621             break;
23622             case "next":
23623                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23624             break;
23625             case "last":
23626                 var total = ds.getTotalCount();
23627                 var extra = total % this.pageSize;
23628                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23629                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23630             break;
23631             case "refresh":
23632                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23633             break;
23634         }
23635     },
23636
23637     /**
23638      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23639      * @param {Roo.data.Store} store The data store to unbind
23640      */
23641     unbind : function(ds){
23642         ds.un("beforeload", this.beforeLoad, this);
23643         ds.un("load", this.onLoad, this);
23644         ds.un("loadexception", this.onLoadError, this);
23645         ds.un("remove", this.updateInfo, this);
23646         ds.un("add", this.updateInfo, this);
23647         this.ds = undefined;
23648     },
23649
23650     /**
23651      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23652      * @param {Roo.data.Store} store The data store to bind
23653      */
23654     bind : function(ds){
23655         ds.on("beforeload", this.beforeLoad, this);
23656         ds.on("load", this.onLoad, this);
23657         ds.on("loadexception", this.onLoadError, this);
23658         ds.on("remove", this.updateInfo, this);
23659         ds.on("add", this.updateInfo, this);
23660         this.ds = ds;
23661     }
23662 });/*
23663  * - LGPL
23664  *
23665  * element
23666  * 
23667  */
23668
23669 /**
23670  * @class Roo.bootstrap.MessageBar
23671  * @extends Roo.bootstrap.Component
23672  * Bootstrap MessageBar class
23673  * @cfg {String} html contents of the MessageBar
23674  * @cfg {String} weight (info | success | warning | danger) default info
23675  * @cfg {String} beforeClass insert the bar before the given class
23676  * @cfg {Boolean} closable (true | false) default false
23677  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23678  * 
23679  * @constructor
23680  * Create a new Element
23681  * @param {Object} config The config object
23682  */
23683
23684 Roo.bootstrap.MessageBar = function(config){
23685     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23686 };
23687
23688 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23689     
23690     html: '',
23691     weight: 'info',
23692     closable: false,
23693     fixed: false,
23694     beforeClass: 'bootstrap-sticky-wrap',
23695     
23696     getAutoCreate : function(){
23697         
23698         var cfg = {
23699             tag: 'div',
23700             cls: 'alert alert-dismissable alert-' + this.weight,
23701             cn: [
23702                 {
23703                     tag: 'span',
23704                     cls: 'message',
23705                     html: this.html || ''
23706                 }
23707             ]
23708         };
23709         
23710         if(this.fixed){
23711             cfg.cls += ' alert-messages-fixed';
23712         }
23713         
23714         if(this.closable){
23715             cfg.cn.push({
23716                 tag: 'button',
23717                 cls: 'close',
23718                 html: 'x'
23719             });
23720         }
23721         
23722         return cfg;
23723     },
23724     
23725     onRender : function(ct, position)
23726     {
23727         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23728         
23729         if(!this.el){
23730             var cfg = Roo.apply({},  this.getAutoCreate());
23731             cfg.id = Roo.id();
23732             
23733             if (this.cls) {
23734                 cfg.cls += ' ' + this.cls;
23735             }
23736             if (this.style) {
23737                 cfg.style = this.style;
23738             }
23739             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23740             
23741             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23742         }
23743         
23744         this.el.select('>button.close').on('click', this.hide, this);
23745         
23746     },
23747     
23748     show : function()
23749     {
23750         if (!this.rendered) {
23751             this.render();
23752         }
23753         
23754         this.el.show();
23755         
23756         this.fireEvent('show', this);
23757         
23758     },
23759     
23760     hide : function()
23761     {
23762         if (!this.rendered) {
23763             this.render();
23764         }
23765         
23766         this.el.hide();
23767         
23768         this.fireEvent('hide', this);
23769     },
23770     
23771     update : function()
23772     {
23773 //        var e = this.el.dom.firstChild;
23774 //        
23775 //        if(this.closable){
23776 //            e = e.nextSibling;
23777 //        }
23778 //        
23779 //        e.data = this.html || '';
23780
23781         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23782     }
23783    
23784 });
23785
23786  
23787
23788      /*
23789  * - LGPL
23790  *
23791  * Graph
23792  * 
23793  */
23794
23795
23796 /**
23797  * @class Roo.bootstrap.Graph
23798  * @extends Roo.bootstrap.Component
23799  * Bootstrap Graph class
23800 > Prameters
23801  -sm {number} sm 4
23802  -md {number} md 5
23803  @cfg {String} graphtype  bar | vbar | pie
23804  @cfg {number} g_x coodinator | centre x (pie)
23805  @cfg {number} g_y coodinator | centre y (pie)
23806  @cfg {number} g_r radius (pie)
23807  @cfg {number} g_height height of the chart (respected by all elements in the set)
23808  @cfg {number} g_width width of the chart (respected by all elements in the set)
23809  @cfg {Object} title The title of the chart
23810     
23811  -{Array}  values
23812  -opts (object) options for the chart 
23813      o {
23814      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23815      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23816      o vgutter (number)
23817      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.
23818      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23819      o to
23820      o stretch (boolean)
23821      o }
23822  -opts (object) options for the pie
23823      o{
23824      o cut
23825      o startAngle (number)
23826      o endAngle (number)
23827      } 
23828  *
23829  * @constructor
23830  * Create a new Input
23831  * @param {Object} config The config object
23832  */
23833
23834 Roo.bootstrap.Graph = function(config){
23835     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23836     
23837     this.addEvents({
23838         // img events
23839         /**
23840          * @event click
23841          * The img click event for the img.
23842          * @param {Roo.EventObject} e
23843          */
23844         "click" : true
23845     });
23846 };
23847
23848 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23849     
23850     sm: 4,
23851     md: 5,
23852     graphtype: 'bar',
23853     g_height: 250,
23854     g_width: 400,
23855     g_x: 50,
23856     g_y: 50,
23857     g_r: 30,
23858     opts:{
23859         //g_colors: this.colors,
23860         g_type: 'soft',
23861         g_gutter: '20%'
23862
23863     },
23864     title : false,
23865
23866     getAutoCreate : function(){
23867         
23868         var cfg = {
23869             tag: 'div',
23870             html : null
23871         };
23872         
23873         
23874         return  cfg;
23875     },
23876
23877     onRender : function(ct,position){
23878         
23879         
23880         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23881         
23882         if (typeof(Raphael) == 'undefined') {
23883             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23884             return;
23885         }
23886         
23887         this.raphael = Raphael(this.el.dom);
23888         
23889                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23890                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23891                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23892                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23893                 /*
23894                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23895                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23896                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23897                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23898                 
23899                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23900                 r.barchart(330, 10, 300, 220, data1);
23901                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23902                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23903                 */
23904                 
23905                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23906                 // r.barchart(30, 30, 560, 250,  xdata, {
23907                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23908                 //     axis : "0 0 1 1",
23909                 //     axisxlabels :  xdata
23910                 //     //yvalues : cols,
23911                    
23912                 // });
23913 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23914 //        
23915 //        this.load(null,xdata,{
23916 //                axis : "0 0 1 1",
23917 //                axisxlabels :  xdata
23918 //                });
23919
23920     },
23921
23922     load : function(graphtype,xdata,opts)
23923     {
23924         this.raphael.clear();
23925         if(!graphtype) {
23926             graphtype = this.graphtype;
23927         }
23928         if(!opts){
23929             opts = this.opts;
23930         }
23931         var r = this.raphael,
23932             fin = function () {
23933                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23934             },
23935             fout = function () {
23936                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23937             },
23938             pfin = function() {
23939                 this.sector.stop();
23940                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23941
23942                 if (this.label) {
23943                     this.label[0].stop();
23944                     this.label[0].attr({ r: 7.5 });
23945                     this.label[1].attr({ "font-weight": 800 });
23946                 }
23947             },
23948             pfout = function() {
23949                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23950
23951                 if (this.label) {
23952                     this.label[0].animate({ r: 5 }, 500, "bounce");
23953                     this.label[1].attr({ "font-weight": 400 });
23954                 }
23955             };
23956
23957         switch(graphtype){
23958             case 'bar':
23959                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23960                 break;
23961             case 'hbar':
23962                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23963                 break;
23964             case 'pie':
23965 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23966 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23967 //            
23968                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23969                 
23970                 break;
23971
23972         }
23973         
23974         if(this.title){
23975             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23976         }
23977         
23978     },
23979     
23980     setTitle: function(o)
23981     {
23982         this.title = o;
23983     },
23984     
23985     initEvents: function() {
23986         
23987         if(!this.href){
23988             this.el.on('click', this.onClick, this);
23989         }
23990     },
23991     
23992     onClick : function(e)
23993     {
23994         Roo.log('img onclick');
23995         this.fireEvent('click', this, e);
23996     }
23997    
23998 });
23999
24000  
24001 /*
24002  * - LGPL
24003  *
24004  * numberBox
24005  * 
24006  */
24007 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24008
24009 /**
24010  * @class Roo.bootstrap.dash.NumberBox
24011  * @extends Roo.bootstrap.Component
24012  * Bootstrap NumberBox class
24013  * @cfg {String} headline Box headline
24014  * @cfg {String} content Box content
24015  * @cfg {String} icon Box icon
24016  * @cfg {String} footer Footer text
24017  * @cfg {String} fhref Footer href
24018  * 
24019  * @constructor
24020  * Create a new NumberBox
24021  * @param {Object} config The config object
24022  */
24023
24024
24025 Roo.bootstrap.dash.NumberBox = function(config){
24026     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24027     
24028 };
24029
24030 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24031     
24032     headline : '',
24033     content : '',
24034     icon : '',
24035     footer : '',
24036     fhref : '',
24037     ficon : '',
24038     
24039     getAutoCreate : function(){
24040         
24041         var cfg = {
24042             tag : 'div',
24043             cls : 'small-box ',
24044             cn : [
24045                 {
24046                     tag : 'div',
24047                     cls : 'inner',
24048                     cn :[
24049                         {
24050                             tag : 'h3',
24051                             cls : 'roo-headline',
24052                             html : this.headline
24053                         },
24054                         {
24055                             tag : 'p',
24056                             cls : 'roo-content',
24057                             html : this.content
24058                         }
24059                     ]
24060                 }
24061             ]
24062         };
24063         
24064         if(this.icon){
24065             cfg.cn.push({
24066                 tag : 'div',
24067                 cls : 'icon',
24068                 cn :[
24069                     {
24070                         tag : 'i',
24071                         cls : 'ion ' + this.icon
24072                     }
24073                 ]
24074             });
24075         }
24076         
24077         if(this.footer){
24078             var footer = {
24079                 tag : 'a',
24080                 cls : 'small-box-footer',
24081                 href : this.fhref || '#',
24082                 html : this.footer
24083             };
24084             
24085             cfg.cn.push(footer);
24086             
24087         }
24088         
24089         return  cfg;
24090     },
24091
24092     onRender : function(ct,position){
24093         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24094
24095
24096        
24097                 
24098     },
24099
24100     setHeadline: function (value)
24101     {
24102         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24103     },
24104     
24105     setFooter: function (value, href)
24106     {
24107         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24108         
24109         if(href){
24110             this.el.select('a.small-box-footer',true).first().attr('href', href);
24111         }
24112         
24113     },
24114
24115     setContent: function (value)
24116     {
24117         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24118     },
24119
24120     initEvents: function() 
24121     {   
24122         
24123     }
24124     
24125 });
24126
24127  
24128 /*
24129  * - LGPL
24130  *
24131  * TabBox
24132  * 
24133  */
24134 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24135
24136 /**
24137  * @class Roo.bootstrap.dash.TabBox
24138  * @extends Roo.bootstrap.Component
24139  * Bootstrap TabBox class
24140  * @cfg {String} title Title of the TabBox
24141  * @cfg {String} icon Icon of the TabBox
24142  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24143  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24144  * 
24145  * @constructor
24146  * Create a new TabBox
24147  * @param {Object} config The config object
24148  */
24149
24150
24151 Roo.bootstrap.dash.TabBox = function(config){
24152     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24153     this.addEvents({
24154         // raw events
24155         /**
24156          * @event addpane
24157          * When a pane is added
24158          * @param {Roo.bootstrap.dash.TabPane} pane
24159          */
24160         "addpane" : true,
24161         /**
24162          * @event activatepane
24163          * When a pane is activated
24164          * @param {Roo.bootstrap.dash.TabPane} pane
24165          */
24166         "activatepane" : true
24167         
24168          
24169     });
24170     
24171     this.panes = [];
24172 };
24173
24174 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24175
24176     title : '',
24177     icon : false,
24178     showtabs : true,
24179     tabScrollable : false,
24180     
24181     getChildContainer : function()
24182     {
24183         return this.el.select('.tab-content', true).first();
24184     },
24185     
24186     getAutoCreate : function(){
24187         
24188         var header = {
24189             tag: 'li',
24190             cls: 'pull-left header',
24191             html: this.title,
24192             cn : []
24193         };
24194         
24195         if(this.icon){
24196             header.cn.push({
24197                 tag: 'i',
24198                 cls: 'fa ' + this.icon
24199             });
24200         }
24201         
24202         var h = {
24203             tag: 'ul',
24204             cls: 'nav nav-tabs pull-right',
24205             cn: [
24206                 header
24207             ]
24208         };
24209         
24210         if(this.tabScrollable){
24211             h = {
24212                 tag: 'div',
24213                 cls: 'tab-header',
24214                 cn: [
24215                     {
24216                         tag: 'ul',
24217                         cls: 'nav nav-tabs pull-right',
24218                         cn: [
24219                             header
24220                         ]
24221                     }
24222                 ]
24223             };
24224         }
24225         
24226         var cfg = {
24227             tag: 'div',
24228             cls: 'nav-tabs-custom',
24229             cn: [
24230                 h,
24231                 {
24232                     tag: 'div',
24233                     cls: 'tab-content no-padding',
24234                     cn: []
24235                 }
24236             ]
24237         };
24238
24239         return  cfg;
24240     },
24241     initEvents : function()
24242     {
24243         //Roo.log('add add pane handler');
24244         this.on('addpane', this.onAddPane, this);
24245     },
24246      /**
24247      * Updates the box title
24248      * @param {String} html to set the title to.
24249      */
24250     setTitle : function(value)
24251     {
24252         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24253     },
24254     onAddPane : function(pane)
24255     {
24256         this.panes.push(pane);
24257         //Roo.log('addpane');
24258         //Roo.log(pane);
24259         // tabs are rendere left to right..
24260         if(!this.showtabs){
24261             return;
24262         }
24263         
24264         var ctr = this.el.select('.nav-tabs', true).first();
24265          
24266          
24267         var existing = ctr.select('.nav-tab',true);
24268         var qty = existing.getCount();;
24269         
24270         
24271         var tab = ctr.createChild({
24272             tag : 'li',
24273             cls : 'nav-tab' + (qty ? '' : ' active'),
24274             cn : [
24275                 {
24276                     tag : 'a',
24277                     href:'#',
24278                     html : pane.title
24279                 }
24280             ]
24281         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24282         pane.tab = tab;
24283         
24284         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24285         if (!qty) {
24286             pane.el.addClass('active');
24287         }
24288         
24289                 
24290     },
24291     onTabClick : function(ev,un,ob,pane)
24292     {
24293         //Roo.log('tab - prev default');
24294         ev.preventDefault();
24295         
24296         
24297         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24298         pane.tab.addClass('active');
24299         //Roo.log(pane.title);
24300         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24301         // technically we should have a deactivate event.. but maybe add later.
24302         // and it should not de-activate the selected tab...
24303         this.fireEvent('activatepane', pane);
24304         pane.el.addClass('active');
24305         pane.fireEvent('activate');
24306         
24307         
24308     },
24309     
24310     getActivePane : function()
24311     {
24312         var r = false;
24313         Roo.each(this.panes, function(p) {
24314             if(p.el.hasClass('active')){
24315                 r = p;
24316                 return false;
24317             }
24318             
24319             return;
24320         });
24321         
24322         return r;
24323     }
24324     
24325     
24326 });
24327
24328  
24329 /*
24330  * - LGPL
24331  *
24332  * Tab pane
24333  * 
24334  */
24335 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24336 /**
24337  * @class Roo.bootstrap.TabPane
24338  * @extends Roo.bootstrap.Component
24339  * Bootstrap TabPane class
24340  * @cfg {Boolean} active (false | true) Default false
24341  * @cfg {String} title title of panel
24342
24343  * 
24344  * @constructor
24345  * Create a new TabPane
24346  * @param {Object} config The config object
24347  */
24348
24349 Roo.bootstrap.dash.TabPane = function(config){
24350     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24351     
24352     this.addEvents({
24353         // raw events
24354         /**
24355          * @event activate
24356          * When a pane is activated
24357          * @param {Roo.bootstrap.dash.TabPane} pane
24358          */
24359         "activate" : true
24360          
24361     });
24362 };
24363
24364 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24365     
24366     active : false,
24367     title : '',
24368     
24369     // the tabBox that this is attached to.
24370     tab : false,
24371      
24372     getAutoCreate : function() 
24373     {
24374         var cfg = {
24375             tag: 'div',
24376             cls: 'tab-pane'
24377         };
24378         
24379         if(this.active){
24380             cfg.cls += ' active';
24381         }
24382         
24383         return cfg;
24384     },
24385     initEvents  : function()
24386     {
24387         //Roo.log('trigger add pane handler');
24388         this.parent().fireEvent('addpane', this)
24389     },
24390     
24391      /**
24392      * Updates the tab title 
24393      * @param {String} html to set the title to.
24394      */
24395     setTitle: function(str)
24396     {
24397         if (!this.tab) {
24398             return;
24399         }
24400         this.title = str;
24401         this.tab.select('a', true).first().dom.innerHTML = str;
24402         
24403     }
24404     
24405     
24406     
24407 });
24408
24409  
24410
24411
24412  /*
24413  * - LGPL
24414  *
24415  * menu
24416  * 
24417  */
24418 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24419
24420 /**
24421  * @class Roo.bootstrap.menu.Menu
24422  * @extends Roo.bootstrap.Component
24423  * Bootstrap Menu class - container for Menu
24424  * @cfg {String} html Text of the menu
24425  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24426  * @cfg {String} icon Font awesome icon
24427  * @cfg {String} pos Menu align to (top | bottom) default bottom
24428  * 
24429  * 
24430  * @constructor
24431  * Create a new Menu
24432  * @param {Object} config The config object
24433  */
24434
24435
24436 Roo.bootstrap.menu.Menu = function(config){
24437     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24438     
24439     this.addEvents({
24440         /**
24441          * @event beforeshow
24442          * Fires before this menu is displayed
24443          * @param {Roo.bootstrap.menu.Menu} this
24444          */
24445         beforeshow : true,
24446         /**
24447          * @event beforehide
24448          * Fires before this menu is hidden
24449          * @param {Roo.bootstrap.menu.Menu} this
24450          */
24451         beforehide : true,
24452         /**
24453          * @event show
24454          * Fires after this menu is displayed
24455          * @param {Roo.bootstrap.menu.Menu} this
24456          */
24457         show : true,
24458         /**
24459          * @event hide
24460          * Fires after this menu is hidden
24461          * @param {Roo.bootstrap.menu.Menu} this
24462          */
24463         hide : true,
24464         /**
24465          * @event click
24466          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24467          * @param {Roo.bootstrap.menu.Menu} this
24468          * @param {Roo.EventObject} e
24469          */
24470         click : true
24471     });
24472     
24473 };
24474
24475 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24476     
24477     submenu : false,
24478     html : '',
24479     weight : 'default',
24480     icon : false,
24481     pos : 'bottom',
24482     
24483     
24484     getChildContainer : function() {
24485         if(this.isSubMenu){
24486             return this.el;
24487         }
24488         
24489         return this.el.select('ul.dropdown-menu', true).first();  
24490     },
24491     
24492     getAutoCreate : function()
24493     {
24494         var text = [
24495             {
24496                 tag : 'span',
24497                 cls : 'roo-menu-text',
24498                 html : this.html
24499             }
24500         ];
24501         
24502         if(this.icon){
24503             text.unshift({
24504                 tag : 'i',
24505                 cls : 'fa ' + this.icon
24506             })
24507         }
24508         
24509         
24510         var cfg = {
24511             tag : 'div',
24512             cls : 'btn-group',
24513             cn : [
24514                 {
24515                     tag : 'button',
24516                     cls : 'dropdown-button btn btn-' + this.weight,
24517                     cn : text
24518                 },
24519                 {
24520                     tag : 'button',
24521                     cls : 'dropdown-toggle btn btn-' + this.weight,
24522                     cn : [
24523                         {
24524                             tag : 'span',
24525                             cls : 'caret'
24526                         }
24527                     ]
24528                 },
24529                 {
24530                     tag : 'ul',
24531                     cls : 'dropdown-menu'
24532                 }
24533             ]
24534             
24535         };
24536         
24537         if(this.pos == 'top'){
24538             cfg.cls += ' dropup';
24539         }
24540         
24541         if(this.isSubMenu){
24542             cfg = {
24543                 tag : 'ul',
24544                 cls : 'dropdown-menu'
24545             }
24546         }
24547         
24548         return cfg;
24549     },
24550     
24551     onRender : function(ct, position)
24552     {
24553         this.isSubMenu = ct.hasClass('dropdown-submenu');
24554         
24555         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24556     },
24557     
24558     initEvents : function() 
24559     {
24560         if(this.isSubMenu){
24561             return;
24562         }
24563         
24564         this.hidden = true;
24565         
24566         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24567         this.triggerEl.on('click', this.onTriggerPress, this);
24568         
24569         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24570         this.buttonEl.on('click', this.onClick, this);
24571         
24572     },
24573     
24574     list : function()
24575     {
24576         if(this.isSubMenu){
24577             return this.el;
24578         }
24579         
24580         return this.el.select('ul.dropdown-menu', true).first();
24581     },
24582     
24583     onClick : function(e)
24584     {
24585         this.fireEvent("click", this, e);
24586     },
24587     
24588     onTriggerPress  : function(e)
24589     {   
24590         if (this.isVisible()) {
24591             this.hide();
24592         } else {
24593             this.show();
24594         }
24595     },
24596     
24597     isVisible : function(){
24598         return !this.hidden;
24599     },
24600     
24601     show : function()
24602     {
24603         this.fireEvent("beforeshow", this);
24604         
24605         this.hidden = false;
24606         this.el.addClass('open');
24607         
24608         Roo.get(document).on("mouseup", this.onMouseUp, this);
24609         
24610         this.fireEvent("show", this);
24611         
24612         
24613     },
24614     
24615     hide : function()
24616     {
24617         this.fireEvent("beforehide", this);
24618         
24619         this.hidden = true;
24620         this.el.removeClass('open');
24621         
24622         Roo.get(document).un("mouseup", this.onMouseUp);
24623         
24624         this.fireEvent("hide", this);
24625     },
24626     
24627     onMouseUp : function()
24628     {
24629         this.hide();
24630     }
24631     
24632 });
24633
24634  
24635  /*
24636  * - LGPL
24637  *
24638  * menu item
24639  * 
24640  */
24641 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24642
24643 /**
24644  * @class Roo.bootstrap.menu.Item
24645  * @extends Roo.bootstrap.Component
24646  * Bootstrap MenuItem class
24647  * @cfg {Boolean} submenu (true | false) default false
24648  * @cfg {String} html text of the item
24649  * @cfg {String} href the link
24650  * @cfg {Boolean} disable (true | false) default false
24651  * @cfg {Boolean} preventDefault (true | false) default true
24652  * @cfg {String} icon Font awesome icon
24653  * @cfg {String} pos Submenu align to (left | right) default right 
24654  * 
24655  * 
24656  * @constructor
24657  * Create a new Item
24658  * @param {Object} config The config object
24659  */
24660
24661
24662 Roo.bootstrap.menu.Item = function(config){
24663     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24664     this.addEvents({
24665         /**
24666          * @event mouseover
24667          * Fires when the mouse is hovering over this menu
24668          * @param {Roo.bootstrap.menu.Item} this
24669          * @param {Roo.EventObject} e
24670          */
24671         mouseover : true,
24672         /**
24673          * @event mouseout
24674          * Fires when the mouse exits this menu
24675          * @param {Roo.bootstrap.menu.Item} this
24676          * @param {Roo.EventObject} e
24677          */
24678         mouseout : true,
24679         // raw events
24680         /**
24681          * @event click
24682          * The raw click event for the entire grid.
24683          * @param {Roo.EventObject} e
24684          */
24685         click : true
24686     });
24687 };
24688
24689 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24690     
24691     submenu : false,
24692     href : '',
24693     html : '',
24694     preventDefault: true,
24695     disable : false,
24696     icon : false,
24697     pos : 'right',
24698     
24699     getAutoCreate : function()
24700     {
24701         var text = [
24702             {
24703                 tag : 'span',
24704                 cls : 'roo-menu-item-text',
24705                 html : this.html
24706             }
24707         ];
24708         
24709         if(this.icon){
24710             text.unshift({
24711                 tag : 'i',
24712                 cls : 'fa ' + this.icon
24713             })
24714         }
24715         
24716         var cfg = {
24717             tag : 'li',
24718             cn : [
24719                 {
24720                     tag : 'a',
24721                     href : this.href || '#',
24722                     cn : text
24723                 }
24724             ]
24725         };
24726         
24727         if(this.disable){
24728             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24729         }
24730         
24731         if(this.submenu){
24732             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24733             
24734             if(this.pos == 'left'){
24735                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24736             }
24737         }
24738         
24739         return cfg;
24740     },
24741     
24742     initEvents : function() 
24743     {
24744         this.el.on('mouseover', this.onMouseOver, this);
24745         this.el.on('mouseout', this.onMouseOut, this);
24746         
24747         this.el.select('a', true).first().on('click', this.onClick, this);
24748         
24749     },
24750     
24751     onClick : function(e)
24752     {
24753         if(this.preventDefault){
24754             e.preventDefault();
24755         }
24756         
24757         this.fireEvent("click", this, e);
24758     },
24759     
24760     onMouseOver : function(e)
24761     {
24762         if(this.submenu && this.pos == 'left'){
24763             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24764         }
24765         
24766         this.fireEvent("mouseover", this, e);
24767     },
24768     
24769     onMouseOut : function(e)
24770     {
24771         this.fireEvent("mouseout", this, e);
24772     }
24773 });
24774
24775  
24776
24777  /*
24778  * - LGPL
24779  *
24780  * menu separator
24781  * 
24782  */
24783 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24784
24785 /**
24786  * @class Roo.bootstrap.menu.Separator
24787  * @extends Roo.bootstrap.Component
24788  * Bootstrap Separator class
24789  * 
24790  * @constructor
24791  * Create a new Separator
24792  * @param {Object} config The config object
24793  */
24794
24795
24796 Roo.bootstrap.menu.Separator = function(config){
24797     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24798 };
24799
24800 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24801     
24802     getAutoCreate : function(){
24803         var cfg = {
24804             tag : 'li',
24805             cls: 'divider'
24806         };
24807         
24808         return cfg;
24809     }
24810    
24811 });
24812
24813  
24814
24815  /*
24816  * - LGPL
24817  *
24818  * Tooltip
24819  * 
24820  */
24821
24822 /**
24823  * @class Roo.bootstrap.Tooltip
24824  * Bootstrap Tooltip class
24825  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24826  * to determine which dom element triggers the tooltip.
24827  * 
24828  * It needs to add support for additional attributes like tooltip-position
24829  * 
24830  * @constructor
24831  * Create a new Toolti
24832  * @param {Object} config The config object
24833  */
24834
24835 Roo.bootstrap.Tooltip = function(config){
24836     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24837 };
24838
24839 Roo.apply(Roo.bootstrap.Tooltip, {
24840     /**
24841      * @function init initialize tooltip monitoring.
24842      * @static
24843      */
24844     currentEl : false,
24845     currentTip : false,
24846     currentRegion : false,
24847     
24848     //  init : delay?
24849     
24850     init : function()
24851     {
24852         Roo.get(document).on('mouseover', this.enter ,this);
24853         Roo.get(document).on('mouseout', this.leave, this);
24854          
24855         
24856         this.currentTip = new Roo.bootstrap.Tooltip();
24857     },
24858     
24859     enter : function(ev)
24860     {
24861         var dom = ev.getTarget();
24862         
24863         //Roo.log(['enter',dom]);
24864         var el = Roo.fly(dom);
24865         if (this.currentEl) {
24866             //Roo.log(dom);
24867             //Roo.log(this.currentEl);
24868             //Roo.log(this.currentEl.contains(dom));
24869             if (this.currentEl == el) {
24870                 return;
24871             }
24872             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24873                 return;
24874             }
24875
24876         }
24877         
24878         if (this.currentTip.el) {
24879             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24880         }    
24881         //Roo.log(ev);
24882         
24883         if(!el || el.dom == document){
24884             return;
24885         }
24886         
24887         var bindEl = el;
24888         
24889         // you can not look for children, as if el is the body.. then everythign is the child..
24890         if (!el.attr('tooltip')) { //
24891             if (!el.select("[tooltip]").elements.length) {
24892                 return;
24893             }
24894             // is the mouse over this child...?
24895             bindEl = el.select("[tooltip]").first();
24896             var xy = ev.getXY();
24897             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24898                 //Roo.log("not in region.");
24899                 return;
24900             }
24901             //Roo.log("child element over..");
24902             
24903         }
24904         this.currentEl = bindEl;
24905         this.currentTip.bind(bindEl);
24906         this.currentRegion = Roo.lib.Region.getRegion(dom);
24907         this.currentTip.enter();
24908         
24909     },
24910     leave : function(ev)
24911     {
24912         var dom = ev.getTarget();
24913         //Roo.log(['leave',dom]);
24914         if (!this.currentEl) {
24915             return;
24916         }
24917         
24918         
24919         if (dom != this.currentEl.dom) {
24920             return;
24921         }
24922         var xy = ev.getXY();
24923         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24924             return;
24925         }
24926         // only activate leave if mouse cursor is outside... bounding box..
24927         
24928         
24929         
24930         
24931         if (this.currentTip) {
24932             this.currentTip.leave();
24933         }
24934         //Roo.log('clear currentEl');
24935         this.currentEl = false;
24936         
24937         
24938     },
24939     alignment : {
24940         'left' : ['r-l', [-2,0], 'right'],
24941         'right' : ['l-r', [2,0], 'left'],
24942         'bottom' : ['t-b', [0,2], 'top'],
24943         'top' : [ 'b-t', [0,-2], 'bottom']
24944     }
24945     
24946 });
24947
24948
24949 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24950     
24951     
24952     bindEl : false,
24953     
24954     delay : null, // can be { show : 300 , hide: 500}
24955     
24956     timeout : null,
24957     
24958     hoverState : null, //???
24959     
24960     placement : 'bottom', 
24961     
24962     getAutoCreate : function(){
24963     
24964         var cfg = {
24965            cls : 'tooltip',
24966            role : 'tooltip',
24967            cn : [
24968                 {
24969                     cls : 'tooltip-arrow'
24970                 },
24971                 {
24972                     cls : 'tooltip-inner'
24973                 }
24974            ]
24975         };
24976         
24977         return cfg;
24978     },
24979     bind : function(el)
24980     {
24981         this.bindEl = el;
24982     },
24983       
24984     
24985     enter : function () {
24986        
24987         if (this.timeout != null) {
24988             clearTimeout(this.timeout);
24989         }
24990         
24991         this.hoverState = 'in';
24992          //Roo.log("enter - show");
24993         if (!this.delay || !this.delay.show) {
24994             this.show();
24995             return;
24996         }
24997         var _t = this;
24998         this.timeout = setTimeout(function () {
24999             if (_t.hoverState == 'in') {
25000                 _t.show();
25001             }
25002         }, this.delay.show);
25003     },
25004     leave : function()
25005     {
25006         clearTimeout(this.timeout);
25007     
25008         this.hoverState = 'out';
25009          if (!this.delay || !this.delay.hide) {
25010             this.hide();
25011             return;
25012         }
25013        
25014         var _t = this;
25015         this.timeout = setTimeout(function () {
25016             //Roo.log("leave - timeout");
25017             
25018             if (_t.hoverState == 'out') {
25019                 _t.hide();
25020                 Roo.bootstrap.Tooltip.currentEl = false;
25021             }
25022         }, delay);
25023     },
25024     
25025     show : function ()
25026     {
25027         if (!this.el) {
25028             this.render(document.body);
25029         }
25030         // set content.
25031         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25032         
25033         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25034         
25035         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25036         
25037         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25038         
25039         var placement = typeof this.placement == 'function' ?
25040             this.placement.call(this, this.el, on_el) :
25041             this.placement;
25042             
25043         var autoToken = /\s?auto?\s?/i;
25044         var autoPlace = autoToken.test(placement);
25045         if (autoPlace) {
25046             placement = placement.replace(autoToken, '') || 'top';
25047         }
25048         
25049         //this.el.detach()
25050         //this.el.setXY([0,0]);
25051         this.el.show();
25052         //this.el.dom.style.display='block';
25053         
25054         //this.el.appendTo(on_el);
25055         
25056         var p = this.getPosition();
25057         var box = this.el.getBox();
25058         
25059         if (autoPlace) {
25060             // fixme..
25061         }
25062         
25063         var align = Roo.bootstrap.Tooltip.alignment[placement];
25064         
25065         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25066         
25067         if(placement == 'top' || placement == 'bottom'){
25068             if(xy[0] < 0){
25069                 placement = 'right';
25070             }
25071             
25072             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25073                 placement = 'left';
25074             }
25075             
25076             var scroll = Roo.select('body', true).first().getScroll();
25077             
25078             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25079                 placement = 'top';
25080             }
25081             
25082         }
25083         
25084         align = Roo.bootstrap.Tooltip.alignment[placement];
25085         
25086         this.el.alignTo(this.bindEl, align[0],align[1]);
25087         //var arrow = this.el.select('.arrow',true).first();
25088         //arrow.set(align[2], 
25089         
25090         this.el.addClass(placement);
25091         
25092         this.el.addClass('in fade');
25093         
25094         this.hoverState = null;
25095         
25096         if (this.el.hasClass('fade')) {
25097             // fade it?
25098         }
25099         
25100     },
25101     hide : function()
25102     {
25103          
25104         if (!this.el) {
25105             return;
25106         }
25107         //this.el.setXY([0,0]);
25108         this.el.removeClass('in');
25109         //this.el.hide();
25110         
25111     }
25112     
25113 });
25114  
25115
25116  /*
25117  * - LGPL
25118  *
25119  * Location Picker
25120  * 
25121  */
25122
25123 /**
25124  * @class Roo.bootstrap.LocationPicker
25125  * @extends Roo.bootstrap.Component
25126  * Bootstrap LocationPicker class
25127  * @cfg {Number} latitude Position when init default 0
25128  * @cfg {Number} longitude Position when init default 0
25129  * @cfg {Number} zoom default 15
25130  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25131  * @cfg {Boolean} mapTypeControl default false
25132  * @cfg {Boolean} disableDoubleClickZoom default false
25133  * @cfg {Boolean} scrollwheel default true
25134  * @cfg {Boolean} streetViewControl default false
25135  * @cfg {Number} radius default 0
25136  * @cfg {String} locationName
25137  * @cfg {Boolean} draggable default true
25138  * @cfg {Boolean} enableAutocomplete default false
25139  * @cfg {Boolean} enableReverseGeocode default true
25140  * @cfg {String} markerTitle
25141  * 
25142  * @constructor
25143  * Create a new LocationPicker
25144  * @param {Object} config The config object
25145  */
25146
25147
25148 Roo.bootstrap.LocationPicker = function(config){
25149     
25150     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25151     
25152     this.addEvents({
25153         /**
25154          * @event initial
25155          * Fires when the picker initialized.
25156          * @param {Roo.bootstrap.LocationPicker} this
25157          * @param {Google Location} location
25158          */
25159         initial : true,
25160         /**
25161          * @event positionchanged
25162          * Fires when the picker position changed.
25163          * @param {Roo.bootstrap.LocationPicker} this
25164          * @param {Google Location} location
25165          */
25166         positionchanged : true,
25167         /**
25168          * @event resize
25169          * Fires when the map resize.
25170          * @param {Roo.bootstrap.LocationPicker} this
25171          */
25172         resize : true,
25173         /**
25174          * @event show
25175          * Fires when the map show.
25176          * @param {Roo.bootstrap.LocationPicker} this
25177          */
25178         show : true,
25179         /**
25180          * @event hide
25181          * Fires when the map hide.
25182          * @param {Roo.bootstrap.LocationPicker} this
25183          */
25184         hide : true,
25185         /**
25186          * @event mapClick
25187          * Fires when click the map.
25188          * @param {Roo.bootstrap.LocationPicker} this
25189          * @param {Map event} e
25190          */
25191         mapClick : true,
25192         /**
25193          * @event mapRightClick
25194          * Fires when right click the map.
25195          * @param {Roo.bootstrap.LocationPicker} this
25196          * @param {Map event} e
25197          */
25198         mapRightClick : true,
25199         /**
25200          * @event markerClick
25201          * Fires when click the marker.
25202          * @param {Roo.bootstrap.LocationPicker} this
25203          * @param {Map event} e
25204          */
25205         markerClick : true,
25206         /**
25207          * @event markerRightClick
25208          * Fires when right click the marker.
25209          * @param {Roo.bootstrap.LocationPicker} this
25210          * @param {Map event} e
25211          */
25212         markerRightClick : true,
25213         /**
25214          * @event OverlayViewDraw
25215          * Fires when OverlayView Draw
25216          * @param {Roo.bootstrap.LocationPicker} this
25217          */
25218         OverlayViewDraw : true,
25219         /**
25220          * @event OverlayViewOnAdd
25221          * Fires when OverlayView Draw
25222          * @param {Roo.bootstrap.LocationPicker} this
25223          */
25224         OverlayViewOnAdd : true,
25225         /**
25226          * @event OverlayViewOnRemove
25227          * Fires when OverlayView Draw
25228          * @param {Roo.bootstrap.LocationPicker} this
25229          */
25230         OverlayViewOnRemove : true,
25231         /**
25232          * @event OverlayViewShow
25233          * Fires when OverlayView Draw
25234          * @param {Roo.bootstrap.LocationPicker} this
25235          * @param {Pixel} cpx
25236          */
25237         OverlayViewShow : true,
25238         /**
25239          * @event OverlayViewHide
25240          * Fires when OverlayView Draw
25241          * @param {Roo.bootstrap.LocationPicker} this
25242          */
25243         OverlayViewHide : true,
25244         /**
25245          * @event loadexception
25246          * Fires when load google lib failed.
25247          * @param {Roo.bootstrap.LocationPicker} this
25248          */
25249         loadexception : true
25250     });
25251         
25252 };
25253
25254 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25255     
25256     gMapContext: false,
25257     
25258     latitude: 0,
25259     longitude: 0,
25260     zoom: 15,
25261     mapTypeId: false,
25262     mapTypeControl: false,
25263     disableDoubleClickZoom: false,
25264     scrollwheel: true,
25265     streetViewControl: false,
25266     radius: 0,
25267     locationName: '',
25268     draggable: true,
25269     enableAutocomplete: false,
25270     enableReverseGeocode: true,
25271     markerTitle: '',
25272     
25273     getAutoCreate: function()
25274     {
25275
25276         var cfg = {
25277             tag: 'div',
25278             cls: 'roo-location-picker'
25279         };
25280         
25281         return cfg
25282     },
25283     
25284     initEvents: function(ct, position)
25285     {       
25286         if(!this.el.getWidth() || this.isApplied()){
25287             return;
25288         }
25289         
25290         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25291         
25292         this.initial();
25293     },
25294     
25295     initial: function()
25296     {
25297         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25298             this.fireEvent('loadexception', this);
25299             return;
25300         }
25301         
25302         if(!this.mapTypeId){
25303             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25304         }
25305         
25306         this.gMapContext = this.GMapContext();
25307         
25308         this.initOverlayView();
25309         
25310         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25311         
25312         var _this = this;
25313                 
25314         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25315             _this.setPosition(_this.gMapContext.marker.position);
25316         });
25317         
25318         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25319             _this.fireEvent('mapClick', this, event);
25320             
25321         });
25322
25323         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25324             _this.fireEvent('mapRightClick', this, event);
25325             
25326         });
25327         
25328         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25329             _this.fireEvent('markerClick', this, event);
25330             
25331         });
25332
25333         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25334             _this.fireEvent('markerRightClick', this, event);
25335             
25336         });
25337         
25338         this.setPosition(this.gMapContext.location);
25339         
25340         this.fireEvent('initial', this, this.gMapContext.location);
25341     },
25342     
25343     initOverlayView: function()
25344     {
25345         var _this = this;
25346         
25347         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25348             
25349             draw: function()
25350             {
25351                 _this.fireEvent('OverlayViewDraw', _this);
25352             },
25353             
25354             onAdd: function()
25355             {
25356                 _this.fireEvent('OverlayViewOnAdd', _this);
25357             },
25358             
25359             onRemove: function()
25360             {
25361                 _this.fireEvent('OverlayViewOnRemove', _this);
25362             },
25363             
25364             show: function(cpx)
25365             {
25366                 _this.fireEvent('OverlayViewShow', _this, cpx);
25367             },
25368             
25369             hide: function()
25370             {
25371                 _this.fireEvent('OverlayViewHide', _this);
25372             }
25373             
25374         });
25375     },
25376     
25377     fromLatLngToContainerPixel: function(event)
25378     {
25379         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25380     },
25381     
25382     isApplied: function() 
25383     {
25384         return this.getGmapContext() == false ? false : true;
25385     },
25386     
25387     getGmapContext: function() 
25388     {
25389         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25390     },
25391     
25392     GMapContext: function() 
25393     {
25394         var position = new google.maps.LatLng(this.latitude, this.longitude);
25395         
25396         var _map = new google.maps.Map(this.el.dom, {
25397             center: position,
25398             zoom: this.zoom,
25399             mapTypeId: this.mapTypeId,
25400             mapTypeControl: this.mapTypeControl,
25401             disableDoubleClickZoom: this.disableDoubleClickZoom,
25402             scrollwheel: this.scrollwheel,
25403             streetViewControl: this.streetViewControl,
25404             locationName: this.locationName,
25405             draggable: this.draggable,
25406             enableAutocomplete: this.enableAutocomplete,
25407             enableReverseGeocode: this.enableReverseGeocode
25408         });
25409         
25410         var _marker = new google.maps.Marker({
25411             position: position,
25412             map: _map,
25413             title: this.markerTitle,
25414             draggable: this.draggable
25415         });
25416         
25417         return {
25418             map: _map,
25419             marker: _marker,
25420             circle: null,
25421             location: position,
25422             radius: this.radius,
25423             locationName: this.locationName,
25424             addressComponents: {
25425                 formatted_address: null,
25426                 addressLine1: null,
25427                 addressLine2: null,
25428                 streetName: null,
25429                 streetNumber: null,
25430                 city: null,
25431                 district: null,
25432                 state: null,
25433                 stateOrProvince: null
25434             },
25435             settings: this,
25436             domContainer: this.el.dom,
25437             geodecoder: new google.maps.Geocoder()
25438         };
25439     },
25440     
25441     drawCircle: function(center, radius, options) 
25442     {
25443         if (this.gMapContext.circle != null) {
25444             this.gMapContext.circle.setMap(null);
25445         }
25446         if (radius > 0) {
25447             radius *= 1;
25448             options = Roo.apply({}, options, {
25449                 strokeColor: "#0000FF",
25450                 strokeOpacity: .35,
25451                 strokeWeight: 2,
25452                 fillColor: "#0000FF",
25453                 fillOpacity: .2
25454             });
25455             
25456             options.map = this.gMapContext.map;
25457             options.radius = radius;
25458             options.center = center;
25459             this.gMapContext.circle = new google.maps.Circle(options);
25460             return this.gMapContext.circle;
25461         }
25462         
25463         return null;
25464     },
25465     
25466     setPosition: function(location) 
25467     {
25468         this.gMapContext.location = location;
25469         this.gMapContext.marker.setPosition(location);
25470         this.gMapContext.map.panTo(location);
25471         this.drawCircle(location, this.gMapContext.radius, {});
25472         
25473         var _this = this;
25474         
25475         if (this.gMapContext.settings.enableReverseGeocode) {
25476             this.gMapContext.geodecoder.geocode({
25477                 latLng: this.gMapContext.location
25478             }, function(results, status) {
25479                 
25480                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25481                     _this.gMapContext.locationName = results[0].formatted_address;
25482                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25483                     
25484                     _this.fireEvent('positionchanged', this, location);
25485                 }
25486             });
25487             
25488             return;
25489         }
25490         
25491         this.fireEvent('positionchanged', this, location);
25492     },
25493     
25494     resize: function()
25495     {
25496         google.maps.event.trigger(this.gMapContext.map, "resize");
25497         
25498         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25499         
25500         this.fireEvent('resize', this);
25501     },
25502     
25503     setPositionByLatLng: function(latitude, longitude)
25504     {
25505         this.setPosition(new google.maps.LatLng(latitude, longitude));
25506     },
25507     
25508     getCurrentPosition: function() 
25509     {
25510         return {
25511             latitude: this.gMapContext.location.lat(),
25512             longitude: this.gMapContext.location.lng()
25513         };
25514     },
25515     
25516     getAddressName: function() 
25517     {
25518         return this.gMapContext.locationName;
25519     },
25520     
25521     getAddressComponents: function() 
25522     {
25523         return this.gMapContext.addressComponents;
25524     },
25525     
25526     address_component_from_google_geocode: function(address_components) 
25527     {
25528         var result = {};
25529         
25530         for (var i = 0; i < address_components.length; i++) {
25531             var component = address_components[i];
25532             if (component.types.indexOf("postal_code") >= 0) {
25533                 result.postalCode = component.short_name;
25534             } else if (component.types.indexOf("street_number") >= 0) {
25535                 result.streetNumber = component.short_name;
25536             } else if (component.types.indexOf("route") >= 0) {
25537                 result.streetName = component.short_name;
25538             } else if (component.types.indexOf("neighborhood") >= 0) {
25539                 result.city = component.short_name;
25540             } else if (component.types.indexOf("locality") >= 0) {
25541                 result.city = component.short_name;
25542             } else if (component.types.indexOf("sublocality") >= 0) {
25543                 result.district = component.short_name;
25544             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25545                 result.stateOrProvince = component.short_name;
25546             } else if (component.types.indexOf("country") >= 0) {
25547                 result.country = component.short_name;
25548             }
25549         }
25550         
25551         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25552         result.addressLine2 = "";
25553         return result;
25554     },
25555     
25556     setZoomLevel: function(zoom)
25557     {
25558         this.gMapContext.map.setZoom(zoom);
25559     },
25560     
25561     show: function()
25562     {
25563         if(!this.el){
25564             return;
25565         }
25566         
25567         this.el.show();
25568         
25569         this.resize();
25570         
25571         this.fireEvent('show', this);
25572     },
25573     
25574     hide: function()
25575     {
25576         if(!this.el){
25577             return;
25578         }
25579         
25580         this.el.hide();
25581         
25582         this.fireEvent('hide', this);
25583     }
25584     
25585 });
25586
25587 Roo.apply(Roo.bootstrap.LocationPicker, {
25588     
25589     OverlayView : function(map, options)
25590     {
25591         options = options || {};
25592         
25593         this.setMap(map);
25594     }
25595     
25596     
25597 });/*
25598  * - LGPL
25599  *
25600  * Alert
25601  * 
25602  */
25603
25604 /**
25605  * @class Roo.bootstrap.Alert
25606  * @extends Roo.bootstrap.Component
25607  * Bootstrap Alert class
25608  * @cfg {String} title The title of alert
25609  * @cfg {String} html The content of alert
25610  * @cfg {String} weight (  success | info | warning | danger )
25611  * @cfg {String} faicon font-awesomeicon
25612  * 
25613  * @constructor
25614  * Create a new alert
25615  * @param {Object} config The config object
25616  */
25617
25618
25619 Roo.bootstrap.Alert = function(config){
25620     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25621     
25622 };
25623
25624 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25625     
25626     title: '',
25627     html: '',
25628     weight: false,
25629     faicon: false,
25630     
25631     getAutoCreate : function()
25632     {
25633         
25634         var cfg = {
25635             tag : 'div',
25636             cls : 'alert',
25637             cn : [
25638                 {
25639                     tag : 'i',
25640                     cls : 'roo-alert-icon'
25641                     
25642                 },
25643                 {
25644                     tag : 'b',
25645                     cls : 'roo-alert-title',
25646                     html : this.title
25647                 },
25648                 {
25649                     tag : 'span',
25650                     cls : 'roo-alert-text',
25651                     html : this.html
25652                 }
25653             ]
25654         };
25655         
25656         if(this.faicon){
25657             cfg.cn[0].cls += ' fa ' + this.faicon;
25658         }
25659         
25660         if(this.weight){
25661             cfg.cls += ' alert-' + this.weight;
25662         }
25663         
25664         return cfg;
25665     },
25666     
25667     initEvents: function() 
25668     {
25669         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25670     },
25671     
25672     setTitle : function(str)
25673     {
25674         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25675     },
25676     
25677     setText : function(str)
25678     {
25679         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25680     },
25681     
25682     setWeight : function(weight)
25683     {
25684         if(this.weight){
25685             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25686         }
25687         
25688         this.weight = weight;
25689         
25690         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25691     },
25692     
25693     setIcon : function(icon)
25694     {
25695         if(this.faicon){
25696             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25697         }
25698         
25699         this.faicon = icon;
25700         
25701         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25702     },
25703     
25704     hide: function() 
25705     {
25706         this.el.hide();   
25707     },
25708     
25709     show: function() 
25710     {  
25711         this.el.show();   
25712     }
25713     
25714 });
25715
25716  
25717 /*
25718 * Licence: LGPL
25719 */
25720
25721 /**
25722  * @class Roo.bootstrap.UploadCropbox
25723  * @extends Roo.bootstrap.Component
25724  * Bootstrap UploadCropbox class
25725  * @cfg {String} emptyText show when image has been loaded
25726  * @cfg {String} rotateNotify show when image too small to rotate
25727  * @cfg {Number} errorTimeout default 3000
25728  * @cfg {Number} minWidth default 300
25729  * @cfg {Number} minHeight default 300
25730  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25731  * @cfg {Boolean} isDocument (true|false) default false
25732  * @cfg {String} url action url
25733  * @cfg {String} paramName default 'imageUpload'
25734  * @cfg {String} method default POST
25735  * @cfg {Boolean} loadMask (true|false) default true
25736  * @cfg {Boolean} loadingText default 'Loading...'
25737  * 
25738  * @constructor
25739  * Create a new UploadCropbox
25740  * @param {Object} config The config object
25741  */
25742
25743 Roo.bootstrap.UploadCropbox = function(config){
25744     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25745     
25746     this.addEvents({
25747         /**
25748          * @event beforeselectfile
25749          * Fire before select file
25750          * @param {Roo.bootstrap.UploadCropbox} this
25751          */
25752         "beforeselectfile" : true,
25753         /**
25754          * @event initial
25755          * Fire after initEvent
25756          * @param {Roo.bootstrap.UploadCropbox} this
25757          */
25758         "initial" : true,
25759         /**
25760          * @event crop
25761          * Fire after initEvent
25762          * @param {Roo.bootstrap.UploadCropbox} this
25763          * @param {String} data
25764          */
25765         "crop" : true,
25766         /**
25767          * @event prepare
25768          * Fire when preparing the file data
25769          * @param {Roo.bootstrap.UploadCropbox} this
25770          * @param {Object} file
25771          */
25772         "prepare" : true,
25773         /**
25774          * @event exception
25775          * Fire when get exception
25776          * @param {Roo.bootstrap.UploadCropbox} this
25777          * @param {XMLHttpRequest} xhr
25778          */
25779         "exception" : true,
25780         /**
25781          * @event beforeloadcanvas
25782          * Fire before load the canvas
25783          * @param {Roo.bootstrap.UploadCropbox} this
25784          * @param {String} src
25785          */
25786         "beforeloadcanvas" : true,
25787         /**
25788          * @event trash
25789          * Fire when trash image
25790          * @param {Roo.bootstrap.UploadCropbox} this
25791          */
25792         "trash" : true,
25793         /**
25794          * @event download
25795          * Fire when download the image
25796          * @param {Roo.bootstrap.UploadCropbox} this
25797          */
25798         "download" : true,
25799         /**
25800          * @event footerbuttonclick
25801          * Fire when footerbuttonclick
25802          * @param {Roo.bootstrap.UploadCropbox} this
25803          * @param {String} type
25804          */
25805         "footerbuttonclick" : true,
25806         /**
25807          * @event resize
25808          * Fire when resize
25809          * @param {Roo.bootstrap.UploadCropbox} this
25810          */
25811         "resize" : true,
25812         /**
25813          * @event rotate
25814          * Fire when rotate the image
25815          * @param {Roo.bootstrap.UploadCropbox} this
25816          * @param {String} pos
25817          */
25818         "rotate" : true,
25819         /**
25820          * @event inspect
25821          * Fire when inspect the file
25822          * @param {Roo.bootstrap.UploadCropbox} this
25823          * @param {Object} file
25824          */
25825         "inspect" : true,
25826         /**
25827          * @event upload
25828          * Fire when xhr upload the file
25829          * @param {Roo.bootstrap.UploadCropbox} this
25830          * @param {Object} data
25831          */
25832         "upload" : true,
25833         /**
25834          * @event arrange
25835          * Fire when arrange the file data
25836          * @param {Roo.bootstrap.UploadCropbox} this
25837          * @param {Object} formData
25838          */
25839         "arrange" : true
25840     });
25841     
25842     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25843 };
25844
25845 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25846     
25847     emptyText : 'Click to upload image',
25848     rotateNotify : 'Image is too small to rotate',
25849     errorTimeout : 3000,
25850     scale : 0,
25851     baseScale : 1,
25852     rotate : 0,
25853     dragable : false,
25854     pinching : false,
25855     mouseX : 0,
25856     mouseY : 0,
25857     cropData : false,
25858     minWidth : 300,
25859     minHeight : 300,
25860     file : false,
25861     exif : {},
25862     baseRotate : 1,
25863     cropType : 'image/jpeg',
25864     buttons : false,
25865     canvasLoaded : false,
25866     isDocument : false,
25867     method : 'POST',
25868     paramName : 'imageUpload',
25869     loadMask : true,
25870     loadingText : 'Loading...',
25871     maskEl : false,
25872     
25873     getAutoCreate : function()
25874     {
25875         var cfg = {
25876             tag : 'div',
25877             cls : 'roo-upload-cropbox',
25878             cn : [
25879                 {
25880                     tag : 'input',
25881                     cls : 'roo-upload-cropbox-selector',
25882                     type : 'file'
25883                 },
25884                 {
25885                     tag : 'div',
25886                     cls : 'roo-upload-cropbox-body',
25887                     style : 'cursor:pointer',
25888                     cn : [
25889                         {
25890                             tag : 'div',
25891                             cls : 'roo-upload-cropbox-preview'
25892                         },
25893                         {
25894                             tag : 'div',
25895                             cls : 'roo-upload-cropbox-thumb'
25896                         },
25897                         {
25898                             tag : 'div',
25899                             cls : 'roo-upload-cropbox-empty-notify',
25900                             html : this.emptyText
25901                         },
25902                         {
25903                             tag : 'div',
25904                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25905                             html : this.rotateNotify
25906                         }
25907                     ]
25908                 },
25909                 {
25910                     tag : 'div',
25911                     cls : 'roo-upload-cropbox-footer',
25912                     cn : {
25913                         tag : 'div',
25914                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25915                         cn : []
25916                     }
25917                 }
25918             ]
25919         };
25920         
25921         return cfg;
25922     },
25923     
25924     onRender : function(ct, position)
25925     {
25926         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25927         
25928         if (this.buttons.length) {
25929             
25930             Roo.each(this.buttons, function(bb) {
25931                 
25932                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25933                 
25934                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25935                 
25936             }, this);
25937         }
25938         
25939         if(this.loadMask){
25940             this.maskEl = this.el;
25941         }
25942     },
25943     
25944     initEvents : function()
25945     {
25946         this.urlAPI = (window.createObjectURL && window) || 
25947                                 (window.URL && URL.revokeObjectURL && URL) || 
25948                                 (window.webkitURL && webkitURL);
25949                         
25950         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25951         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25952         
25953         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25954         this.selectorEl.hide();
25955         
25956         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25957         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25958         
25959         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25960         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25961         this.thumbEl.hide();
25962         
25963         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25964         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25965         
25966         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25967         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25968         this.errorEl.hide();
25969         
25970         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25971         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25972         this.footerEl.hide();
25973         
25974         this.setThumbBoxSize();
25975         
25976         this.bind();
25977         
25978         this.resize();
25979         
25980         this.fireEvent('initial', this);
25981     },
25982
25983     bind : function()
25984     {
25985         var _this = this;
25986         
25987         window.addEventListener("resize", function() { _this.resize(); } );
25988         
25989         this.bodyEl.on('click', this.beforeSelectFile, this);
25990         
25991         if(Roo.isTouch){
25992             this.bodyEl.on('touchstart', this.onTouchStart, this);
25993             this.bodyEl.on('touchmove', this.onTouchMove, this);
25994             this.bodyEl.on('touchend', this.onTouchEnd, this);
25995         }
25996         
25997         if(!Roo.isTouch){
25998             this.bodyEl.on('mousedown', this.onMouseDown, this);
25999             this.bodyEl.on('mousemove', this.onMouseMove, this);
26000             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26001             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26002             Roo.get(document).on('mouseup', this.onMouseUp, this);
26003         }
26004         
26005         this.selectorEl.on('change', this.onFileSelected, this);
26006     },
26007     
26008     reset : function()
26009     {    
26010         this.scale = 0;
26011         this.baseScale = 1;
26012         this.rotate = 0;
26013         this.baseRotate = 1;
26014         this.dragable = false;
26015         this.pinching = false;
26016         this.mouseX = 0;
26017         this.mouseY = 0;
26018         this.cropData = false;
26019         this.notifyEl.dom.innerHTML = this.emptyText;
26020         
26021         this.selectorEl.dom.value = '';
26022         
26023     },
26024     
26025     resize : function()
26026     {
26027         if(this.fireEvent('resize', this) != false){
26028             this.setThumbBoxPosition();
26029             this.setCanvasPosition();
26030         }
26031     },
26032     
26033     onFooterButtonClick : function(e, el, o, type)
26034     {
26035         switch (type) {
26036             case 'rotate-left' :
26037                 this.onRotateLeft(e);
26038                 break;
26039             case 'rotate-right' :
26040                 this.onRotateRight(e);
26041                 break;
26042             case 'picture' :
26043                 this.beforeSelectFile(e);
26044                 break;
26045             case 'trash' :
26046                 this.trash(e);
26047                 break;
26048             case 'crop' :
26049                 this.crop(e);
26050                 break;
26051             case 'download' :
26052                 this.download(e);
26053                 break;
26054             default :
26055                 break;
26056         }
26057         
26058         this.fireEvent('footerbuttonclick', this, type);
26059     },
26060     
26061     beforeSelectFile : function(e)
26062     {
26063         e.preventDefault();
26064         
26065         if(this.fireEvent('beforeselectfile', this) != false){
26066             this.selectorEl.dom.click();
26067         }
26068     },
26069     
26070     onFileSelected : function(e)
26071     {
26072         e.preventDefault();
26073         
26074         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26075             return;
26076         }
26077         
26078         var file = this.selectorEl.dom.files[0];
26079         
26080         if(this.fireEvent('inspect', this, file) != false){
26081             this.prepare(file);
26082         }
26083         
26084     },
26085     
26086     trash : function(e)
26087     {
26088         this.fireEvent('trash', this);
26089     },
26090     
26091     download : function(e)
26092     {
26093         this.fireEvent('download', this);
26094     },
26095     
26096     loadCanvas : function(src)
26097     {   
26098         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26099             
26100             this.reset();
26101             
26102             this.imageEl = document.createElement('img');
26103             
26104             var _this = this;
26105             
26106             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26107             
26108             this.imageEl.src = src;
26109         }
26110     },
26111     
26112     onLoadCanvas : function()
26113     {   
26114         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26115         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26116         
26117         this.bodyEl.un('click', this.beforeSelectFile, this);
26118         
26119         this.notifyEl.hide();
26120         this.thumbEl.show();
26121         this.footerEl.show();
26122         
26123         this.baseRotateLevel();
26124         
26125         if(this.isDocument){
26126             this.setThumbBoxSize();
26127         }
26128         
26129         this.setThumbBoxPosition();
26130         
26131         this.baseScaleLevel();
26132         
26133         this.draw();
26134         
26135         this.resize();
26136         
26137         this.canvasLoaded = true;
26138         
26139         if(this.loadMask){
26140             this.maskEl.unmask();
26141         }
26142         
26143     },
26144     
26145     setCanvasPosition : function()
26146     {   
26147         if(!this.canvasEl){
26148             return;
26149         }
26150         
26151         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26152         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26153         
26154         this.previewEl.setLeft(pw);
26155         this.previewEl.setTop(ph);
26156         
26157     },
26158     
26159     onMouseDown : function(e)
26160     {   
26161         e.stopEvent();
26162         
26163         this.dragable = true;
26164         this.pinching = false;
26165         
26166         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26167             this.dragable = false;
26168             return;
26169         }
26170         
26171         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26172         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26173         
26174     },
26175     
26176     onMouseMove : function(e)
26177     {   
26178         e.stopEvent();
26179         
26180         if(!this.canvasLoaded){
26181             return;
26182         }
26183         
26184         if (!this.dragable){
26185             return;
26186         }
26187         
26188         var minX = Math.ceil(this.thumbEl.getLeft(true));
26189         var minY = Math.ceil(this.thumbEl.getTop(true));
26190         
26191         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26192         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26193         
26194         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26195         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26196         
26197         x = x - this.mouseX;
26198         y = y - this.mouseY;
26199         
26200         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26201         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26202         
26203         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26204         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26205         
26206         this.previewEl.setLeft(bgX);
26207         this.previewEl.setTop(bgY);
26208         
26209         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26210         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26211     },
26212     
26213     onMouseUp : function(e)
26214     {   
26215         e.stopEvent();
26216         
26217         this.dragable = false;
26218     },
26219     
26220     onMouseWheel : function(e)
26221     {   
26222         e.stopEvent();
26223         
26224         this.startScale = this.scale;
26225         
26226         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26227         
26228         if(!this.zoomable()){
26229             this.scale = this.startScale;
26230             return;
26231         }
26232         
26233         this.draw();
26234         
26235         return;
26236     },
26237     
26238     zoomable : function()
26239     {
26240         var minScale = this.thumbEl.getWidth() / this.minWidth;
26241         
26242         if(this.minWidth < this.minHeight){
26243             minScale = this.thumbEl.getHeight() / this.minHeight;
26244         }
26245         
26246         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26247         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26248         
26249         if(
26250                 this.isDocument &&
26251                 (this.rotate == 0 || this.rotate == 180) && 
26252                 (
26253                     width > this.imageEl.OriginWidth || 
26254                     height > this.imageEl.OriginHeight ||
26255                     (width < this.minWidth && height < this.minHeight)
26256                 )
26257         ){
26258             return false;
26259         }
26260         
26261         if(
26262                 this.isDocument &&
26263                 (this.rotate == 90 || this.rotate == 270) && 
26264                 (
26265                     width > this.imageEl.OriginWidth || 
26266                     height > this.imageEl.OriginHeight ||
26267                     (width < this.minHeight && height < this.minWidth)
26268                 )
26269         ){
26270             return false;
26271         }
26272         
26273         if(
26274                 !this.isDocument &&
26275                 (this.rotate == 0 || this.rotate == 180) && 
26276                 (
26277                     width < this.minWidth || 
26278                     width > this.imageEl.OriginWidth || 
26279                     height < this.minHeight || 
26280                     height > this.imageEl.OriginHeight
26281                 )
26282         ){
26283             return false;
26284         }
26285         
26286         if(
26287                 !this.isDocument &&
26288                 (this.rotate == 90 || this.rotate == 270) && 
26289                 (
26290                     width < this.minHeight || 
26291                     width > this.imageEl.OriginWidth || 
26292                     height < this.minWidth || 
26293                     height > this.imageEl.OriginHeight
26294                 )
26295         ){
26296             return false;
26297         }
26298         
26299         return true;
26300         
26301     },
26302     
26303     onRotateLeft : function(e)
26304     {   
26305         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26306             
26307             var minScale = this.thumbEl.getWidth() / this.minWidth;
26308             
26309             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26310             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26311             
26312             this.startScale = this.scale;
26313             
26314             while (this.getScaleLevel() < minScale){
26315             
26316                 this.scale = this.scale + 1;
26317                 
26318                 if(!this.zoomable()){
26319                     break;
26320                 }
26321                 
26322                 if(
26323                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26324                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26325                 ){
26326                     continue;
26327                 }
26328                 
26329                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26330
26331                 this.draw();
26332                 
26333                 return;
26334             }
26335             
26336             this.scale = this.startScale;
26337             
26338             this.onRotateFail();
26339             
26340             return false;
26341         }
26342         
26343         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26344
26345         if(this.isDocument){
26346             this.setThumbBoxSize();
26347             this.setThumbBoxPosition();
26348             this.setCanvasPosition();
26349         }
26350         
26351         this.draw();
26352         
26353         this.fireEvent('rotate', this, 'left');
26354         
26355     },
26356     
26357     onRotateRight : function(e)
26358     {
26359         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26360             
26361             var minScale = this.thumbEl.getWidth() / this.minWidth;
26362         
26363             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26364             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26365             
26366             this.startScale = this.scale;
26367             
26368             while (this.getScaleLevel() < minScale){
26369             
26370                 this.scale = this.scale + 1;
26371                 
26372                 if(!this.zoomable()){
26373                     break;
26374                 }
26375                 
26376                 if(
26377                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26378                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26379                 ){
26380                     continue;
26381                 }
26382                 
26383                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26384
26385                 this.draw();
26386                 
26387                 return;
26388             }
26389             
26390             this.scale = this.startScale;
26391             
26392             this.onRotateFail();
26393             
26394             return false;
26395         }
26396         
26397         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26398
26399         if(this.isDocument){
26400             this.setThumbBoxSize();
26401             this.setThumbBoxPosition();
26402             this.setCanvasPosition();
26403         }
26404         
26405         this.draw();
26406         
26407         this.fireEvent('rotate', this, 'right');
26408     },
26409     
26410     onRotateFail : function()
26411     {
26412         this.errorEl.show(true);
26413         
26414         var _this = this;
26415         
26416         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26417     },
26418     
26419     draw : function()
26420     {
26421         this.previewEl.dom.innerHTML = '';
26422         
26423         var canvasEl = document.createElement("canvas");
26424         
26425         var contextEl = canvasEl.getContext("2d");
26426         
26427         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26428         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26429         var center = this.imageEl.OriginWidth / 2;
26430         
26431         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26432             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26433             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26434             center = this.imageEl.OriginHeight / 2;
26435         }
26436         
26437         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26438         
26439         contextEl.translate(center, center);
26440         contextEl.rotate(this.rotate * Math.PI / 180);
26441
26442         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26443         
26444         this.canvasEl = document.createElement("canvas");
26445         
26446         this.contextEl = this.canvasEl.getContext("2d");
26447         
26448         switch (this.rotate) {
26449             case 0 :
26450                 
26451                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26452                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26453                 
26454                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26455                 
26456                 break;
26457             case 90 : 
26458                 
26459                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26460                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26461                 
26462                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26463                     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);
26464                     break;
26465                 }
26466                 
26467                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26468                 
26469                 break;
26470             case 180 :
26471                 
26472                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26473                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26474                 
26475                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26476                     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);
26477                     break;
26478                 }
26479                 
26480                 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);
26481                 
26482                 break;
26483             case 270 :
26484                 
26485                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26486                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26487         
26488                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26489                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26490                     break;
26491                 }
26492                 
26493                 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);
26494                 
26495                 break;
26496             default : 
26497                 break;
26498         }
26499         
26500         this.previewEl.appendChild(this.canvasEl);
26501         
26502         this.setCanvasPosition();
26503     },
26504     
26505     crop : function()
26506     {
26507         if(!this.canvasLoaded){
26508             return;
26509         }
26510         
26511         var imageCanvas = document.createElement("canvas");
26512         
26513         var imageContext = imageCanvas.getContext("2d");
26514         
26515         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26516         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26517         
26518         var center = imageCanvas.width / 2;
26519         
26520         imageContext.translate(center, center);
26521         
26522         imageContext.rotate(this.rotate * Math.PI / 180);
26523         
26524         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26525         
26526         var canvas = document.createElement("canvas");
26527         
26528         var context = canvas.getContext("2d");
26529                 
26530         canvas.width = this.minWidth;
26531         canvas.height = this.minHeight;
26532
26533         switch (this.rotate) {
26534             case 0 :
26535                 
26536                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26537                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26538                 
26539                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26540                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26541                 
26542                 var targetWidth = this.minWidth - 2 * x;
26543                 var targetHeight = this.minHeight - 2 * y;
26544                 
26545                 var scale = 1;
26546                 
26547                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26548                     scale = targetWidth / width;
26549                 }
26550                 
26551                 if(x > 0 && y == 0){
26552                     scale = targetHeight / height;
26553                 }
26554                 
26555                 if(x > 0 && y > 0){
26556                     scale = targetWidth / width;
26557                     
26558                     if(width < height){
26559                         scale = targetHeight / height;
26560                     }
26561                 }
26562                 
26563                 context.scale(scale, scale);
26564                 
26565                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26566                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26567
26568                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26569                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26570
26571                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26572                 
26573                 break;
26574             case 90 : 
26575                 
26576                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26577                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26578                 
26579                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26580                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26581                 
26582                 var targetWidth = this.minWidth - 2 * x;
26583                 var targetHeight = this.minHeight - 2 * y;
26584                 
26585                 var scale = 1;
26586                 
26587                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26588                     scale = targetWidth / width;
26589                 }
26590                 
26591                 if(x > 0 && y == 0){
26592                     scale = targetHeight / height;
26593                 }
26594                 
26595                 if(x > 0 && y > 0){
26596                     scale = targetWidth / width;
26597                     
26598                     if(width < height){
26599                         scale = targetHeight / height;
26600                     }
26601                 }
26602                 
26603                 context.scale(scale, scale);
26604                 
26605                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26606                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26607
26608                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26609                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26610                 
26611                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26612                 
26613                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26614                 
26615                 break;
26616             case 180 :
26617                 
26618                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26619                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26620                 
26621                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26622                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26623                 
26624                 var targetWidth = this.minWidth - 2 * x;
26625                 var targetHeight = this.minHeight - 2 * y;
26626                 
26627                 var scale = 1;
26628                 
26629                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26630                     scale = targetWidth / width;
26631                 }
26632                 
26633                 if(x > 0 && y == 0){
26634                     scale = targetHeight / height;
26635                 }
26636                 
26637                 if(x > 0 && y > 0){
26638                     scale = targetWidth / width;
26639                     
26640                     if(width < height){
26641                         scale = targetHeight / height;
26642                     }
26643                 }
26644                 
26645                 context.scale(scale, scale);
26646                 
26647                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26648                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26649
26650                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26651                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26652
26653                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26654                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26655                 
26656                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26657                 
26658                 break;
26659             case 270 :
26660                 
26661                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26662                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26663                 
26664                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26665                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26666                 
26667                 var targetWidth = this.minWidth - 2 * x;
26668                 var targetHeight = this.minHeight - 2 * y;
26669                 
26670                 var scale = 1;
26671                 
26672                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26673                     scale = targetWidth / width;
26674                 }
26675                 
26676                 if(x > 0 && y == 0){
26677                     scale = targetHeight / height;
26678                 }
26679                 
26680                 if(x > 0 && y > 0){
26681                     scale = targetWidth / width;
26682                     
26683                     if(width < height){
26684                         scale = targetHeight / height;
26685                     }
26686                 }
26687                 
26688                 context.scale(scale, scale);
26689                 
26690                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26691                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26692
26693                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26694                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26695                 
26696                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26697                 
26698                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26699                 
26700                 break;
26701             default : 
26702                 break;
26703         }
26704         
26705         this.cropData = canvas.toDataURL(this.cropType);
26706         
26707         if(this.fireEvent('crop', this, this.cropData) !== false){
26708             this.process(this.file, this.cropData);
26709         }
26710         
26711         return;
26712         
26713     },
26714     
26715     setThumbBoxSize : function()
26716     {
26717         var width, height;
26718         
26719         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26720             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26721             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26722             
26723             this.minWidth = width;
26724             this.minHeight = height;
26725             
26726             if(this.rotate == 90 || this.rotate == 270){
26727                 this.minWidth = height;
26728                 this.minHeight = width;
26729             }
26730         }
26731         
26732         height = 300;
26733         width = Math.ceil(this.minWidth * height / this.minHeight);
26734         
26735         if(this.minWidth > this.minHeight){
26736             width = 300;
26737             height = Math.ceil(this.minHeight * width / this.minWidth);
26738         }
26739         
26740         this.thumbEl.setStyle({
26741             width : width + 'px',
26742             height : height + 'px'
26743         });
26744
26745         return;
26746             
26747     },
26748     
26749     setThumbBoxPosition : function()
26750     {
26751         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26752         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26753         
26754         this.thumbEl.setLeft(x);
26755         this.thumbEl.setTop(y);
26756         
26757     },
26758     
26759     baseRotateLevel : function()
26760     {
26761         this.baseRotate = 1;
26762         
26763         if(
26764                 typeof(this.exif) != 'undefined' &&
26765                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26766                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26767         ){
26768             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26769         }
26770         
26771         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26772         
26773     },
26774     
26775     baseScaleLevel : function()
26776     {
26777         var width, height;
26778         
26779         if(this.isDocument){
26780             
26781             if(this.baseRotate == 6 || this.baseRotate == 8){
26782             
26783                 height = this.thumbEl.getHeight();
26784                 this.baseScale = height / this.imageEl.OriginWidth;
26785
26786                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26787                     width = this.thumbEl.getWidth();
26788                     this.baseScale = width / this.imageEl.OriginHeight;
26789                 }
26790
26791                 return;
26792             }
26793
26794             height = this.thumbEl.getHeight();
26795             this.baseScale = height / this.imageEl.OriginHeight;
26796
26797             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26798                 width = this.thumbEl.getWidth();
26799                 this.baseScale = width / this.imageEl.OriginWidth;
26800             }
26801
26802             return;
26803         }
26804         
26805         if(this.baseRotate == 6 || this.baseRotate == 8){
26806             
26807             width = this.thumbEl.getHeight();
26808             this.baseScale = width / this.imageEl.OriginHeight;
26809             
26810             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26811                 height = this.thumbEl.getWidth();
26812                 this.baseScale = height / this.imageEl.OriginHeight;
26813             }
26814             
26815             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26816                 height = this.thumbEl.getWidth();
26817                 this.baseScale = height / this.imageEl.OriginHeight;
26818                 
26819                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26820                     width = this.thumbEl.getHeight();
26821                     this.baseScale = width / this.imageEl.OriginWidth;
26822                 }
26823             }
26824             
26825             return;
26826         }
26827         
26828         width = this.thumbEl.getWidth();
26829         this.baseScale = width / this.imageEl.OriginWidth;
26830         
26831         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26832             height = this.thumbEl.getHeight();
26833             this.baseScale = height / this.imageEl.OriginHeight;
26834         }
26835         
26836         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26837             
26838             height = this.thumbEl.getHeight();
26839             this.baseScale = height / this.imageEl.OriginHeight;
26840             
26841             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26842                 width = this.thumbEl.getWidth();
26843                 this.baseScale = width / this.imageEl.OriginWidth;
26844             }
26845             
26846         }
26847         
26848         return;
26849     },
26850     
26851     getScaleLevel : function()
26852     {
26853         return this.baseScale * Math.pow(1.1, this.scale);
26854     },
26855     
26856     onTouchStart : function(e)
26857     {
26858         if(!this.canvasLoaded){
26859             this.beforeSelectFile(e);
26860             return;
26861         }
26862         
26863         var touches = e.browserEvent.touches;
26864         
26865         if(!touches){
26866             return;
26867         }
26868         
26869         if(touches.length == 1){
26870             this.onMouseDown(e);
26871             return;
26872         }
26873         
26874         if(touches.length != 2){
26875             return;
26876         }
26877         
26878         var coords = [];
26879         
26880         for(var i = 0, finger; finger = touches[i]; i++){
26881             coords.push(finger.pageX, finger.pageY);
26882         }
26883         
26884         var x = Math.pow(coords[0] - coords[2], 2);
26885         var y = Math.pow(coords[1] - coords[3], 2);
26886         
26887         this.startDistance = Math.sqrt(x + y);
26888         
26889         this.startScale = this.scale;
26890         
26891         this.pinching = true;
26892         this.dragable = false;
26893         
26894     },
26895     
26896     onTouchMove : function(e)
26897     {
26898         if(!this.pinching && !this.dragable){
26899             return;
26900         }
26901         
26902         var touches = e.browserEvent.touches;
26903         
26904         if(!touches){
26905             return;
26906         }
26907         
26908         if(this.dragable){
26909             this.onMouseMove(e);
26910             return;
26911         }
26912         
26913         var coords = [];
26914         
26915         for(var i = 0, finger; finger = touches[i]; i++){
26916             coords.push(finger.pageX, finger.pageY);
26917         }
26918         
26919         var x = Math.pow(coords[0] - coords[2], 2);
26920         var y = Math.pow(coords[1] - coords[3], 2);
26921         
26922         this.endDistance = Math.sqrt(x + y);
26923         
26924         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26925         
26926         if(!this.zoomable()){
26927             this.scale = this.startScale;
26928             return;
26929         }
26930         
26931         this.draw();
26932         
26933     },
26934     
26935     onTouchEnd : function(e)
26936     {
26937         this.pinching = false;
26938         this.dragable = false;
26939         
26940     },
26941     
26942     process : function(file, crop)
26943     {
26944         if(this.loadMask){
26945             this.maskEl.mask(this.loadingText);
26946         }
26947         
26948         this.xhr = new XMLHttpRequest();
26949         
26950         file.xhr = this.xhr;
26951
26952         this.xhr.open(this.method, this.url, true);
26953         
26954         var headers = {
26955             "Accept": "application/json",
26956             "Cache-Control": "no-cache",
26957             "X-Requested-With": "XMLHttpRequest"
26958         };
26959         
26960         for (var headerName in headers) {
26961             var headerValue = headers[headerName];
26962             if (headerValue) {
26963                 this.xhr.setRequestHeader(headerName, headerValue);
26964             }
26965         }
26966         
26967         var _this = this;
26968         
26969         this.xhr.onload = function()
26970         {
26971             _this.xhrOnLoad(_this.xhr);
26972         }
26973         
26974         this.xhr.onerror = function()
26975         {
26976             _this.xhrOnError(_this.xhr);
26977         }
26978         
26979         var formData = new FormData();
26980
26981         formData.append('returnHTML', 'NO');
26982         
26983         if(crop){
26984             formData.append('crop', crop);
26985         }
26986         
26987         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26988             formData.append(this.paramName, file, file.name);
26989         }
26990         
26991         if(typeof(file.filename) != 'undefined'){
26992             formData.append('filename', file.filename);
26993         }
26994         
26995         if(typeof(file.mimetype) != 'undefined'){
26996             formData.append('mimetype', file.mimetype);
26997         }
26998         
26999         if(this.fireEvent('arrange', this, formData) != false){
27000             this.xhr.send(formData);
27001         };
27002     },
27003     
27004     xhrOnLoad : function(xhr)
27005     {
27006         if(this.loadMask){
27007             this.maskEl.unmask();
27008         }
27009         
27010         if (xhr.readyState !== 4) {
27011             this.fireEvent('exception', this, xhr);
27012             return;
27013         }
27014
27015         var response = Roo.decode(xhr.responseText);
27016         
27017         if(!response.success){
27018             this.fireEvent('exception', this, xhr);
27019             return;
27020         }
27021         
27022         var response = Roo.decode(xhr.responseText);
27023         
27024         this.fireEvent('upload', this, response);
27025         
27026     },
27027     
27028     xhrOnError : function()
27029     {
27030         if(this.loadMask){
27031             this.maskEl.unmask();
27032         }
27033         
27034         Roo.log('xhr on error');
27035         
27036         var response = Roo.decode(xhr.responseText);
27037           
27038         Roo.log(response);
27039         
27040     },
27041     
27042     prepare : function(file)
27043     {   
27044         if(this.loadMask){
27045             this.maskEl.mask(this.loadingText);
27046         }
27047         
27048         this.file = false;
27049         this.exif = {};
27050         
27051         if(typeof(file) === 'string'){
27052             this.loadCanvas(file);
27053             return;
27054         }
27055         
27056         if(!file || !this.urlAPI){
27057             return;
27058         }
27059         
27060         this.file = file;
27061         this.cropType = file.type;
27062         
27063         var _this = this;
27064         
27065         if(this.fireEvent('prepare', this, this.file) != false){
27066             
27067             var reader = new FileReader();
27068             
27069             reader.onload = function (e) {
27070                 if (e.target.error) {
27071                     Roo.log(e.target.error);
27072                     return;
27073                 }
27074                 
27075                 var buffer = e.target.result,
27076                     dataView = new DataView(buffer),
27077                     offset = 2,
27078                     maxOffset = dataView.byteLength - 4,
27079                     markerBytes,
27080                     markerLength;
27081                 
27082                 if (dataView.getUint16(0) === 0xffd8) {
27083                     while (offset < maxOffset) {
27084                         markerBytes = dataView.getUint16(offset);
27085                         
27086                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27087                             markerLength = dataView.getUint16(offset + 2) + 2;
27088                             if (offset + markerLength > dataView.byteLength) {
27089                                 Roo.log('Invalid meta data: Invalid segment size.');
27090                                 break;
27091                             }
27092                             
27093                             if(markerBytes == 0xffe1){
27094                                 _this.parseExifData(
27095                                     dataView,
27096                                     offset,
27097                                     markerLength
27098                                 );
27099                             }
27100                             
27101                             offset += markerLength;
27102                             
27103                             continue;
27104                         }
27105                         
27106                         break;
27107                     }
27108                     
27109                 }
27110                 
27111                 var url = _this.urlAPI.createObjectURL(_this.file);
27112                 
27113                 _this.loadCanvas(url);
27114                 
27115                 return;
27116             }
27117             
27118             reader.readAsArrayBuffer(this.file);
27119             
27120         }
27121         
27122     },
27123     
27124     parseExifData : function(dataView, offset, length)
27125     {
27126         var tiffOffset = offset + 10,
27127             littleEndian,
27128             dirOffset;
27129     
27130         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27131             // No Exif data, might be XMP data instead
27132             return;
27133         }
27134         
27135         // Check for the ASCII code for "Exif" (0x45786966):
27136         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27137             // No Exif data, might be XMP data instead
27138             return;
27139         }
27140         if (tiffOffset + 8 > dataView.byteLength) {
27141             Roo.log('Invalid Exif data: Invalid segment size.');
27142             return;
27143         }
27144         // Check for the two null bytes:
27145         if (dataView.getUint16(offset + 8) !== 0x0000) {
27146             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27147             return;
27148         }
27149         // Check the byte alignment:
27150         switch (dataView.getUint16(tiffOffset)) {
27151         case 0x4949:
27152             littleEndian = true;
27153             break;
27154         case 0x4D4D:
27155             littleEndian = false;
27156             break;
27157         default:
27158             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27159             return;
27160         }
27161         // Check for the TIFF tag marker (0x002A):
27162         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27163             Roo.log('Invalid Exif data: Missing TIFF marker.');
27164             return;
27165         }
27166         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27167         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27168         
27169         this.parseExifTags(
27170             dataView,
27171             tiffOffset,
27172             tiffOffset + dirOffset,
27173             littleEndian
27174         );
27175     },
27176     
27177     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27178     {
27179         var tagsNumber,
27180             dirEndOffset,
27181             i;
27182         if (dirOffset + 6 > dataView.byteLength) {
27183             Roo.log('Invalid Exif data: Invalid directory offset.');
27184             return;
27185         }
27186         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27187         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27188         if (dirEndOffset + 4 > dataView.byteLength) {
27189             Roo.log('Invalid Exif data: Invalid directory size.');
27190             return;
27191         }
27192         for (i = 0; i < tagsNumber; i += 1) {
27193             this.parseExifTag(
27194                 dataView,
27195                 tiffOffset,
27196                 dirOffset + 2 + 12 * i, // tag offset
27197                 littleEndian
27198             );
27199         }
27200         // Return the offset to the next directory:
27201         return dataView.getUint32(dirEndOffset, littleEndian);
27202     },
27203     
27204     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27205     {
27206         var tag = dataView.getUint16(offset, littleEndian);
27207         
27208         this.exif[tag] = this.getExifValue(
27209             dataView,
27210             tiffOffset,
27211             offset,
27212             dataView.getUint16(offset + 2, littleEndian), // tag type
27213             dataView.getUint32(offset + 4, littleEndian), // tag length
27214             littleEndian
27215         );
27216     },
27217     
27218     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27219     {
27220         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27221             tagSize,
27222             dataOffset,
27223             values,
27224             i,
27225             str,
27226             c;
27227     
27228         if (!tagType) {
27229             Roo.log('Invalid Exif data: Invalid tag type.');
27230             return;
27231         }
27232         
27233         tagSize = tagType.size * length;
27234         // Determine if the value is contained in the dataOffset bytes,
27235         // or if the value at the dataOffset is a pointer to the actual data:
27236         dataOffset = tagSize > 4 ?
27237                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27238         if (dataOffset + tagSize > dataView.byteLength) {
27239             Roo.log('Invalid Exif data: Invalid data offset.');
27240             return;
27241         }
27242         if (length === 1) {
27243             return tagType.getValue(dataView, dataOffset, littleEndian);
27244         }
27245         values = [];
27246         for (i = 0; i < length; i += 1) {
27247             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27248         }
27249         
27250         if (tagType.ascii) {
27251             str = '';
27252             // Concatenate the chars:
27253             for (i = 0; i < values.length; i += 1) {
27254                 c = values[i];
27255                 // Ignore the terminating NULL byte(s):
27256                 if (c === '\u0000') {
27257                     break;
27258                 }
27259                 str += c;
27260             }
27261             return str;
27262         }
27263         return values;
27264     }
27265     
27266 });
27267
27268 Roo.apply(Roo.bootstrap.UploadCropbox, {
27269     tags : {
27270         'Orientation': 0x0112
27271     },
27272     
27273     Orientation: {
27274             1: 0, //'top-left',
27275 //            2: 'top-right',
27276             3: 180, //'bottom-right',
27277 //            4: 'bottom-left',
27278 //            5: 'left-top',
27279             6: 90, //'right-top',
27280 //            7: 'right-bottom',
27281             8: 270 //'left-bottom'
27282     },
27283     
27284     exifTagTypes : {
27285         // byte, 8-bit unsigned int:
27286         1: {
27287             getValue: function (dataView, dataOffset) {
27288                 return dataView.getUint8(dataOffset);
27289             },
27290             size: 1
27291         },
27292         // ascii, 8-bit byte:
27293         2: {
27294             getValue: function (dataView, dataOffset) {
27295                 return String.fromCharCode(dataView.getUint8(dataOffset));
27296             },
27297             size: 1,
27298             ascii: true
27299         },
27300         // short, 16 bit int:
27301         3: {
27302             getValue: function (dataView, dataOffset, littleEndian) {
27303                 return dataView.getUint16(dataOffset, littleEndian);
27304             },
27305             size: 2
27306         },
27307         // long, 32 bit int:
27308         4: {
27309             getValue: function (dataView, dataOffset, littleEndian) {
27310                 return dataView.getUint32(dataOffset, littleEndian);
27311             },
27312             size: 4
27313         },
27314         // rational = two long values, first is numerator, second is denominator:
27315         5: {
27316             getValue: function (dataView, dataOffset, littleEndian) {
27317                 return dataView.getUint32(dataOffset, littleEndian) /
27318                     dataView.getUint32(dataOffset + 4, littleEndian);
27319             },
27320             size: 8
27321         },
27322         // slong, 32 bit signed int:
27323         9: {
27324             getValue: function (dataView, dataOffset, littleEndian) {
27325                 return dataView.getInt32(dataOffset, littleEndian);
27326             },
27327             size: 4
27328         },
27329         // srational, two slongs, first is numerator, second is denominator:
27330         10: {
27331             getValue: function (dataView, dataOffset, littleEndian) {
27332                 return dataView.getInt32(dataOffset, littleEndian) /
27333                     dataView.getInt32(dataOffset + 4, littleEndian);
27334             },
27335             size: 8
27336         }
27337     },
27338     
27339     footer : {
27340         STANDARD : [
27341             {
27342                 tag : 'div',
27343                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27344                 action : 'rotate-left',
27345                 cn : [
27346                     {
27347                         tag : 'button',
27348                         cls : 'btn btn-default',
27349                         html : '<i class="fa fa-undo"></i>'
27350                     }
27351                 ]
27352             },
27353             {
27354                 tag : 'div',
27355                 cls : 'btn-group roo-upload-cropbox-picture',
27356                 action : 'picture',
27357                 cn : [
27358                     {
27359                         tag : 'button',
27360                         cls : 'btn btn-default',
27361                         html : '<i class="fa fa-picture-o"></i>'
27362                     }
27363                 ]
27364             },
27365             {
27366                 tag : 'div',
27367                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27368                 action : 'rotate-right',
27369                 cn : [
27370                     {
27371                         tag : 'button',
27372                         cls : 'btn btn-default',
27373                         html : '<i class="fa fa-repeat"></i>'
27374                     }
27375                 ]
27376             }
27377         ],
27378         DOCUMENT : [
27379             {
27380                 tag : 'div',
27381                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27382                 action : 'rotate-left',
27383                 cn : [
27384                     {
27385                         tag : 'button',
27386                         cls : 'btn btn-default',
27387                         html : '<i class="fa fa-undo"></i>'
27388                     }
27389                 ]
27390             },
27391             {
27392                 tag : 'div',
27393                 cls : 'btn-group roo-upload-cropbox-download',
27394                 action : 'download',
27395                 cn : [
27396                     {
27397                         tag : 'button',
27398                         cls : 'btn btn-default',
27399                         html : '<i class="fa fa-download"></i>'
27400                     }
27401                 ]
27402             },
27403             {
27404                 tag : 'div',
27405                 cls : 'btn-group roo-upload-cropbox-crop',
27406                 action : 'crop',
27407                 cn : [
27408                     {
27409                         tag : 'button',
27410                         cls : 'btn btn-default',
27411                         html : '<i class="fa fa-crop"></i>'
27412                     }
27413                 ]
27414             },
27415             {
27416                 tag : 'div',
27417                 cls : 'btn-group roo-upload-cropbox-trash',
27418                 action : 'trash',
27419                 cn : [
27420                     {
27421                         tag : 'button',
27422                         cls : 'btn btn-default',
27423                         html : '<i class="fa fa-trash"></i>'
27424                     }
27425                 ]
27426             },
27427             {
27428                 tag : 'div',
27429                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27430                 action : 'rotate-right',
27431                 cn : [
27432                     {
27433                         tag : 'button',
27434                         cls : 'btn btn-default',
27435                         html : '<i class="fa fa-repeat"></i>'
27436                     }
27437                 ]
27438             }
27439         ],
27440         ROTATOR : [
27441             {
27442                 tag : 'div',
27443                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27444                 action : 'rotate-left',
27445                 cn : [
27446                     {
27447                         tag : 'button',
27448                         cls : 'btn btn-default',
27449                         html : '<i class="fa fa-undo"></i>'
27450                     }
27451                 ]
27452             },
27453             {
27454                 tag : 'div',
27455                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27456                 action : 'rotate-right',
27457                 cn : [
27458                     {
27459                         tag : 'button',
27460                         cls : 'btn btn-default',
27461                         html : '<i class="fa fa-repeat"></i>'
27462                     }
27463                 ]
27464             }
27465         ]
27466     }
27467 });
27468
27469 /*
27470 * Licence: LGPL
27471 */
27472
27473 /**
27474  * @class Roo.bootstrap.DocumentManager
27475  * @extends Roo.bootstrap.Component
27476  * Bootstrap DocumentManager class
27477  * @cfg {String} paramName default 'imageUpload'
27478  * @cfg {String} toolTipName default 'filename'
27479  * @cfg {String} method default POST
27480  * @cfg {String} url action url
27481  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27482  * @cfg {Boolean} multiple multiple upload default true
27483  * @cfg {Number} thumbSize default 300
27484  * @cfg {String} fieldLabel
27485  * @cfg {Number} labelWidth default 4
27486  * @cfg {String} labelAlign (left|top) default left
27487  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27488  * 
27489  * @constructor
27490  * Create a new DocumentManager
27491  * @param {Object} config The config object
27492  */
27493
27494 Roo.bootstrap.DocumentManager = function(config){
27495     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27496     
27497     this.files = [];
27498     this.delegates = [];
27499     
27500     this.addEvents({
27501         /**
27502          * @event initial
27503          * Fire when initial the DocumentManager
27504          * @param {Roo.bootstrap.DocumentManager} this
27505          */
27506         "initial" : true,
27507         /**
27508          * @event inspect
27509          * inspect selected file
27510          * @param {Roo.bootstrap.DocumentManager} this
27511          * @param {File} file
27512          */
27513         "inspect" : true,
27514         /**
27515          * @event exception
27516          * Fire when xhr load exception
27517          * @param {Roo.bootstrap.DocumentManager} this
27518          * @param {XMLHttpRequest} xhr
27519          */
27520         "exception" : true,
27521         /**
27522          * @event afterupload
27523          * Fire when xhr load exception
27524          * @param {Roo.bootstrap.DocumentManager} this
27525          * @param {XMLHttpRequest} xhr
27526          */
27527         "afterupload" : true,
27528         /**
27529          * @event prepare
27530          * prepare the form data
27531          * @param {Roo.bootstrap.DocumentManager} this
27532          * @param {Object} formData
27533          */
27534         "prepare" : true,
27535         /**
27536          * @event remove
27537          * Fire when remove the file
27538          * @param {Roo.bootstrap.DocumentManager} this
27539          * @param {Object} file
27540          */
27541         "remove" : true,
27542         /**
27543          * @event refresh
27544          * Fire after refresh the file
27545          * @param {Roo.bootstrap.DocumentManager} this
27546          */
27547         "refresh" : true,
27548         /**
27549          * @event click
27550          * Fire after click the image
27551          * @param {Roo.bootstrap.DocumentManager} this
27552          * @param {Object} file
27553          */
27554         "click" : true,
27555         /**
27556          * @event edit
27557          * Fire when upload a image and editable set to true
27558          * @param {Roo.bootstrap.DocumentManager} this
27559          * @param {Object} file
27560          */
27561         "edit" : true,
27562         /**
27563          * @event beforeselectfile
27564          * Fire before select file
27565          * @param {Roo.bootstrap.DocumentManager} this
27566          */
27567         "beforeselectfile" : true,
27568         /**
27569          * @event process
27570          * Fire before process file
27571          * @param {Roo.bootstrap.DocumentManager} this
27572          * @param {Object} file
27573          */
27574         "process" : true
27575         
27576     });
27577 };
27578
27579 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27580     
27581     boxes : 0,
27582     inputName : '',
27583     thumbSize : 300,
27584     multiple : true,
27585     files : false,
27586     method : 'POST',
27587     url : '',
27588     paramName : 'imageUpload',
27589     toolTipName : 'filename',
27590     fieldLabel : '',
27591     labelWidth : 4,
27592     labelAlign : 'left',
27593     editable : true,
27594     delegates : false,
27595     xhr : false, 
27596     
27597     getAutoCreate : function()
27598     {   
27599         var managerWidget = {
27600             tag : 'div',
27601             cls : 'roo-document-manager',
27602             cn : [
27603                 {
27604                     tag : 'input',
27605                     cls : 'roo-document-manager-selector',
27606                     type : 'file'
27607                 },
27608                 {
27609                     tag : 'div',
27610                     cls : 'roo-document-manager-uploader',
27611                     cn : [
27612                         {
27613                             tag : 'div',
27614                             cls : 'roo-document-manager-upload-btn',
27615                             html : '<i class="fa fa-plus"></i>'
27616                         }
27617                     ]
27618                     
27619                 }
27620             ]
27621         };
27622         
27623         var content = [
27624             {
27625                 tag : 'div',
27626                 cls : 'column col-md-12',
27627                 cn : managerWidget
27628             }
27629         ];
27630         
27631         if(this.fieldLabel.length){
27632             
27633             content = [
27634                 {
27635                     tag : 'div',
27636                     cls : 'column col-md-12',
27637                     html : this.fieldLabel
27638                 },
27639                 {
27640                     tag : 'div',
27641                     cls : 'column col-md-12',
27642                     cn : managerWidget
27643                 }
27644             ];
27645
27646             if(this.labelAlign == 'left'){
27647                 content = [
27648                     {
27649                         tag : 'div',
27650                         cls : 'column col-md-' + this.labelWidth,
27651                         html : this.fieldLabel
27652                     },
27653                     {
27654                         tag : 'div',
27655                         cls : 'column col-md-' + (12 - this.labelWidth),
27656                         cn : managerWidget
27657                     }
27658                 ];
27659                 
27660             }
27661         }
27662         
27663         var cfg = {
27664             tag : 'div',
27665             cls : 'row clearfix',
27666             cn : content
27667         };
27668         
27669         return cfg;
27670         
27671     },
27672     
27673     initEvents : function()
27674     {
27675         this.managerEl = this.el.select('.roo-document-manager', true).first();
27676         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27677         
27678         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27679         this.selectorEl.hide();
27680         
27681         if(this.multiple){
27682             this.selectorEl.attr('multiple', 'multiple');
27683         }
27684         
27685         this.selectorEl.on('change', this.onFileSelected, this);
27686         
27687         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27688         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27689         
27690         this.uploader.on('click', this.onUploaderClick, this);
27691         
27692         this.renderProgressDialog();
27693         
27694         var _this = this;
27695         
27696         window.addEventListener("resize", function() { _this.refresh(); } );
27697         
27698         this.fireEvent('initial', this);
27699     },
27700     
27701     renderProgressDialog : function()
27702     {
27703         var _this = this;
27704         
27705         this.progressDialog = new Roo.bootstrap.Modal({
27706             cls : 'roo-document-manager-progress-dialog',
27707             allow_close : false,
27708             title : '',
27709             buttons : [
27710                 {
27711                     name  :'cancel',
27712                     weight : 'danger',
27713                     html : 'Cancel'
27714                 }
27715             ], 
27716             listeners : { 
27717                 btnclick : function() {
27718                     _this.uploadCancel();
27719                     this.hide();
27720                 }
27721             }
27722         });
27723          
27724         this.progressDialog.render(Roo.get(document.body));
27725          
27726         this.progress = new Roo.bootstrap.Progress({
27727             cls : 'roo-document-manager-progress',
27728             active : true,
27729             striped : true
27730         });
27731         
27732         this.progress.render(this.progressDialog.getChildContainer());
27733         
27734         this.progressBar = new Roo.bootstrap.ProgressBar({
27735             cls : 'roo-document-manager-progress-bar',
27736             aria_valuenow : 0,
27737             aria_valuemin : 0,
27738             aria_valuemax : 12,
27739             panel : 'success'
27740         });
27741         
27742         this.progressBar.render(this.progress.getChildContainer());
27743     },
27744     
27745     onUploaderClick : function(e)
27746     {
27747         e.preventDefault();
27748      
27749         if(this.fireEvent('beforeselectfile', this) != false){
27750             this.selectorEl.dom.click();
27751         }
27752         
27753     },
27754     
27755     onFileSelected : function(e)
27756     {
27757         e.preventDefault();
27758         
27759         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27760             return;
27761         }
27762         
27763         Roo.each(this.selectorEl.dom.files, function(file){
27764             if(this.fireEvent('inspect', this, file) != false){
27765                 this.files.push(file);
27766             }
27767         }, this);
27768         
27769         this.queue();
27770         
27771     },
27772     
27773     queue : function()
27774     {
27775         this.selectorEl.dom.value = '';
27776         
27777         if(!this.files.length){
27778             return;
27779         }
27780         
27781         if(this.boxes > 0 && this.files.length > this.boxes){
27782             this.files = this.files.slice(0, this.boxes);
27783         }
27784         
27785         this.uploader.show();
27786         
27787         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27788             this.uploader.hide();
27789         }
27790         
27791         var _this = this;
27792         
27793         var files = [];
27794         
27795         var docs = [];
27796         
27797         Roo.each(this.files, function(file){
27798             
27799             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27800                 var f = this.renderPreview(file);
27801                 files.push(f);
27802                 return;
27803             }
27804             
27805             if(file.type.indexOf('image') != -1){
27806                 this.delegates.push(
27807                     (function(){
27808                         _this.process(file);
27809                     }).createDelegate(this)
27810                 );
27811         
27812                 return;
27813             }
27814             
27815             docs.push(
27816                 (function(){
27817                     _this.process(file);
27818                 }).createDelegate(this)
27819             );
27820             
27821         }, this);
27822         
27823         this.files = files;
27824         
27825         this.delegates = this.delegates.concat(docs);
27826         
27827         if(!this.delegates.length){
27828             this.refresh();
27829             return;
27830         }
27831         
27832         this.progressBar.aria_valuemax = this.delegates.length;
27833         
27834         this.arrange();
27835         
27836         return;
27837     },
27838     
27839     arrange : function()
27840     {
27841         if(!this.delegates.length){
27842             this.progressDialog.hide();
27843             this.refresh();
27844             return;
27845         }
27846         
27847         var delegate = this.delegates.shift();
27848         
27849         this.progressDialog.show();
27850         
27851         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27852         
27853         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27854         
27855         delegate();
27856     },
27857     
27858     refresh : function()
27859     {
27860         this.uploader.show();
27861         
27862         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27863             this.uploader.hide();
27864         }
27865         
27866         Roo.isTouch ? this.closable(false) : this.closable(true);
27867         
27868         this.fireEvent('refresh', this);
27869     },
27870     
27871     onRemove : function(e, el, o)
27872     {
27873         e.preventDefault();
27874         
27875         this.fireEvent('remove', this, o);
27876         
27877     },
27878     
27879     remove : function(o)
27880     {
27881         var files = [];
27882         
27883         Roo.each(this.files, function(file){
27884             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27885                 files.push(file);
27886                 return;
27887             }
27888
27889             o.target.remove();
27890
27891         }, this);
27892         
27893         this.files = files;
27894         
27895         this.refresh();
27896     },
27897     
27898     clear : function()
27899     {
27900         Roo.each(this.files, function(file){
27901             if(!file.target){
27902                 return;
27903             }
27904             
27905             file.target.remove();
27906
27907         }, this);
27908         
27909         this.files = [];
27910         
27911         this.refresh();
27912     },
27913     
27914     onClick : function(e, el, o)
27915     {
27916         e.preventDefault();
27917         
27918         this.fireEvent('click', this, o);
27919         
27920     },
27921     
27922     closable : function(closable)
27923     {
27924         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27925             
27926             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27927             
27928             if(closable){
27929                 el.show();
27930                 return;
27931             }
27932             
27933             el.hide();
27934             
27935         }, this);
27936     },
27937     
27938     xhrOnLoad : function(xhr)
27939     {
27940         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27941             el.remove();
27942         }, this);
27943         
27944         if (xhr.readyState !== 4) {
27945             this.arrange();
27946             this.fireEvent('exception', this, xhr);
27947             return;
27948         }
27949
27950         var response = Roo.decode(xhr.responseText);
27951         
27952         if(!response.success){
27953             this.arrange();
27954             this.fireEvent('exception', this, xhr);
27955             return;
27956         }
27957         
27958         var file = this.renderPreview(response.data);
27959         
27960         this.files.push(file);
27961         
27962         this.arrange();
27963         
27964         this.fireEvent('afterupload', this, xhr);
27965         
27966     },
27967     
27968     xhrOnError : function(xhr)
27969     {
27970         Roo.log('xhr on error');
27971         
27972         var response = Roo.decode(xhr.responseText);
27973           
27974         Roo.log(response);
27975         
27976         this.arrange();
27977     },
27978     
27979     process : function(file)
27980     {
27981         if(this.fireEvent('process', this, file) !== false){
27982             if(this.editable && file.type.indexOf('image') != -1){
27983                 this.fireEvent('edit', this, file);
27984                 return;
27985             }
27986
27987             this.uploadStart(file, false);
27988
27989             return;
27990         }
27991         
27992     },
27993     
27994     uploadStart : function(file, crop)
27995     {
27996         this.xhr = new XMLHttpRequest();
27997         
27998         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27999             this.arrange();
28000             return;
28001         }
28002         
28003         file.xhr = this.xhr;
28004             
28005         this.managerEl.createChild({
28006             tag : 'div',
28007             cls : 'roo-document-manager-loading',
28008             cn : [
28009                 {
28010                     tag : 'div',
28011                     tooltip : file.name,
28012                     cls : 'roo-document-manager-thumb',
28013                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28014                 }
28015             ]
28016
28017         });
28018
28019         this.xhr.open(this.method, this.url, true);
28020         
28021         var headers = {
28022             "Accept": "application/json",
28023             "Cache-Control": "no-cache",
28024             "X-Requested-With": "XMLHttpRequest"
28025         };
28026         
28027         for (var headerName in headers) {
28028             var headerValue = headers[headerName];
28029             if (headerValue) {
28030                 this.xhr.setRequestHeader(headerName, headerValue);
28031             }
28032         }
28033         
28034         var _this = this;
28035         
28036         this.xhr.onload = function()
28037         {
28038             _this.xhrOnLoad(_this.xhr);
28039         }
28040         
28041         this.xhr.onerror = function()
28042         {
28043             _this.xhrOnError(_this.xhr);
28044         }
28045         
28046         var formData = new FormData();
28047
28048         formData.append('returnHTML', 'NO');
28049         
28050         if(crop){
28051             formData.append('crop', crop);
28052         }
28053         
28054         formData.append(this.paramName, file, file.name);
28055         
28056         var options = {
28057             file : file, 
28058             manually : false
28059         };
28060         
28061         if(this.fireEvent('prepare', this, formData, options) != false){
28062             
28063             if(options.manually){
28064                 return;
28065             }
28066             
28067             this.xhr.send(formData);
28068             return;
28069         };
28070         
28071         this.uploadCancel();
28072     },
28073     
28074     uploadCancel : function()
28075     {
28076         if (this.xhr) {
28077             this.xhr.abort();
28078         }
28079         
28080         this.delegates = [];
28081         
28082         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28083             el.remove();
28084         }, this);
28085         
28086         this.arrange();
28087     },
28088     
28089     renderPreview : function(file)
28090     {
28091         if(typeof(file.target) != 'undefined' && file.target){
28092             return file;
28093         }
28094         
28095         var previewEl = this.managerEl.createChild({
28096             tag : 'div',
28097             cls : 'roo-document-manager-preview',
28098             cn : [
28099                 {
28100                     tag : 'div',
28101                     tooltip : file[this.toolTipName],
28102                     cls : 'roo-document-manager-thumb',
28103                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28104                 },
28105                 {
28106                     tag : 'button',
28107                     cls : 'close',
28108                     html : '<i class="fa fa-times-circle"></i>'
28109                 }
28110             ]
28111         });
28112
28113         var close = previewEl.select('button.close', true).first();
28114
28115         close.on('click', this.onRemove, this, file);
28116
28117         file.target = previewEl;
28118
28119         var image = previewEl.select('img', true).first();
28120         
28121         var _this = this;
28122         
28123         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28124         
28125         image.on('click', this.onClick, this, file);
28126         
28127         return file;
28128         
28129     },
28130     
28131     onPreviewLoad : function(file, image)
28132     {
28133         if(typeof(file.target) == 'undefined' || !file.target){
28134             return;
28135         }
28136         
28137         var width = image.dom.naturalWidth || image.dom.width;
28138         var height = image.dom.naturalHeight || image.dom.height;
28139         
28140         if(width > height){
28141             file.target.addClass('wide');
28142             return;
28143         }
28144         
28145         file.target.addClass('tall');
28146         return;
28147         
28148     },
28149     
28150     uploadFromSource : function(file, crop)
28151     {
28152         this.xhr = new XMLHttpRequest();
28153         
28154         this.managerEl.createChild({
28155             tag : 'div',
28156             cls : 'roo-document-manager-loading',
28157             cn : [
28158                 {
28159                     tag : 'div',
28160                     tooltip : file.name,
28161                     cls : 'roo-document-manager-thumb',
28162                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28163                 }
28164             ]
28165
28166         });
28167
28168         this.xhr.open(this.method, this.url, true);
28169         
28170         var headers = {
28171             "Accept": "application/json",
28172             "Cache-Control": "no-cache",
28173             "X-Requested-With": "XMLHttpRequest"
28174         };
28175         
28176         for (var headerName in headers) {
28177             var headerValue = headers[headerName];
28178             if (headerValue) {
28179                 this.xhr.setRequestHeader(headerName, headerValue);
28180             }
28181         }
28182         
28183         var _this = this;
28184         
28185         this.xhr.onload = function()
28186         {
28187             _this.xhrOnLoad(_this.xhr);
28188         }
28189         
28190         this.xhr.onerror = function()
28191         {
28192             _this.xhrOnError(_this.xhr);
28193         }
28194         
28195         var formData = new FormData();
28196
28197         formData.append('returnHTML', 'NO');
28198         
28199         formData.append('crop', crop);
28200         
28201         if(typeof(file.filename) != 'undefined'){
28202             formData.append('filename', file.filename);
28203         }
28204         
28205         if(typeof(file.mimetype) != 'undefined'){
28206             formData.append('mimetype', file.mimetype);
28207         }
28208         
28209         if(this.fireEvent('prepare', this, formData) != false){
28210             this.xhr.send(formData);
28211         };
28212     }
28213 });
28214
28215 /*
28216 * Licence: LGPL
28217 */
28218
28219 /**
28220  * @class Roo.bootstrap.DocumentViewer
28221  * @extends Roo.bootstrap.Component
28222  * Bootstrap DocumentViewer class
28223  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28224  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28225  * 
28226  * @constructor
28227  * Create a new DocumentViewer
28228  * @param {Object} config The config object
28229  */
28230
28231 Roo.bootstrap.DocumentViewer = function(config){
28232     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28233     
28234     this.addEvents({
28235         /**
28236          * @event initial
28237          * Fire after initEvent
28238          * @param {Roo.bootstrap.DocumentViewer} this
28239          */
28240         "initial" : true,
28241         /**
28242          * @event click
28243          * Fire after click
28244          * @param {Roo.bootstrap.DocumentViewer} this
28245          */
28246         "click" : true,
28247         /**
28248          * @event download
28249          * Fire after download button
28250          * @param {Roo.bootstrap.DocumentViewer} this
28251          */
28252         "download" : true,
28253         /**
28254          * @event trash
28255          * Fire after trash button
28256          * @param {Roo.bootstrap.DocumentViewer} this
28257          */
28258         "trash" : true
28259         
28260     });
28261 };
28262
28263 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28264     
28265     showDownload : true,
28266     
28267     showTrash : true,
28268     
28269     getAutoCreate : function()
28270     {
28271         var cfg = {
28272             tag : 'div',
28273             cls : 'roo-document-viewer',
28274             cn : [
28275                 {
28276                     tag : 'div',
28277                     cls : 'roo-document-viewer-body',
28278                     cn : [
28279                         {
28280                             tag : 'div',
28281                             cls : 'roo-document-viewer-thumb',
28282                             cn : [
28283                                 {
28284                                     tag : 'img',
28285                                     cls : 'roo-document-viewer-image'
28286                                 }
28287                             ]
28288                         }
28289                     ]
28290                 },
28291                 {
28292                     tag : 'div',
28293                     cls : 'roo-document-viewer-footer',
28294                     cn : {
28295                         tag : 'div',
28296                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28297                         cn : [
28298                             {
28299                                 tag : 'div',
28300                                 cls : 'btn-group roo-document-viewer-download',
28301                                 cn : [
28302                                     {
28303                                         tag : 'button',
28304                                         cls : 'btn btn-default',
28305                                         html : '<i class="fa fa-download"></i>'
28306                                     }
28307                                 ]
28308                             },
28309                             {
28310                                 tag : 'div',
28311                                 cls : 'btn-group roo-document-viewer-trash',
28312                                 cn : [
28313                                     {
28314                                         tag : 'button',
28315                                         cls : 'btn btn-default',
28316                                         html : '<i class="fa fa-trash"></i>'
28317                                     }
28318                                 ]
28319                             }
28320                         ]
28321                     }
28322                 }
28323             ]
28324         };
28325         
28326         return cfg;
28327     },
28328     
28329     initEvents : function()
28330     {
28331         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28332         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28333         
28334         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28335         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28336         
28337         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28338         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28339         
28340         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28341         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28342         
28343         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28344         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28345         
28346         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28347         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28348         
28349         this.bodyEl.on('click', this.onClick, this);
28350         this.downloadBtn.on('click', this.onDownload, this);
28351         this.trashBtn.on('click', this.onTrash, this);
28352         
28353         this.downloadBtn.hide();
28354         this.trashBtn.hide();
28355         
28356         if(this.showDownload){
28357             this.downloadBtn.show();
28358         }
28359         
28360         if(this.showTrash){
28361             this.trashBtn.show();
28362         }
28363         
28364         if(!this.showDownload && !this.showTrash) {
28365             this.footerEl.hide();
28366         }
28367         
28368     },
28369     
28370     initial : function()
28371     {
28372         this.fireEvent('initial', this);
28373         
28374     },
28375     
28376     onClick : function(e)
28377     {
28378         e.preventDefault();
28379         
28380         this.fireEvent('click', this);
28381     },
28382     
28383     onDownload : function(e)
28384     {
28385         e.preventDefault();
28386         
28387         this.fireEvent('download', this);
28388     },
28389     
28390     onTrash : function(e)
28391     {
28392         e.preventDefault();
28393         
28394         this.fireEvent('trash', this);
28395     }
28396     
28397 });
28398 /*
28399  * - LGPL
28400  *
28401  * nav progress bar
28402  * 
28403  */
28404
28405 /**
28406  * @class Roo.bootstrap.NavProgressBar
28407  * @extends Roo.bootstrap.Component
28408  * Bootstrap NavProgressBar class
28409  * 
28410  * @constructor
28411  * Create a new nav progress bar
28412  * @param {Object} config The config object
28413  */
28414
28415 Roo.bootstrap.NavProgressBar = function(config){
28416     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28417
28418     this.bullets = this.bullets || [];
28419    
28420 //    Roo.bootstrap.NavProgressBar.register(this);
28421      this.addEvents({
28422         /**
28423              * @event changed
28424              * Fires when the active item changes
28425              * @param {Roo.bootstrap.NavProgressBar} this
28426              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28427              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28428          */
28429         'changed': true
28430      });
28431     
28432 };
28433
28434 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28435     
28436     bullets : [],
28437     barItems : [],
28438     
28439     getAutoCreate : function()
28440     {
28441         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28442         
28443         cfg = {
28444             tag : 'div',
28445             cls : 'roo-navigation-bar-group',
28446             cn : [
28447                 {
28448                     tag : 'div',
28449                     cls : 'roo-navigation-top-bar'
28450                 },
28451                 {
28452                     tag : 'div',
28453                     cls : 'roo-navigation-bullets-bar',
28454                     cn : [
28455                         {
28456                             tag : 'ul',
28457                             cls : 'roo-navigation-bar'
28458                         }
28459                     ]
28460                 },
28461                 
28462                 {
28463                     tag : 'div',
28464                     cls : 'roo-navigation-bottom-bar'
28465                 }
28466             ]
28467             
28468         };
28469         
28470         return cfg;
28471         
28472     },
28473     
28474     initEvents: function() 
28475     {
28476         
28477     },
28478     
28479     onRender : function(ct, position) 
28480     {
28481         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28482         
28483         if(this.bullets.length){
28484             Roo.each(this.bullets, function(b){
28485                this.addItem(b);
28486             }, this);
28487         }
28488         
28489         this.format();
28490         
28491     },
28492     
28493     addItem : function(cfg)
28494     {
28495         var item = new Roo.bootstrap.NavProgressItem(cfg);
28496         
28497         item.parentId = this.id;
28498         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28499         
28500         if(cfg.html){
28501             var top = new Roo.bootstrap.Element({
28502                 tag : 'div',
28503                 cls : 'roo-navigation-bar-text'
28504             });
28505             
28506             var bottom = new Roo.bootstrap.Element({
28507                 tag : 'div',
28508                 cls : 'roo-navigation-bar-text'
28509             });
28510             
28511             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28512             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28513             
28514             var topText = new Roo.bootstrap.Element({
28515                 tag : 'span',
28516                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28517             });
28518             
28519             var bottomText = new Roo.bootstrap.Element({
28520                 tag : 'span',
28521                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28522             });
28523             
28524             topText.onRender(top.el, null);
28525             bottomText.onRender(bottom.el, null);
28526             
28527             item.topEl = top;
28528             item.bottomEl = bottom;
28529         }
28530         
28531         this.barItems.push(item);
28532         
28533         return item;
28534     },
28535     
28536     getActive : function()
28537     {
28538         var active = false;
28539         
28540         Roo.each(this.barItems, function(v){
28541             
28542             if (!v.isActive()) {
28543                 return;
28544             }
28545             
28546             active = v;
28547             return false;
28548             
28549         });
28550         
28551         return active;
28552     },
28553     
28554     setActiveItem : function(item)
28555     {
28556         var prev = false;
28557         
28558         Roo.each(this.barItems, function(v){
28559             if (v.rid == item.rid) {
28560                 return ;
28561             }
28562             
28563             if (v.isActive()) {
28564                 v.setActive(false);
28565                 prev = v;
28566             }
28567         });
28568
28569         item.setActive(true);
28570         
28571         this.fireEvent('changed', this, item, prev);
28572     },
28573     
28574     getBarItem: function(rid)
28575     {
28576         var ret = false;
28577         
28578         Roo.each(this.barItems, function(e) {
28579             if (e.rid != rid) {
28580                 return;
28581             }
28582             
28583             ret =  e;
28584             return false;
28585         });
28586         
28587         return ret;
28588     },
28589     
28590     indexOfItem : function(item)
28591     {
28592         var index = false;
28593         
28594         Roo.each(this.barItems, function(v, i){
28595             
28596             if (v.rid != item.rid) {
28597                 return;
28598             }
28599             
28600             index = i;
28601             return false
28602         });
28603         
28604         return index;
28605     },
28606     
28607     setActiveNext : function()
28608     {
28609         var i = this.indexOfItem(this.getActive());
28610         
28611         if (i > this.barItems.length) {
28612             return;
28613         }
28614         
28615         this.setActiveItem(this.barItems[i+1]);
28616     },
28617     
28618     setActivePrev : function()
28619     {
28620         var i = this.indexOfItem(this.getActive());
28621         
28622         if (i  < 1) {
28623             return;
28624         }
28625         
28626         this.setActiveItem(this.barItems[i-1]);
28627     },
28628     
28629     format : function()
28630     {
28631         if(!this.barItems.length){
28632             return;
28633         }
28634      
28635         var width = 100 / this.barItems.length;
28636         
28637         Roo.each(this.barItems, function(i){
28638             i.el.setStyle('width', width + '%');
28639             i.topEl.el.setStyle('width', width + '%');
28640             i.bottomEl.el.setStyle('width', width + '%');
28641         }, this);
28642         
28643     }
28644     
28645 });
28646 /*
28647  * - LGPL
28648  *
28649  * Nav Progress Item
28650  * 
28651  */
28652
28653 /**
28654  * @class Roo.bootstrap.NavProgressItem
28655  * @extends Roo.bootstrap.Component
28656  * Bootstrap NavProgressItem class
28657  * @cfg {String} rid the reference id
28658  * @cfg {Boolean} active (true|false) Is item active default false
28659  * @cfg {Boolean} disabled (true|false) Is item active default false
28660  * @cfg {String} html
28661  * @cfg {String} position (top|bottom) text position default bottom
28662  * @cfg {String} icon show icon instead of number
28663  * 
28664  * @constructor
28665  * Create a new NavProgressItem
28666  * @param {Object} config The config object
28667  */
28668 Roo.bootstrap.NavProgressItem = function(config){
28669     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28670     this.addEvents({
28671         // raw events
28672         /**
28673          * @event click
28674          * The raw click event for the entire grid.
28675          * @param {Roo.bootstrap.NavProgressItem} this
28676          * @param {Roo.EventObject} e
28677          */
28678         "click" : true
28679     });
28680    
28681 };
28682
28683 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28684     
28685     rid : '',
28686     active : false,
28687     disabled : false,
28688     html : '',
28689     position : 'bottom',
28690     icon : false,
28691     
28692     getAutoCreate : function()
28693     {
28694         var iconCls = 'roo-navigation-bar-item-icon';
28695         
28696         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28697         
28698         var cfg = {
28699             tag: 'li',
28700             cls: 'roo-navigation-bar-item',
28701             cn : [
28702                 {
28703                     tag : 'i',
28704                     cls : iconCls
28705                 }
28706             ]
28707         };
28708         
28709         if(this.active){
28710             cfg.cls += ' active';
28711         }
28712         if(this.disabled){
28713             cfg.cls += ' disabled';
28714         }
28715         
28716         return cfg;
28717     },
28718     
28719     disable : function()
28720     {
28721         this.setDisabled(true);
28722     },
28723     
28724     enable : function()
28725     {
28726         this.setDisabled(false);
28727     },
28728     
28729     initEvents: function() 
28730     {
28731         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28732         
28733         this.iconEl.on('click', this.onClick, this);
28734     },
28735     
28736     onClick : function(e)
28737     {
28738         e.preventDefault();
28739         
28740         if(this.disabled){
28741             return;
28742         }
28743         
28744         if(this.fireEvent('click', this, e) === false){
28745             return;
28746         };
28747         
28748         this.parent().setActiveItem(this);
28749     },
28750     
28751     isActive: function () 
28752     {
28753         return this.active;
28754     },
28755     
28756     setActive : function(state)
28757     {
28758         if(this.active == state){
28759             return;
28760         }
28761         
28762         this.active = state;
28763         
28764         if (state) {
28765             this.el.addClass('active');
28766             return;
28767         }
28768         
28769         this.el.removeClass('active');
28770         
28771         return;
28772     },
28773     
28774     setDisabled : function(state)
28775     {
28776         if(this.disabled == state){
28777             return;
28778         }
28779         
28780         this.disabled = state;
28781         
28782         if (state) {
28783             this.el.addClass('disabled');
28784             return;
28785         }
28786         
28787         this.el.removeClass('disabled');
28788     },
28789     
28790     tooltipEl : function()
28791     {
28792         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28793     }
28794 });
28795  
28796
28797  /*
28798  * - LGPL
28799  *
28800  * FieldLabel
28801  * 
28802  */
28803
28804 /**
28805  * @class Roo.bootstrap.FieldLabel
28806  * @extends Roo.bootstrap.Component
28807  * Bootstrap FieldLabel class
28808  * @cfg {String} html contents of the element
28809  * @cfg {String} tag tag of the element default label
28810  * @cfg {String} cls class of the element
28811  * @cfg {String} target label target 
28812  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28813  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28814  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28815  * @cfg {String} iconTooltip default "This field is required"
28816  * 
28817  * @constructor
28818  * Create a new FieldLabel
28819  * @param {Object} config The config object
28820  */
28821
28822 Roo.bootstrap.FieldLabel = function(config){
28823     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28824     
28825     this.addEvents({
28826             /**
28827              * @event invalid
28828              * Fires after the field has been marked as invalid.
28829              * @param {Roo.form.FieldLabel} this
28830              * @param {String} msg The validation message
28831              */
28832             invalid : true,
28833             /**
28834              * @event valid
28835              * Fires after the field has been validated with no errors.
28836              * @param {Roo.form.FieldLabel} this
28837              */
28838             valid : true
28839         });
28840 };
28841
28842 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28843     
28844     tag: 'label',
28845     cls: '',
28846     html: '',
28847     target: '',
28848     allowBlank : true,
28849     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28850     validClass : 'text-success fa fa-lg fa-check',
28851     iconTooltip : 'This field is required',
28852     
28853     getAutoCreate : function(){
28854         
28855         var cfg = {
28856             tag : this.tag,
28857             cls : 'roo-bootstrap-field-label ' + this.cls,
28858             for : this.target,
28859             cn : [
28860                 {
28861                     tag : 'i',
28862                     cls : '',
28863                     tooltip : this.iconTooltip
28864                 },
28865                 {
28866                     tag : 'span',
28867                     html : this.html
28868                 }
28869             ] 
28870         };
28871         
28872         return cfg;
28873     },
28874     
28875     initEvents: function() 
28876     {
28877         Roo.bootstrap.Element.superclass.initEvents.call(this);
28878         
28879         this.iconEl = this.el.select('i', true).first();
28880         
28881         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28882         
28883         Roo.bootstrap.FieldLabel.register(this);
28884     },
28885     
28886     /**
28887      * Mark this field as valid
28888      */
28889     markValid : function()
28890     {
28891         this.iconEl.show();
28892         
28893         this.iconEl.removeClass(this.invalidClass);
28894         
28895         this.iconEl.addClass(this.validClass);
28896         
28897         this.fireEvent('valid', this);
28898     },
28899     
28900     /**
28901      * Mark this field as invalid
28902      * @param {String} msg The validation message
28903      */
28904     markInvalid : function(msg)
28905     {
28906         this.iconEl.show();
28907         
28908         this.iconEl.removeClass(this.validClass);
28909         
28910         this.iconEl.addClass(this.invalidClass);
28911         
28912         this.fireEvent('invalid', this, msg);
28913     }
28914     
28915    
28916 });
28917
28918 Roo.apply(Roo.bootstrap.FieldLabel, {
28919     
28920     groups: {},
28921     
28922      /**
28923     * register a FieldLabel Group
28924     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28925     */
28926     register : function(label)
28927     {
28928         if(this.groups.hasOwnProperty(label.target)){
28929             return;
28930         }
28931      
28932         this.groups[label.target] = label;
28933         
28934     },
28935     /**
28936     * fetch a FieldLabel Group based on the target
28937     * @param {string} target
28938     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28939     */
28940     get: function(target) {
28941         if (typeof(this.groups[target]) == 'undefined') {
28942             return false;
28943         }
28944         
28945         return this.groups[target] ;
28946     }
28947 });
28948
28949  
28950
28951  /*
28952  * - LGPL
28953  *
28954  * page DateSplitField.
28955  * 
28956  */
28957
28958
28959 /**
28960  * @class Roo.bootstrap.DateSplitField
28961  * @extends Roo.bootstrap.Component
28962  * Bootstrap DateSplitField class
28963  * @cfg {string} fieldLabel - the label associated
28964  * @cfg {Number} labelWidth set the width of label (0-12)
28965  * @cfg {String} labelAlign (top|left)
28966  * @cfg {Boolean} dayAllowBlank (true|false) default false
28967  * @cfg {Boolean} monthAllowBlank (true|false) default false
28968  * @cfg {Boolean} yearAllowBlank (true|false) default false
28969  * @cfg {string} dayPlaceholder 
28970  * @cfg {string} monthPlaceholder
28971  * @cfg {string} yearPlaceholder
28972  * @cfg {string} dayFormat default 'd'
28973  * @cfg {string} monthFormat default 'm'
28974  * @cfg {string} yearFormat default 'Y'
28975
28976  *     
28977  * @constructor
28978  * Create a new DateSplitField
28979  * @param {Object} config The config object
28980  */
28981
28982 Roo.bootstrap.DateSplitField = function(config){
28983     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28984     
28985     this.addEvents({
28986         // raw events
28987          /**
28988          * @event years
28989          * getting the data of years
28990          * @param {Roo.bootstrap.DateSplitField} this
28991          * @param {Object} years
28992          */
28993         "years" : true,
28994         /**
28995          * @event days
28996          * getting the data of days
28997          * @param {Roo.bootstrap.DateSplitField} this
28998          * @param {Object} days
28999          */
29000         "days" : true,
29001         /**
29002          * @event invalid
29003          * Fires after the field has been marked as invalid.
29004          * @param {Roo.form.Field} this
29005          * @param {String} msg The validation message
29006          */
29007         invalid : true,
29008        /**
29009          * @event valid
29010          * Fires after the field has been validated with no errors.
29011          * @param {Roo.form.Field} this
29012          */
29013         valid : true
29014     });
29015 };
29016
29017 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29018     
29019     fieldLabel : '',
29020     labelAlign : 'top',
29021     labelWidth : 3,
29022     dayAllowBlank : false,
29023     monthAllowBlank : false,
29024     yearAllowBlank : false,
29025     dayPlaceholder : '',
29026     monthPlaceholder : '',
29027     yearPlaceholder : '',
29028     dayFormat : 'd',
29029     monthFormat : 'm',
29030     yearFormat : 'Y',
29031     isFormField : true,
29032     
29033     getAutoCreate : function()
29034     {
29035         var cfg = {
29036             tag : 'div',
29037             cls : 'row roo-date-split-field-group',
29038             cn : [
29039                 {
29040                     tag : 'input',
29041                     type : 'hidden',
29042                     cls : 'form-hidden-field roo-date-split-field-group-value',
29043                     name : this.name
29044                 }
29045             ]
29046         };
29047         
29048         if(this.fieldLabel){
29049             cfg.cn.push({
29050                 tag : 'div',
29051                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29052                 cn : [
29053                     {
29054                         tag : 'label',
29055                         html : this.fieldLabel
29056                     }
29057                 ]
29058             });
29059         }
29060         
29061         Roo.each(['day', 'month', 'year'], function(t){
29062             cfg.cn.push({
29063                 tag : 'div',
29064                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29065             });
29066         }, this);
29067         
29068         return cfg;
29069     },
29070     
29071     inputEl: function ()
29072     {
29073         return this.el.select('.roo-date-split-field-group-value', true).first();
29074     },
29075     
29076     onRender : function(ct, position) 
29077     {
29078         var _this = this;
29079         
29080         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29081         
29082         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29083         
29084         this.dayField = new Roo.bootstrap.ComboBox({
29085             allowBlank : this.dayAllowBlank,
29086             alwaysQuery : true,
29087             displayField : 'value',
29088             editable : false,
29089             fieldLabel : '',
29090             forceSelection : true,
29091             mode : 'local',
29092             placeholder : this.dayPlaceholder,
29093             selectOnFocus : true,
29094             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29095             triggerAction : 'all',
29096             typeAhead : true,
29097             valueField : 'value',
29098             store : new Roo.data.SimpleStore({
29099                 data : (function() {    
29100                     var days = [];
29101                     _this.fireEvent('days', _this, days);
29102                     return days;
29103                 })(),
29104                 fields : [ 'value' ]
29105             }),
29106             listeners : {
29107                 select : function (_self, record, index)
29108                 {
29109                     _this.setValue(_this.getValue());
29110                 }
29111             }
29112         });
29113
29114         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29115         
29116         this.monthField = new Roo.bootstrap.MonthField({
29117             after : '<i class=\"fa fa-calendar\"></i>',
29118             allowBlank : this.monthAllowBlank,
29119             placeholder : this.monthPlaceholder,
29120             readOnly : true,
29121             listeners : {
29122                 render : function (_self)
29123                 {
29124                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29125                         e.preventDefault();
29126                         _self.focus();
29127                     });
29128                 },
29129                 select : function (_self, oldvalue, newvalue)
29130                 {
29131                     _this.setValue(_this.getValue());
29132                 }
29133             }
29134         });
29135         
29136         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29137         
29138         this.yearField = new Roo.bootstrap.ComboBox({
29139             allowBlank : this.yearAllowBlank,
29140             alwaysQuery : true,
29141             displayField : 'value',
29142             editable : false,
29143             fieldLabel : '',
29144             forceSelection : true,
29145             mode : 'local',
29146             placeholder : this.yearPlaceholder,
29147             selectOnFocus : true,
29148             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29149             triggerAction : 'all',
29150             typeAhead : true,
29151             valueField : 'value',
29152             store : new Roo.data.SimpleStore({
29153                 data : (function() {
29154                     var years = [];
29155                     _this.fireEvent('years', _this, years);
29156                     return years;
29157                 })(),
29158                 fields : [ 'value' ]
29159             }),
29160             listeners : {
29161                 select : function (_self, record, index)
29162                 {
29163                     _this.setValue(_this.getValue());
29164                 }
29165             }
29166         });
29167
29168         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29169     },
29170     
29171     setValue : function(v, format)
29172     {
29173         this.inputEl.dom.value = v;
29174         
29175         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29176         
29177         var d = Date.parseDate(v, f);
29178         
29179         if(!d){
29180             this.validate();
29181             return;
29182         }
29183         
29184         this.setDay(d.format(this.dayFormat));
29185         this.setMonth(d.format(this.monthFormat));
29186         this.setYear(d.format(this.yearFormat));
29187         
29188         this.validate();
29189         
29190         return;
29191     },
29192     
29193     setDay : function(v)
29194     {
29195         this.dayField.setValue(v);
29196         this.inputEl.dom.value = this.getValue();
29197         this.validate();
29198         return;
29199     },
29200     
29201     setMonth : function(v)
29202     {
29203         this.monthField.setValue(v, true);
29204         this.inputEl.dom.value = this.getValue();
29205         this.validate();
29206         return;
29207     },
29208     
29209     setYear : function(v)
29210     {
29211         this.yearField.setValue(v);
29212         this.inputEl.dom.value = this.getValue();
29213         this.validate();
29214         return;
29215     },
29216     
29217     getDay : function()
29218     {
29219         return this.dayField.getValue();
29220     },
29221     
29222     getMonth : function()
29223     {
29224         return this.monthField.getValue();
29225     },
29226     
29227     getYear : function()
29228     {
29229         return this.yearField.getValue();
29230     },
29231     
29232     getValue : function()
29233     {
29234         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29235         
29236         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29237         
29238         return date;
29239     },
29240     
29241     reset : function()
29242     {
29243         this.setDay('');
29244         this.setMonth('');
29245         this.setYear('');
29246         this.inputEl.dom.value = '';
29247         this.validate();
29248         return;
29249     },
29250     
29251     validate : function()
29252     {
29253         var d = this.dayField.validate();
29254         var m = this.monthField.validate();
29255         var y = this.yearField.validate();
29256         
29257         var valid = true;
29258         
29259         if(
29260                 (!this.dayAllowBlank && !d) ||
29261                 (!this.monthAllowBlank && !m) ||
29262                 (!this.yearAllowBlank && !y)
29263         ){
29264             valid = false;
29265         }
29266         
29267         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29268             return valid;
29269         }
29270         
29271         if(valid){
29272             this.markValid();
29273             return valid;
29274         }
29275         
29276         this.markInvalid();
29277         
29278         return valid;
29279     },
29280     
29281     markValid : function()
29282     {
29283         
29284         var label = this.el.select('label', true).first();
29285         var icon = this.el.select('i.fa-star', true).first();
29286
29287         if(label && icon){
29288             icon.remove();
29289         }
29290         
29291         this.fireEvent('valid', this);
29292     },
29293     
29294      /**
29295      * Mark this field as invalid
29296      * @param {String} msg The validation message
29297      */
29298     markInvalid : function(msg)
29299     {
29300         
29301         var label = this.el.select('label', true).first();
29302         var icon = this.el.select('i.fa-star', true).first();
29303
29304         if(label && !icon){
29305             this.el.select('.roo-date-split-field-label', true).createChild({
29306                 tag : 'i',
29307                 cls : 'text-danger fa fa-lg fa-star',
29308                 tooltip : 'This field is required',
29309                 style : 'margin-right:5px;'
29310             }, label, true);
29311         }
29312         
29313         this.fireEvent('invalid', this, msg);
29314     },
29315     
29316     clearInvalid : function()
29317     {
29318         var label = this.el.select('label', true).first();
29319         var icon = this.el.select('i.fa-star', true).first();
29320
29321         if(label && icon){
29322             icon.remove();
29323         }
29324         
29325         this.fireEvent('valid', this);
29326     },
29327     
29328     getName: function()
29329     {
29330         return this.name;
29331     }
29332     
29333 });
29334
29335  /**
29336  *
29337  * This is based on 
29338  * http://masonry.desandro.com
29339  *
29340  * The idea is to render all the bricks based on vertical width...
29341  *
29342  * The original code extends 'outlayer' - we might need to use that....
29343  * 
29344  */
29345
29346
29347 /**
29348  * @class Roo.bootstrap.LayoutMasonry
29349  * @extends Roo.bootstrap.Component
29350  * Bootstrap Layout Masonry class
29351  * 
29352  * @constructor
29353  * Create a new Element
29354  * @param {Object} config The config object
29355  */
29356
29357 Roo.bootstrap.LayoutMasonry = function(config){
29358     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29359     
29360     this.bricks = [];
29361     
29362 };
29363
29364 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29365     
29366     /**
29367      * @cfg {Boolean} isLayoutInstant = no animation?
29368      */   
29369     isLayoutInstant : false, // needed?
29370    
29371     /**
29372      * @cfg {Number} boxWidth  width of the columns
29373      */   
29374     boxWidth : 450,
29375     
29376       /**
29377      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29378      */   
29379     boxHeight : 0,
29380     
29381     /**
29382      * @cfg {Number} padWidth padding below box..
29383      */   
29384     padWidth : 10, 
29385     
29386     /**
29387      * @cfg {Number} gutter gutter width..
29388      */   
29389     gutter : 10,
29390     
29391      /**
29392      * @cfg {Number} maxCols maximum number of columns
29393      */   
29394     
29395     maxCols: 0,
29396     
29397     /**
29398      * @cfg {Boolean} isAutoInitial defalut true
29399      */   
29400     isAutoInitial : true, 
29401     
29402     containerWidth: 0,
29403     
29404     /**
29405      * @cfg {Boolean} isHorizontal defalut false
29406      */   
29407     isHorizontal : false, 
29408
29409     currentSize : null,
29410     
29411     tag: 'div',
29412     
29413     cls: '',
29414     
29415     bricks: null, //CompositeElement
29416     
29417     cols : 1,
29418     
29419     _isLayoutInited : false,
29420     
29421 //    isAlternative : false, // only use for vertical layout...
29422     
29423     /**
29424      * @cfg {Number} alternativePadWidth padding below box..
29425      */   
29426     alternativePadWidth : 50, 
29427     
29428     getAutoCreate : function(){
29429         
29430         var cfg = {
29431             tag: this.tag,
29432             cls: 'blog-masonary-wrapper ' + this.cls,
29433             cn : {
29434                 cls : 'mas-boxes masonary'
29435             }
29436         };
29437         
29438         return cfg;
29439     },
29440     
29441     getChildContainer: function( )
29442     {
29443         if (this.boxesEl) {
29444             return this.boxesEl;
29445         }
29446         
29447         this.boxesEl = this.el.select('.mas-boxes').first();
29448         
29449         return this.boxesEl;
29450     },
29451     
29452     
29453     initEvents : function()
29454     {
29455         var _this = this;
29456         
29457         if(this.isAutoInitial){
29458             Roo.log('hook children rendered');
29459             this.on('childrenrendered', function() {
29460                 Roo.log('children rendered');
29461                 _this.initial();
29462             } ,this);
29463         }
29464     },
29465     
29466     initial : function()
29467     {
29468         this.currentSize = this.el.getBox(true);
29469         
29470         Roo.EventManager.onWindowResize(this.resize, this); 
29471
29472         if(!this.isAutoInitial){
29473             this.layout();
29474             return;
29475         }
29476         
29477         this.layout();
29478         
29479         return;
29480         //this.layout.defer(500,this);
29481         
29482     },
29483     
29484     resize : function()
29485     {
29486         Roo.log('resize');
29487         
29488         var cs = this.el.getBox(true);
29489         
29490         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29491             Roo.log("no change in with or X");
29492             return;
29493         }
29494         
29495         this.currentSize = cs;
29496         
29497         this.layout();
29498         
29499     },
29500     
29501     layout : function()
29502     {   
29503         this._resetLayout();
29504         
29505         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29506         
29507         this.layoutItems( isInstant );
29508       
29509         this._isLayoutInited = true;
29510         
29511     },
29512     
29513     _resetLayout : function()
29514     {
29515         if(this.isHorizontal){
29516             this.horizontalMeasureColumns();
29517             return;
29518         }
29519         
29520         this.verticalMeasureColumns();
29521         
29522     },
29523     
29524     verticalMeasureColumns : function()
29525     {
29526         this.getContainerWidth();
29527         
29528 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29529 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29530 //            return;
29531 //        }
29532         
29533         var boxWidth = this.boxWidth + this.padWidth;
29534         
29535         if(this.containerWidth < this.boxWidth){
29536             boxWidth = this.containerWidth
29537         }
29538         
29539         var containerWidth = this.containerWidth;
29540         
29541         var cols = Math.floor(containerWidth / boxWidth);
29542         
29543         this.cols = Math.max( cols, 1 );
29544         
29545         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29546         
29547         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29548         
29549         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29550         
29551         this.colWidth = boxWidth + avail - this.padWidth;
29552         
29553         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29554         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29555     },
29556     
29557     horizontalMeasureColumns : function()
29558     {
29559         this.getContainerWidth();
29560         
29561         var boxWidth = this.boxWidth;
29562         
29563         if(this.containerWidth < boxWidth){
29564             boxWidth = this.containerWidth;
29565         }
29566         
29567         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29568         
29569         this.el.setHeight(boxWidth);
29570         
29571     },
29572     
29573     getContainerWidth : function()
29574     {
29575         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29576     },
29577     
29578     layoutItems : function( isInstant )
29579     {
29580         var items = Roo.apply([], this.bricks);
29581         
29582         if(this.isHorizontal){
29583             this._horizontalLayoutItems( items , isInstant );
29584             return;
29585         }
29586         
29587 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29588 //            this._verticalAlternativeLayoutItems( items , isInstant );
29589 //            return;
29590 //        }
29591         
29592         this._verticalLayoutItems( items , isInstant );
29593         
29594     },
29595     
29596     _verticalLayoutItems : function ( items , isInstant)
29597     {
29598         if ( !items || !items.length ) {
29599             return;
29600         }
29601         
29602         var standard = [
29603             ['xs', 'xs', 'xs', 'tall'],
29604             ['xs', 'xs', 'tall'],
29605             ['xs', 'xs', 'sm'],
29606             ['xs', 'xs', 'xs'],
29607             ['xs', 'tall'],
29608             ['xs', 'sm'],
29609             ['xs', 'xs'],
29610             ['xs'],
29611             
29612             ['sm', 'xs', 'xs'],
29613             ['sm', 'xs'],
29614             ['sm'],
29615             
29616             ['tall', 'xs', 'xs', 'xs'],
29617             ['tall', 'xs', 'xs'],
29618             ['tall', 'xs'],
29619             ['tall']
29620             
29621         ];
29622         
29623         var queue = [];
29624         
29625         var boxes = [];
29626         
29627         var box = [];
29628         
29629         Roo.each(items, function(item, k){
29630             
29631             switch (item.size) {
29632                 // these layouts take up a full box,
29633                 case 'md' :
29634                 case 'md-left' :
29635                 case 'md-right' :
29636                 case 'wide' :
29637                     
29638                     if(box.length){
29639                         boxes.push(box);
29640                         box = [];
29641                     }
29642                     
29643                     boxes.push([item]);
29644                     
29645                     break;
29646                     
29647                 case 'xs' :
29648                 case 'sm' :
29649                 case 'tall' :
29650                     
29651                     box.push(item);
29652                     
29653                     break;
29654                 default :
29655                     break;
29656                     
29657             }
29658             
29659         }, this);
29660         
29661         if(box.length){
29662             boxes.push(box);
29663             box = [];
29664         }
29665         
29666         var filterPattern = function(box, length)
29667         {
29668             if(!box.length){
29669                 return;
29670             }
29671             
29672             var match = false;
29673             
29674             var pattern = box.slice(0, length);
29675             
29676             var format = [];
29677             
29678             Roo.each(pattern, function(i){
29679                 format.push(i.size);
29680             }, this);
29681             
29682             Roo.each(standard, function(s){
29683                 
29684                 if(String(s) != String(format)){
29685                     return;
29686                 }
29687                 
29688                 match = true;
29689                 return false;
29690                 
29691             }, this);
29692             
29693             if(!match && length == 1){
29694                 return;
29695             }
29696             
29697             if(!match){
29698                 filterPattern(box, length - 1);
29699                 return;
29700             }
29701                 
29702             queue.push(pattern);
29703
29704             box = box.slice(length, box.length);
29705
29706             filterPattern(box, 4);
29707
29708             return;
29709             
29710         }
29711         
29712         Roo.each(boxes, function(box, k){
29713             
29714             if(!box.length){
29715                 return;
29716             }
29717             
29718             if(box.length == 1){
29719                 queue.push(box);
29720                 return;
29721             }
29722             
29723             filterPattern(box, 4);
29724             
29725         }, this);
29726         
29727         this._processVerticalLayoutQueue( queue, isInstant );
29728         
29729     },
29730     
29731 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29732 //    {
29733 //        if ( !items || !items.length ) {
29734 //            return;
29735 //        }
29736 //
29737 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29738 //        
29739 //    },
29740     
29741     _horizontalLayoutItems : function ( items , isInstant)
29742     {
29743         if ( !items || !items.length || items.length < 3) {
29744             return;
29745         }
29746         
29747         items.reverse();
29748         
29749         var eItems = items.slice(0, 3);
29750         
29751         items = items.slice(3, items.length);
29752         
29753         var standard = [
29754             ['xs', 'xs', 'xs', 'wide'],
29755             ['xs', 'xs', 'wide'],
29756             ['xs', 'xs', 'sm'],
29757             ['xs', 'xs', 'xs'],
29758             ['xs', 'wide'],
29759             ['xs', 'sm'],
29760             ['xs', 'xs'],
29761             ['xs'],
29762             
29763             ['sm', 'xs', 'xs'],
29764             ['sm', 'xs'],
29765             ['sm'],
29766             
29767             ['wide', 'xs', 'xs', 'xs'],
29768             ['wide', 'xs', 'xs'],
29769             ['wide', 'xs'],
29770             ['wide'],
29771             
29772             ['wide-thin']
29773         ];
29774         
29775         var queue = [];
29776         
29777         var boxes = [];
29778         
29779         var box = [];
29780         
29781         Roo.each(items, function(item, k){
29782             
29783             switch (item.size) {
29784                 case 'md' :
29785                 case 'md-left' :
29786                 case 'md-right' :
29787                 case 'tall' :
29788                     
29789                     if(box.length){
29790                         boxes.push(box);
29791                         box = [];
29792                     }
29793                     
29794                     boxes.push([item]);
29795                     
29796                     break;
29797                     
29798                 case 'xs' :
29799                 case 'sm' :
29800                 case 'wide' :
29801                 case 'wide-thin' :
29802                     
29803                     box.push(item);
29804                     
29805                     break;
29806                 default :
29807                     break;
29808                     
29809             }
29810             
29811         }, this);
29812         
29813         if(box.length){
29814             boxes.push(box);
29815             box = [];
29816         }
29817         
29818         var filterPattern = function(box, length)
29819         {
29820             if(!box.length){
29821                 return;
29822             }
29823             
29824             var match = false;
29825             
29826             var pattern = box.slice(0, length);
29827             
29828             var format = [];
29829             
29830             Roo.each(pattern, function(i){
29831                 format.push(i.size);
29832             }, this);
29833             
29834             Roo.each(standard, function(s){
29835                 
29836                 if(String(s) != String(format)){
29837                     return;
29838                 }
29839                 
29840                 match = true;
29841                 return false;
29842                 
29843             }, this);
29844             
29845             if(!match && length == 1){
29846                 return;
29847             }
29848             
29849             if(!match){
29850                 filterPattern(box, length - 1);
29851                 return;
29852             }
29853                 
29854             queue.push(pattern);
29855
29856             box = box.slice(length, box.length);
29857
29858             filterPattern(box, 4);
29859
29860             return;
29861             
29862         }
29863         
29864         Roo.each(boxes, function(box, k){
29865             
29866             if(!box.length){
29867                 return;
29868             }
29869             
29870             if(box.length == 1){
29871                 queue.push(box);
29872                 return;
29873             }
29874             
29875             filterPattern(box, 4);
29876             
29877         }, this);
29878         
29879         
29880         var prune = [];
29881         
29882         var pos = this.el.getBox(true);
29883         
29884         var minX = pos.x;
29885         
29886         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29887         
29888         var hit_end = false;
29889         
29890         Roo.each(queue, function(box){
29891             
29892             if(hit_end){
29893                 
29894                 Roo.each(box, function(b){
29895                 
29896                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29897                     b.el.hide();
29898
29899                 }, this);
29900
29901                 return;
29902             }
29903             
29904             var mx = 0;
29905             
29906             Roo.each(box, function(b){
29907                 
29908                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29909                 b.el.show();
29910
29911                 mx = Math.max(mx, b.x);
29912                 
29913             }, this);
29914             
29915             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29916             
29917             if(maxX < minX){
29918                 
29919                 Roo.each(box, function(b){
29920                 
29921                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29922                     b.el.hide();
29923                     
29924                 }, this);
29925                 
29926                 hit_end = true;
29927                 
29928                 return;
29929             }
29930             
29931             prune.push(box);
29932             
29933         }, this);
29934         
29935         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29936     },
29937     
29938     /** Sets position of item in DOM
29939     * @param {Element} item
29940     * @param {Number} x - horizontal position
29941     * @param {Number} y - vertical position
29942     * @param {Boolean} isInstant - disables transitions
29943     */
29944     _processVerticalLayoutQueue : function( queue, isInstant )
29945     {
29946         var pos = this.el.getBox(true);
29947         var x = pos.x;
29948         var y = pos.y;
29949         var maxY = [];
29950         
29951         for (var i = 0; i < this.cols; i++){
29952             maxY[i] = pos.y;
29953         }
29954         
29955         Roo.each(queue, function(box, k){
29956             
29957             var col = k % this.cols;
29958             
29959             Roo.each(box, function(b,kk){
29960                 
29961                 b.el.position('absolute');
29962                 
29963                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29964                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29965                 
29966                 if(b.size == 'md-left' || b.size == 'md-right'){
29967                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29968                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29969                 }
29970                 
29971                 b.el.setWidth(width);
29972                 b.el.setHeight(height);
29973                 // iframe?
29974                 b.el.select('iframe',true).setSize(width,height);
29975                 
29976             }, this);
29977             
29978             for (var i = 0; i < this.cols; i++){
29979                 
29980                 if(maxY[i] < maxY[col]){
29981                     col = i;
29982                     continue;
29983                 }
29984                 
29985                 col = Math.min(col, i);
29986                 
29987             }
29988             
29989             x = pos.x + col * (this.colWidth + this.padWidth);
29990             
29991             y = maxY[col];
29992             
29993             var positions = [];
29994             
29995             switch (box.length){
29996                 case 1 :
29997                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29998                     break;
29999                 case 2 :
30000                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30001                     break;
30002                 case 3 :
30003                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30004                     break;
30005                 case 4 :
30006                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30007                     break;
30008                 default :
30009                     break;
30010             }
30011             
30012             Roo.each(box, function(b,kk){
30013                 
30014                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30015                 
30016                 var sz = b.el.getSize();
30017                 
30018                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30019                 
30020             }, this);
30021             
30022         }, this);
30023         
30024         var mY = 0;
30025         
30026         for (var i = 0; i < this.cols; i++){
30027             mY = Math.max(mY, maxY[i]);
30028         }
30029         
30030         this.el.setHeight(mY - pos.y);
30031         
30032     },
30033     
30034 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30035 //    {
30036 //        var pos = this.el.getBox(true);
30037 //        var x = pos.x;
30038 //        var y = pos.y;
30039 //        var maxX = pos.right;
30040 //        
30041 //        var maxHeight = 0;
30042 //        
30043 //        Roo.each(items, function(item, k){
30044 //            
30045 //            var c = k % 2;
30046 //            
30047 //            item.el.position('absolute');
30048 //                
30049 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30050 //
30051 //            item.el.setWidth(width);
30052 //
30053 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30054 //
30055 //            item.el.setHeight(height);
30056 //            
30057 //            if(c == 0){
30058 //                item.el.setXY([x, y], isInstant ? false : true);
30059 //            } else {
30060 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30061 //            }
30062 //            
30063 //            y = y + height + this.alternativePadWidth;
30064 //            
30065 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30066 //            
30067 //        }, this);
30068 //        
30069 //        this.el.setHeight(maxHeight);
30070 //        
30071 //    },
30072     
30073     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30074     {
30075         var pos = this.el.getBox(true);
30076         
30077         var minX = pos.x;
30078         var minY = pos.y;
30079         
30080         var maxX = pos.right;
30081         
30082         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30083         
30084         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30085         
30086         Roo.each(queue, function(box, k){
30087             
30088             Roo.each(box, function(b, kk){
30089                 
30090                 b.el.position('absolute');
30091                 
30092                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30093                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30094                 
30095                 if(b.size == 'md-left' || b.size == 'md-right'){
30096                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30097                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30098                 }
30099                 
30100                 b.el.setWidth(width);
30101                 b.el.setHeight(height);
30102                 
30103             }, this);
30104             
30105             if(!box.length){
30106                 return;
30107             }
30108             
30109             var positions = [];
30110             
30111             switch (box.length){
30112                 case 1 :
30113                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30114                     break;
30115                 case 2 :
30116                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30117                     break;
30118                 case 3 :
30119                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30120                     break;
30121                 case 4 :
30122                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30123                     break;
30124                 default :
30125                     break;
30126             }
30127             
30128             Roo.each(box, function(b,kk){
30129                 
30130                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30131                 
30132                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30133                 
30134             }, this);
30135             
30136         }, this);
30137         
30138     },
30139     
30140     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30141     {
30142         Roo.each(eItems, function(b,k){
30143             
30144             b.size = (k == 0) ? 'sm' : 'xs';
30145             b.x = (k == 0) ? 2 : 1;
30146             b.y = (k == 0) ? 2 : 1;
30147             
30148             b.el.position('absolute');
30149             
30150             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30151                 
30152             b.el.setWidth(width);
30153             
30154             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30155             
30156             b.el.setHeight(height);
30157             
30158         }, this);
30159
30160         var positions = [];
30161         
30162         positions.push({
30163             x : maxX - this.unitWidth * 2 - this.gutter,
30164             y : minY
30165         });
30166         
30167         positions.push({
30168             x : maxX - this.unitWidth,
30169             y : minY + (this.unitWidth + this.gutter) * 2
30170         });
30171         
30172         positions.push({
30173             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30174             y : minY
30175         });
30176         
30177         Roo.each(eItems, function(b,k){
30178             
30179             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30180
30181         }, this);
30182         
30183     },
30184     
30185     getVerticalOneBoxColPositions : function(x, y, box)
30186     {
30187         var pos = [];
30188         
30189         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30190         
30191         if(box[0].size == 'md-left'){
30192             rand = 0;
30193         }
30194         
30195         if(box[0].size == 'md-right'){
30196             rand = 1;
30197         }
30198         
30199         pos.push({
30200             x : x + (this.unitWidth + this.gutter) * rand,
30201             y : y
30202         });
30203         
30204         return pos;
30205     },
30206     
30207     getVerticalTwoBoxColPositions : function(x, y, box)
30208     {
30209         var pos = [];
30210         
30211         if(box[0].size == 'xs'){
30212             
30213             pos.push({
30214                 x : x,
30215                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30216             });
30217
30218             pos.push({
30219                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30220                 y : y
30221             });
30222             
30223             return pos;
30224             
30225         }
30226         
30227         pos.push({
30228             x : x,
30229             y : y
30230         });
30231
30232         pos.push({
30233             x : x + (this.unitWidth + this.gutter) * 2,
30234             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30235         });
30236         
30237         return pos;
30238         
30239     },
30240     
30241     getVerticalThreeBoxColPositions : function(x, y, box)
30242     {
30243         var pos = [];
30244         
30245         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30246             
30247             pos.push({
30248                 x : x,
30249                 y : y
30250             });
30251
30252             pos.push({
30253                 x : x + (this.unitWidth + this.gutter) * 1,
30254                 y : y
30255             });
30256             
30257             pos.push({
30258                 x : x + (this.unitWidth + this.gutter) * 2,
30259                 y : y
30260             });
30261             
30262             return pos;
30263             
30264         }
30265         
30266         if(box[0].size == 'xs' && box[1].size == 'xs'){
30267             
30268             pos.push({
30269                 x : x,
30270                 y : y
30271             });
30272
30273             pos.push({
30274                 x : x,
30275                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30276             });
30277             
30278             pos.push({
30279                 x : x + (this.unitWidth + this.gutter) * 1,
30280                 y : y
30281             });
30282             
30283             return pos;
30284             
30285         }
30286         
30287         pos.push({
30288             x : x,
30289             y : y
30290         });
30291
30292         pos.push({
30293             x : x + (this.unitWidth + this.gutter) * 2,
30294             y : y
30295         });
30296
30297         pos.push({
30298             x : x + (this.unitWidth + this.gutter) * 2,
30299             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30300         });
30301             
30302         return pos;
30303         
30304     },
30305     
30306     getVerticalFourBoxColPositions : function(x, y, box)
30307     {
30308         var pos = [];
30309         
30310         if(box[0].size == 'xs'){
30311             
30312             pos.push({
30313                 x : x,
30314                 y : y
30315             });
30316
30317             pos.push({
30318                 x : x,
30319                 y : y + (this.unitHeight + this.gutter) * 1
30320             });
30321             
30322             pos.push({
30323                 x : x,
30324                 y : y + (this.unitHeight + this.gutter) * 2
30325             });
30326             
30327             pos.push({
30328                 x : x + (this.unitWidth + this.gutter) * 1,
30329                 y : y
30330             });
30331             
30332             return pos;
30333             
30334         }
30335         
30336         pos.push({
30337             x : x,
30338             y : y
30339         });
30340
30341         pos.push({
30342             x : x + (this.unitWidth + this.gutter) * 2,
30343             y : y
30344         });
30345
30346         pos.push({
30347             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30348             y : y + (this.unitHeight + this.gutter) * 1
30349         });
30350
30351         pos.push({
30352             x : x + (this.unitWidth + this.gutter) * 2,
30353             y : y + (this.unitWidth + this.gutter) * 2
30354         });
30355
30356         return pos;
30357         
30358     },
30359     
30360     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30361     {
30362         var pos = [];
30363         
30364         if(box[0].size == 'md-left'){
30365             pos.push({
30366                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30367                 y : minY
30368             });
30369             
30370             return pos;
30371         }
30372         
30373         if(box[0].size == 'md-right'){
30374             pos.push({
30375                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30376                 y : minY + (this.unitWidth + this.gutter) * 1
30377             });
30378             
30379             return pos;
30380         }
30381         
30382         var rand = Math.floor(Math.random() * (4 - box[0].y));
30383         
30384         pos.push({
30385             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30386             y : minY + (this.unitWidth + this.gutter) * rand
30387         });
30388         
30389         return pos;
30390         
30391     },
30392     
30393     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30394     {
30395         var pos = [];
30396         
30397         if(box[0].size == 'xs'){
30398             
30399             pos.push({
30400                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30401                 y : minY
30402             });
30403
30404             pos.push({
30405                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30406                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30407             });
30408             
30409             return pos;
30410             
30411         }
30412         
30413         pos.push({
30414             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30415             y : minY
30416         });
30417
30418         pos.push({
30419             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30420             y : minY + (this.unitWidth + this.gutter) * 2
30421         });
30422         
30423         return pos;
30424         
30425     },
30426     
30427     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30428     {
30429         var pos = [];
30430         
30431         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30432             
30433             pos.push({
30434                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30435                 y : minY
30436             });
30437
30438             pos.push({
30439                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30440                 y : minY + (this.unitWidth + this.gutter) * 1
30441             });
30442             
30443             pos.push({
30444                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30445                 y : minY + (this.unitWidth + this.gutter) * 2
30446             });
30447             
30448             return pos;
30449             
30450         }
30451         
30452         if(box[0].size == 'xs' && box[1].size == 'xs'){
30453             
30454             pos.push({
30455                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30456                 y : minY
30457             });
30458
30459             pos.push({
30460                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30461                 y : minY
30462             });
30463             
30464             pos.push({
30465                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30466                 y : minY + (this.unitWidth + this.gutter) * 1
30467             });
30468             
30469             return pos;
30470             
30471         }
30472         
30473         pos.push({
30474             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30475             y : minY
30476         });
30477
30478         pos.push({
30479             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30480             y : minY + (this.unitWidth + this.gutter) * 2
30481         });
30482
30483         pos.push({
30484             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30485             y : minY + (this.unitWidth + this.gutter) * 2
30486         });
30487             
30488         return pos;
30489         
30490     },
30491     
30492     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30493     {
30494         var pos = [];
30495         
30496         if(box[0].size == 'xs'){
30497             
30498             pos.push({
30499                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30500                 y : minY
30501             });
30502
30503             pos.push({
30504                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30505                 y : minY
30506             });
30507             
30508             pos.push({
30509                 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),
30510                 y : minY
30511             });
30512             
30513             pos.push({
30514                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30515                 y : minY + (this.unitWidth + this.gutter) * 1
30516             });
30517             
30518             return pos;
30519             
30520         }
30521         
30522         pos.push({
30523             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30524             y : minY
30525         });
30526         
30527         pos.push({
30528             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30529             y : minY + (this.unitWidth + this.gutter) * 2
30530         });
30531         
30532         pos.push({
30533             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30534             y : minY + (this.unitWidth + this.gutter) * 2
30535         });
30536         
30537         pos.push({
30538             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),
30539             y : minY + (this.unitWidth + this.gutter) * 2
30540         });
30541
30542         return pos;
30543         
30544     }
30545     
30546 });
30547
30548  
30549
30550  /**
30551  *
30552  * This is based on 
30553  * http://masonry.desandro.com
30554  *
30555  * The idea is to render all the bricks based on vertical width...
30556  *
30557  * The original code extends 'outlayer' - we might need to use that....
30558  * 
30559  */
30560
30561
30562 /**
30563  * @class Roo.bootstrap.LayoutMasonryAuto
30564  * @extends Roo.bootstrap.Component
30565  * Bootstrap Layout Masonry class
30566  * 
30567  * @constructor
30568  * Create a new Element
30569  * @param {Object} config The config object
30570  */
30571
30572 Roo.bootstrap.LayoutMasonryAuto = function(config){
30573     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30574 };
30575
30576 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30577     
30578       /**
30579      * @cfg {Boolean} isFitWidth  - resize the width..
30580      */   
30581     isFitWidth : false,  // options..
30582     /**
30583      * @cfg {Boolean} isOriginLeft = left align?
30584      */   
30585     isOriginLeft : true,
30586     /**
30587      * @cfg {Boolean} isOriginTop = top align?
30588      */   
30589     isOriginTop : false,
30590     /**
30591      * @cfg {Boolean} isLayoutInstant = no animation?
30592      */   
30593     isLayoutInstant : false, // needed?
30594     /**
30595      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30596      */   
30597     isResizingContainer : true,
30598     /**
30599      * @cfg {Number} columnWidth  width of the columns 
30600      */   
30601     
30602     columnWidth : 0,
30603     
30604     /**
30605      * @cfg {Number} maxCols maximum number of columns
30606      */   
30607     
30608     maxCols: 0,
30609     /**
30610      * @cfg {Number} padHeight padding below box..
30611      */   
30612     
30613     padHeight : 10, 
30614     
30615     /**
30616      * @cfg {Boolean} isAutoInitial defalut true
30617      */   
30618     
30619     isAutoInitial : true, 
30620     
30621     // private?
30622     gutter : 0,
30623     
30624     containerWidth: 0,
30625     initialColumnWidth : 0,
30626     currentSize : null,
30627     
30628     colYs : null, // array.
30629     maxY : 0,
30630     padWidth: 10,
30631     
30632     
30633     tag: 'div',
30634     cls: '',
30635     bricks: null, //CompositeElement
30636     cols : 0, // array?
30637     // element : null, // wrapped now this.el
30638     _isLayoutInited : null, 
30639     
30640     
30641     getAutoCreate : function(){
30642         
30643         var cfg = {
30644             tag: this.tag,
30645             cls: 'blog-masonary-wrapper ' + this.cls,
30646             cn : {
30647                 cls : 'mas-boxes masonary'
30648             }
30649         };
30650         
30651         return cfg;
30652     },
30653     
30654     getChildContainer: function( )
30655     {
30656         if (this.boxesEl) {
30657             return this.boxesEl;
30658         }
30659         
30660         this.boxesEl = this.el.select('.mas-boxes').first();
30661         
30662         return this.boxesEl;
30663     },
30664     
30665     
30666     initEvents : function()
30667     {
30668         var _this = this;
30669         
30670         if(this.isAutoInitial){
30671             Roo.log('hook children rendered');
30672             this.on('childrenrendered', function() {
30673                 Roo.log('children rendered');
30674                 _this.initial();
30675             } ,this);
30676         }
30677         
30678     },
30679     
30680     initial : function()
30681     {
30682         this.reloadItems();
30683
30684         this.currentSize = this.el.getBox(true);
30685
30686         /// was window resize... - let's see if this works..
30687         Roo.EventManager.onWindowResize(this.resize, this); 
30688
30689         if(!this.isAutoInitial){
30690             this.layout();
30691             return;
30692         }
30693         
30694         this.layout.defer(500,this);
30695     },
30696     
30697     reloadItems: function()
30698     {
30699         this.bricks = this.el.select('.masonry-brick', true);
30700         
30701         this.bricks.each(function(b) {
30702             //Roo.log(b.getSize());
30703             if (!b.attr('originalwidth')) {
30704                 b.attr('originalwidth',  b.getSize().width);
30705             }
30706             
30707         });
30708         
30709         Roo.log(this.bricks.elements.length);
30710     },
30711     
30712     resize : function()
30713     {
30714         Roo.log('resize');
30715         var cs = this.el.getBox(true);
30716         
30717         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30718             Roo.log("no change in with or X");
30719             return;
30720         }
30721         this.currentSize = cs;
30722         this.layout();
30723     },
30724     
30725     layout : function()
30726     {
30727          Roo.log('layout');
30728         this._resetLayout();
30729         //this._manageStamps();
30730       
30731         // don't animate first layout
30732         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30733         this.layoutItems( isInstant );
30734       
30735         // flag for initalized
30736         this._isLayoutInited = true;
30737     },
30738     
30739     layoutItems : function( isInstant )
30740     {
30741         //var items = this._getItemsForLayout( this.items );
30742         // original code supports filtering layout items.. we just ignore it..
30743         
30744         this._layoutItems( this.bricks , isInstant );
30745       
30746         this._postLayout();
30747     },
30748     _layoutItems : function ( items , isInstant)
30749     {
30750        //this.fireEvent( 'layout', this, items );
30751     
30752
30753         if ( !items || !items.elements.length ) {
30754           // no items, emit event with empty array
30755             return;
30756         }
30757
30758         var queue = [];
30759         items.each(function(item) {
30760             Roo.log("layout item");
30761             Roo.log(item);
30762             // get x/y object from method
30763             var position = this._getItemLayoutPosition( item );
30764             // enqueue
30765             position.item = item;
30766             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30767             queue.push( position );
30768         }, this);
30769       
30770         this._processLayoutQueue( queue );
30771     },
30772     /** Sets position of item in DOM
30773     * @param {Element} item
30774     * @param {Number} x - horizontal position
30775     * @param {Number} y - vertical position
30776     * @param {Boolean} isInstant - disables transitions
30777     */
30778     _processLayoutQueue : function( queue )
30779     {
30780         for ( var i=0, len = queue.length; i < len; i++ ) {
30781             var obj = queue[i];
30782             obj.item.position('absolute');
30783             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30784         }
30785     },
30786       
30787     
30788     /**
30789     * Any logic you want to do after each layout,
30790     * i.e. size the container
30791     */
30792     _postLayout : function()
30793     {
30794         this.resizeContainer();
30795     },
30796     
30797     resizeContainer : function()
30798     {
30799         if ( !this.isResizingContainer ) {
30800             return;
30801         }
30802         var size = this._getContainerSize();
30803         if ( size ) {
30804             this.el.setSize(size.width,size.height);
30805             this.boxesEl.setSize(size.width,size.height);
30806         }
30807     },
30808     
30809     
30810     
30811     _resetLayout : function()
30812     {
30813         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30814         this.colWidth = this.el.getWidth();
30815         //this.gutter = this.el.getWidth(); 
30816         
30817         this.measureColumns();
30818
30819         // reset column Y
30820         var i = this.cols;
30821         this.colYs = [];
30822         while (i--) {
30823             this.colYs.push( 0 );
30824         }
30825     
30826         this.maxY = 0;
30827     },
30828
30829     measureColumns : function()
30830     {
30831         this.getContainerWidth();
30832       // if columnWidth is 0, default to outerWidth of first item
30833         if ( !this.columnWidth ) {
30834             var firstItem = this.bricks.first();
30835             Roo.log(firstItem);
30836             this.columnWidth  = this.containerWidth;
30837             if (firstItem && firstItem.attr('originalwidth') ) {
30838                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30839             }
30840             // columnWidth fall back to item of first element
30841             Roo.log("set column width?");
30842                         this.initialColumnWidth = this.columnWidth  ;
30843
30844             // if first elem has no width, default to size of container
30845             
30846         }
30847         
30848         
30849         if (this.initialColumnWidth) {
30850             this.columnWidth = this.initialColumnWidth;
30851         }
30852         
30853         
30854             
30855         // column width is fixed at the top - however if container width get's smaller we should
30856         // reduce it...
30857         
30858         // this bit calcs how man columns..
30859             
30860         var columnWidth = this.columnWidth += this.gutter;
30861       
30862         // calculate columns
30863         var containerWidth = this.containerWidth + this.gutter;
30864         
30865         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30866         // fix rounding errors, typically with gutters
30867         var excess = columnWidth - containerWidth % columnWidth;
30868         
30869         
30870         // if overshoot is less than a pixel, round up, otherwise floor it
30871         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30872         cols = Math[ mathMethod ]( cols );
30873         this.cols = Math.max( cols, 1 );
30874         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30875         
30876          // padding positioning..
30877         var totalColWidth = this.cols * this.columnWidth;
30878         var padavail = this.containerWidth - totalColWidth;
30879         // so for 2 columns - we need 3 'pads'
30880         
30881         var padNeeded = (1+this.cols) * this.padWidth;
30882         
30883         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30884         
30885         this.columnWidth += padExtra
30886         //this.padWidth = Math.floor(padavail /  ( this.cols));
30887         
30888         // adjust colum width so that padding is fixed??
30889         
30890         // we have 3 columns ... total = width * 3
30891         // we have X left over... that should be used by 
30892         
30893         //if (this.expandC) {
30894             
30895         //}
30896         
30897         
30898         
30899     },
30900     
30901     getContainerWidth : function()
30902     {
30903        /* // container is parent if fit width
30904         var container = this.isFitWidth ? this.element.parentNode : this.element;
30905         // check that this.size and size are there
30906         // IE8 triggers resize on body size change, so they might not be
30907         
30908         var size = getSize( container );  //FIXME
30909         this.containerWidth = size && size.innerWidth; //FIXME
30910         */
30911          
30912         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30913         
30914     },
30915     
30916     _getItemLayoutPosition : function( item )  // what is item?
30917     {
30918         // we resize the item to our columnWidth..
30919       
30920         item.setWidth(this.columnWidth);
30921         item.autoBoxAdjust  = false;
30922         
30923         var sz = item.getSize();
30924  
30925         // how many columns does this brick span
30926         var remainder = this.containerWidth % this.columnWidth;
30927         
30928         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30929         // round if off by 1 pixel, otherwise use ceil
30930         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30931         colSpan = Math.min( colSpan, this.cols );
30932         
30933         // normally this should be '1' as we dont' currently allow multi width columns..
30934         
30935         var colGroup = this._getColGroup( colSpan );
30936         // get the minimum Y value from the columns
30937         var minimumY = Math.min.apply( Math, colGroup );
30938         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30939         
30940         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30941          
30942         // position the brick
30943         var position = {
30944             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30945             y: this.currentSize.y + minimumY + this.padHeight
30946         };
30947         
30948         Roo.log(position);
30949         // apply setHeight to necessary columns
30950         var setHeight = minimumY + sz.height + this.padHeight;
30951         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30952         
30953         var setSpan = this.cols + 1 - colGroup.length;
30954         for ( var i = 0; i < setSpan; i++ ) {
30955           this.colYs[ shortColIndex + i ] = setHeight ;
30956         }
30957       
30958         return position;
30959     },
30960     
30961     /**
30962      * @param {Number} colSpan - number of columns the element spans
30963      * @returns {Array} colGroup
30964      */
30965     _getColGroup : function( colSpan )
30966     {
30967         if ( colSpan < 2 ) {
30968           // if brick spans only one column, use all the column Ys
30969           return this.colYs;
30970         }
30971       
30972         var colGroup = [];
30973         // how many different places could this brick fit horizontally
30974         var groupCount = this.cols + 1 - colSpan;
30975         // for each group potential horizontal position
30976         for ( var i = 0; i < groupCount; i++ ) {
30977           // make an array of colY values for that one group
30978           var groupColYs = this.colYs.slice( i, i + colSpan );
30979           // and get the max value of the array
30980           colGroup[i] = Math.max.apply( Math, groupColYs );
30981         }
30982         return colGroup;
30983     },
30984     /*
30985     _manageStamp : function( stamp )
30986     {
30987         var stampSize =  stamp.getSize();
30988         var offset = stamp.getBox();
30989         // get the columns that this stamp affects
30990         var firstX = this.isOriginLeft ? offset.x : offset.right;
30991         var lastX = firstX + stampSize.width;
30992         var firstCol = Math.floor( firstX / this.columnWidth );
30993         firstCol = Math.max( 0, firstCol );
30994         
30995         var lastCol = Math.floor( lastX / this.columnWidth );
30996         // lastCol should not go over if multiple of columnWidth #425
30997         lastCol -= lastX % this.columnWidth ? 0 : 1;
30998         lastCol = Math.min( this.cols - 1, lastCol );
30999         
31000         // set colYs to bottom of the stamp
31001         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31002             stampSize.height;
31003             
31004         for ( var i = firstCol; i <= lastCol; i++ ) {
31005           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31006         }
31007     },
31008     */
31009     
31010     _getContainerSize : function()
31011     {
31012         this.maxY = Math.max.apply( Math, this.colYs );
31013         var size = {
31014             height: this.maxY
31015         };
31016       
31017         if ( this.isFitWidth ) {
31018             size.width = this._getContainerFitWidth();
31019         }
31020       
31021         return size;
31022     },
31023     
31024     _getContainerFitWidth : function()
31025     {
31026         var unusedCols = 0;
31027         // count unused columns
31028         var i = this.cols;
31029         while ( --i ) {
31030           if ( this.colYs[i] !== 0 ) {
31031             break;
31032           }
31033           unusedCols++;
31034         }
31035         // fit container to columns that have been used
31036         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31037     },
31038     
31039     needsResizeLayout : function()
31040     {
31041         var previousWidth = this.containerWidth;
31042         this.getContainerWidth();
31043         return previousWidth !== this.containerWidth;
31044     }
31045  
31046 });
31047
31048  
31049
31050  /*
31051  * - LGPL
31052  *
31053  * element
31054  * 
31055  */
31056
31057 /**
31058  * @class Roo.bootstrap.MasonryBrick
31059  * @extends Roo.bootstrap.Component
31060  * Bootstrap MasonryBrick class
31061  * 
31062  * @constructor
31063  * Create a new MasonryBrick
31064  * @param {Object} config The config object
31065  */
31066
31067 Roo.bootstrap.MasonryBrick = function(config){
31068     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31069     
31070     this.addEvents({
31071         // raw events
31072         /**
31073          * @event click
31074          * When a MasonryBrick is clcik
31075          * @param {Roo.bootstrap.MasonryBrick} this
31076          * @param {Roo.EventObject} e
31077          */
31078         "click" : true
31079     });
31080 };
31081
31082 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31083     
31084     /**
31085      * @cfg {String} title
31086      */   
31087     title : '',
31088     /**
31089      * @cfg {String} html
31090      */   
31091     html : '',
31092     /**
31093      * @cfg {String} bgimage
31094      */   
31095     bgimage : '',
31096     /**
31097      * @cfg {String} videourl
31098      */   
31099     videourl : '',
31100     /**
31101      * @cfg {String} cls
31102      */   
31103     cls : '',
31104     /**
31105      * @cfg {String} href
31106      */   
31107     href : '',
31108     /**
31109      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31110      */   
31111     size : 'xs',
31112     
31113     /**
31114      * @cfg {String} (center|bottom) placetitle
31115      */   
31116     placetitle : '',
31117     
31118     /**
31119      * @cfg {Boolean} isFitContainer defalut true
31120      */   
31121     isFitContainer : true, 
31122     
31123     /**
31124      * @cfg {Boolean} preventDefault defalut false
31125      */   
31126     preventDefault : false, 
31127     
31128     getAutoCreate : function()
31129     {
31130         if(!this.isFitContainer){
31131             return this.getSplitAutoCreate();
31132         }
31133         
31134         var cls = 'masonry-brick masonry-brick-full';
31135         
31136         if(this.href.length){
31137             cls += ' masonry-brick-link';
31138         }
31139         
31140         if(this.bgimage.length){
31141             cls += ' masonry-brick-image';
31142         }
31143         
31144         if(!this.html.length){
31145             cls += ' enable-mask';
31146         }
31147         
31148         if(this.size){
31149             cls += ' masonry-' + this.size + '-brick';
31150         }
31151         
31152         if(this.placetitle.length){
31153             
31154             switch (this.placetitle) {
31155                 case 'center' :
31156                     cls += ' masonry-center-title';
31157                     break;
31158                 case 'bottom' :
31159                     cls += ' masonry-bottom-title';
31160                     break;
31161                 default:
31162                     break;
31163             }
31164             
31165         } else {
31166             if(!this.html.length && !this.bgimage.length){
31167                 cls += ' masonry-center-title';
31168             }
31169
31170             if(!this.html.length && this.bgimage.length){
31171                 cls += ' masonry-bottom-title';
31172             }
31173         }
31174         
31175         if(this.cls){
31176             cls += ' ' + this.cls;
31177         }
31178         
31179         var cfg = {
31180             tag: (this.href.length) ? 'a' : 'div',
31181             cls: cls,
31182             cn: [
31183                 {
31184                     tag: 'div',
31185                     cls: 'masonry-brick-paragraph',
31186                     cn: []
31187                 }
31188             ]
31189         };
31190         
31191         if(this.href.length){
31192             cfg.href = this.href;
31193         }
31194         
31195         var cn = cfg.cn[0].cn;
31196         
31197         if(this.title.length){
31198             cn.push({
31199                 tag: 'h4',
31200                 cls: 'masonry-brick-title',
31201                 html: this.title
31202             });
31203         }
31204         
31205         if(this.html.length){
31206             cn.push({
31207                 tag: 'p',
31208                 cls: 'masonry-brick-text',
31209                 html: this.html
31210             });
31211         }  
31212         if (!this.title.length && !this.html.length) {
31213             cfg.cn[0].cls += ' hide';
31214         }
31215         
31216         if(this.bgimage.length){
31217             cfg.cn.push({
31218                 tag: 'img',
31219                 cls: 'masonry-brick-image-view',
31220                 src: this.bgimage
31221             });
31222         }
31223         
31224         if(this.videourl.length){
31225             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31226             // youtube support only?
31227             cfg.cn.push({
31228                 tag: 'iframe',
31229                 cls: 'masonry-brick-image-view',
31230                 src: vurl,
31231                 frameborder : 0,
31232                 allowfullscreen : true
31233             });
31234             
31235             
31236         }
31237         
31238         cfg.cn.push({
31239             tag: 'div',
31240             cls: 'masonry-brick-mask'
31241         });
31242         
31243         return cfg;
31244         
31245     },
31246     
31247     getSplitAutoCreate : function()
31248     {
31249         var cls = 'masonry-brick masonry-brick-split';
31250         
31251         if(this.href.length){
31252             cls += ' masonry-brick-link';
31253         }
31254         
31255         if(this.bgimage.length){
31256             cls += ' masonry-brick-image';
31257         }
31258         
31259         if(this.size){
31260             cls += ' masonry-' + this.size + '-brick';
31261         }
31262         
31263         switch (this.placetitle) {
31264             case 'center' :
31265                 cls += ' masonry-center-title';
31266                 break;
31267             case 'bottom' :
31268                 cls += ' masonry-bottom-title';
31269                 break;
31270             default:
31271                 if(!this.bgimage.length){
31272                     cls += ' masonry-center-title';
31273                 }
31274
31275                 if(this.bgimage.length){
31276                     cls += ' masonry-bottom-title';
31277                 }
31278                 break;
31279         }
31280         
31281         if(this.cls){
31282             cls += ' ' + this.cls;
31283         }
31284         
31285         var cfg = {
31286             tag: (this.href.length) ? 'a' : 'div',
31287             cls: cls,
31288             cn: [
31289                 {
31290                     tag: 'div',
31291                     cls: 'masonry-brick-split-head',
31292                     cn: [
31293                         {
31294                             tag: 'div',
31295                             cls: 'masonry-brick-paragraph',
31296                             cn: []
31297                         }
31298                     ]
31299                 },
31300                 {
31301                     tag: 'div',
31302                     cls: 'masonry-brick-split-body',
31303                     cn: []
31304                 }
31305             ]
31306         };
31307         
31308         if(this.href.length){
31309             cfg.href = this.href;
31310         }
31311         
31312         if(this.title.length){
31313             cfg.cn[0].cn[0].cn.push({
31314                 tag: 'h4',
31315                 cls: 'masonry-brick-title',
31316                 html: this.title
31317             });
31318         }
31319         
31320         if(this.html.length){
31321             cfg.cn[1].cn.push({
31322                 tag: 'p',
31323                 cls: 'masonry-brick-text',
31324                 html: this.html
31325             });
31326         }
31327
31328         if(this.bgimage.length){
31329             cfg.cn[0].cn.push({
31330                 tag: 'img',
31331                 cls: 'masonry-brick-image-view',
31332                 src: this.bgimage
31333             });
31334         }
31335         
31336         if(this.videourl.length){
31337             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31338             // youtube support only?
31339             cfg.cn[0].cn.cn.push({
31340                 tag: 'iframe',
31341                 cls: 'masonry-brick-image-view',
31342                 src: vurl,
31343                 frameborder : 0,
31344                 allowfullscreen : true
31345             });
31346         }
31347         
31348         return cfg;
31349     },
31350     
31351     initEvents: function() 
31352     {
31353         switch (this.size) {
31354             case 'xs' :
31355                 this.x = 1;
31356                 this.y = 1;
31357                 break;
31358             case 'sm' :
31359                 this.x = 2;
31360                 this.y = 2;
31361                 break;
31362             case 'md' :
31363             case 'md-left' :
31364             case 'md-right' :
31365                 this.x = 3;
31366                 this.y = 3;
31367                 break;
31368             case 'tall' :
31369                 this.x = 2;
31370                 this.y = 3;
31371                 break;
31372             case 'wide' :
31373                 this.x = 3;
31374                 this.y = 2;
31375                 break;
31376             case 'wide-thin' :
31377                 this.x = 3;
31378                 this.y = 1;
31379                 break;
31380                         
31381             default :
31382                 break;
31383         }
31384         
31385         if(Roo.isTouch){
31386             this.el.on('touchstart', this.onTouchStart, this);
31387             this.el.on('touchmove', this.onTouchMove, this);
31388             this.el.on('touchend', this.onTouchEnd, this);
31389             this.el.on('contextmenu', this.onContextMenu, this);
31390         } else {
31391             this.el.on('mouseenter'  ,this.enter, this);
31392             this.el.on('mouseleave', this.leave, this);
31393             this.el.on('click', this.onClick, this);
31394         }
31395         
31396         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31397             this.parent().bricks.push(this);   
31398         }
31399         
31400     },
31401     
31402     onClick: function(e, el)
31403     {
31404         var time = this.endTimer - this.startTimer;
31405         
31406         if(Roo.isTouch){
31407             if(time > 1000){
31408                 e.preventDefault();
31409                 return;
31410             }
31411         }
31412         
31413         if(!this.preventDefault){
31414             return;
31415         }
31416         
31417         e.preventDefault();
31418         this.fireEvent('click', this);
31419     },
31420     
31421     enter: function(e, el)
31422     {
31423         e.preventDefault();
31424         
31425         if(!this.isFitContainer){
31426             return;
31427         }
31428         
31429         if(this.bgimage.length && this.html.length){
31430             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31431         }
31432     },
31433     
31434     leave: function(e, el)
31435     {
31436         e.preventDefault();
31437         
31438         if(!this.isFitContainer){
31439             return;
31440         }
31441         
31442         if(this.bgimage.length && this.html.length){
31443             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31444         }
31445     },
31446     
31447     onTouchStart: function(e, el)
31448     {
31449 //        e.preventDefault();
31450         
31451         this.touchmoved = false;
31452         
31453         if(!this.isFitContainer){
31454             return;
31455         }
31456         
31457         if(!this.bgimage.length || !this.html.length){
31458             return;
31459         }
31460         
31461         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31462         
31463         this.timer = new Date().getTime();
31464         
31465     },
31466     
31467     onTouchMove: function(e, el)
31468     {
31469         this.touchmoved = true;
31470     },
31471     
31472     onContextMenu : function(e,el)
31473     {
31474         e.preventDefault();
31475         e.stopPropagation();
31476         return false;
31477     },
31478     
31479     onTouchEnd: function(e, el)
31480     {
31481 //        e.preventDefault();
31482         
31483         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31484         
31485             this.leave(e,el);
31486             
31487             return;
31488         }
31489         
31490         if(!this.bgimage.length || !this.html.length){
31491             
31492             if(this.href.length){
31493                 window.location.href = this.href;
31494             }
31495             
31496             return;
31497         }
31498         
31499         if(!this.isFitContainer){
31500             return;
31501         }
31502         
31503         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31504         
31505         window.location.href = this.href;
31506     }
31507     
31508 });
31509
31510  
31511
31512  /*
31513  * - LGPL
31514  *
31515  * element
31516  * 
31517  */
31518
31519 /**
31520  * @class Roo.bootstrap.Brick
31521  * @extends Roo.bootstrap.Component
31522  * Bootstrap Brick class
31523  * 
31524  * @constructor
31525  * Create a new Brick
31526  * @param {Object} config The config object
31527  */
31528
31529 Roo.bootstrap.Brick = function(config){
31530     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31531     
31532     this.addEvents({
31533         // raw events
31534         /**
31535          * @event click
31536          * When a Brick is click
31537          * @param {Roo.bootstrap.Brick} this
31538          * @param {Roo.EventObject} e
31539          */
31540         "click" : true
31541     });
31542 };
31543
31544 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31545     
31546     /**
31547      * @cfg {String} title
31548      */   
31549     title : '',
31550     /**
31551      * @cfg {String} html
31552      */   
31553     html : '',
31554     /**
31555      * @cfg {String} bgimage
31556      */   
31557     bgimage : '',
31558     /**
31559      * @cfg {String} cls
31560      */   
31561     cls : '',
31562     /**
31563      * @cfg {String} href
31564      */   
31565     href : '',
31566     /**
31567      * @cfg {String} video
31568      */   
31569     video : '',
31570     /**
31571      * @cfg {Boolean} square
31572      */   
31573     square : true,
31574     
31575     getAutoCreate : function()
31576     {
31577         var cls = 'roo-brick';
31578         
31579         if(this.href.length){
31580             cls += ' roo-brick-link';
31581         }
31582         
31583         if(this.bgimage.length){
31584             cls += ' roo-brick-image';
31585         }
31586         
31587         if(!this.html.length && !this.bgimage.length){
31588             cls += ' roo-brick-center-title';
31589         }
31590         
31591         if(!this.html.length && this.bgimage.length){
31592             cls += ' roo-brick-bottom-title';
31593         }
31594         
31595         if(this.cls){
31596             cls += ' ' + this.cls;
31597         }
31598         
31599         var cfg = {
31600             tag: (this.href.length) ? 'a' : 'div',
31601             cls: cls,
31602             cn: [
31603                 {
31604                     tag: 'div',
31605                     cls: 'roo-brick-paragraph',
31606                     cn: []
31607                 }
31608             ]
31609         };
31610         
31611         if(this.href.length){
31612             cfg.href = this.href;
31613         }
31614         
31615         var cn = cfg.cn[0].cn;
31616         
31617         if(this.title.length){
31618             cn.push({
31619                 tag: 'h4',
31620                 cls: 'roo-brick-title',
31621                 html: this.title
31622             });
31623         }
31624         
31625         if(this.html.length){
31626             cn.push({
31627                 tag: 'p',
31628                 cls: 'roo-brick-text',
31629                 html: this.html
31630             });
31631         } else {
31632             cn.cls += ' hide';
31633         }
31634         
31635         if(this.bgimage.length){
31636             cfg.cn.push({
31637                 tag: 'img',
31638                 cls: 'roo-brick-image-view',
31639                 src: this.bgimage
31640             });
31641         }
31642         
31643         return cfg;
31644     },
31645     
31646     initEvents: function() 
31647     {
31648         if(this.title.length || this.html.length){
31649             this.el.on('mouseenter'  ,this.enter, this);
31650             this.el.on('mouseleave', this.leave, this);
31651         }
31652         
31653         
31654         Roo.EventManager.onWindowResize(this.resize, this); 
31655         
31656         this.resize();
31657     },
31658     
31659     resize : function()
31660     {
31661         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31662         
31663         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31664         
31665         if(this.bgimage.length){
31666             var image = this.el.select('.roo-brick-image-view', true).first();
31667             image.setWidth(paragraph.getWidth());
31668             image.setHeight(paragraph.getWidth());
31669             
31670             this.el.setHeight(paragraph.getWidth());
31671             
31672         }
31673         
31674     },
31675     
31676     enter: function(e, el)
31677     {
31678         e.preventDefault();
31679         
31680         if(this.bgimage.length){
31681             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31682             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31683         }
31684     },
31685     
31686     leave: function(e, el)
31687     {
31688         e.preventDefault();
31689         
31690         if(this.bgimage.length){
31691             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31692             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31693         }
31694     }
31695     
31696 });
31697
31698  
31699
31700  /*
31701  * - LGPL
31702  *
31703  * Input
31704  * 
31705  */
31706
31707 /**
31708  * @class Roo.bootstrap.NumberField
31709  * @extends Roo.bootstrap.Input
31710  * Bootstrap NumberField class
31711  * 
31712  * 
31713  * 
31714  * 
31715  * @constructor
31716  * Create a new NumberField
31717  * @param {Object} config The config object
31718  */
31719
31720 Roo.bootstrap.NumberField = function(config){
31721     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31722 };
31723
31724 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31725     
31726     /**
31727      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31728      */
31729     allowDecimals : true,
31730     /**
31731      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31732      */
31733     decimalSeparator : ".",
31734     /**
31735      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31736      */
31737     decimalPrecision : 2,
31738     /**
31739      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31740      */
31741     allowNegative : true,
31742     /**
31743      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31744      */
31745     minValue : Number.NEGATIVE_INFINITY,
31746     /**
31747      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31748      */
31749     maxValue : Number.MAX_VALUE,
31750     /**
31751      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31752      */
31753     minText : "The minimum value for this field is {0}",
31754     /**
31755      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31756      */
31757     maxText : "The maximum value for this field is {0}",
31758     /**
31759      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
31760      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31761      */
31762     nanText : "{0} is not a valid number",
31763     /**
31764      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31765      */
31766     castInt : true,
31767
31768     // private
31769     initEvents : function()
31770     {   
31771         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31772         
31773         var allowed = "0123456789";
31774         
31775         if(this.allowDecimals){
31776             allowed += this.decimalSeparator;
31777         }
31778         
31779         if(this.allowNegative){
31780             allowed += "-";
31781         }
31782         
31783         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31784         
31785         var keyPress = function(e){
31786             
31787             var k = e.getKey();
31788             
31789             var c = e.getCharCode();
31790             
31791             if(
31792                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31793                     allowed.indexOf(String.fromCharCode(c)) === -1
31794             ){
31795                 e.stopEvent();
31796                 return;
31797             }
31798             
31799             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31800                 return;
31801             }
31802             
31803             if(allowed.indexOf(String.fromCharCode(c)) === -1){
31804                 e.stopEvent();
31805             }
31806         };
31807         
31808         this.el.on("keypress", keyPress, this);
31809     },
31810     
31811     validateValue : function(value)
31812     {
31813         
31814         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31815             return false;
31816         }
31817         
31818         var num = this.parseValue(value);
31819         
31820         if(isNaN(num)){
31821             this.markInvalid(String.format(this.nanText, value));
31822             return false;
31823         }
31824         
31825         if(num < this.minValue){
31826             this.markInvalid(String.format(this.minText, this.minValue));
31827             return false;
31828         }
31829         
31830         if(num > this.maxValue){
31831             this.markInvalid(String.format(this.maxText, this.maxValue));
31832             return false;
31833         }
31834         
31835         return true;
31836     },
31837
31838     getValue : function()
31839     {
31840         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31841     },
31842
31843     parseValue : function(value)
31844     {
31845         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31846         return isNaN(value) ? '' : value;
31847     },
31848
31849     fixPrecision : function(value)
31850     {
31851         var nan = isNaN(value);
31852         
31853         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31854             return nan ? '' : value;
31855         }
31856         return parseFloat(value).toFixed(this.decimalPrecision);
31857     },
31858
31859     setValue : function(v)
31860     {
31861         v = this.fixPrecision(v);
31862         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31863     },
31864
31865     decimalPrecisionFcn : function(v)
31866     {
31867         return Math.floor(v);
31868     },
31869
31870     beforeBlur : function()
31871     {
31872         if(!this.castInt){
31873             return;
31874         }
31875         
31876         var v = this.parseValue(this.getRawValue());
31877         if(v){
31878             this.setValue(v);
31879         }
31880     }
31881     
31882 });
31883
31884  
31885
31886 /*
31887 * Licence: LGPL
31888 */
31889
31890 /**
31891  * @class Roo.bootstrap.DocumentSlider
31892  * @extends Roo.bootstrap.Component
31893  * Bootstrap DocumentSlider class
31894  * 
31895  * @constructor
31896  * Create a new DocumentViewer
31897  * @param {Object} config The config object
31898  */
31899
31900 Roo.bootstrap.DocumentSlider = function(config){
31901     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31902     
31903     this.files = [];
31904     
31905     this.addEvents({
31906         /**
31907          * @event initial
31908          * Fire after initEvent
31909          * @param {Roo.bootstrap.DocumentSlider} this
31910          */
31911         "initial" : true,
31912         /**
31913          * @event update
31914          * Fire after update
31915          * @param {Roo.bootstrap.DocumentSlider} this
31916          */
31917         "update" : true,
31918         /**
31919          * @event click
31920          * Fire after click
31921          * @param {Roo.bootstrap.DocumentSlider} this
31922          */
31923         "click" : true
31924     });
31925 };
31926
31927 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
31928     
31929     files : false,
31930     
31931     indicator : 0,
31932     
31933     getAutoCreate : function()
31934     {
31935         var cfg = {
31936             tag : 'div',
31937             cls : 'roo-document-slider',
31938             cn : [
31939                 {
31940                     tag : 'div',
31941                     cls : 'roo-document-slider-header',
31942                     cn : [
31943                         {
31944                             tag : 'div',
31945                             cls : 'roo-document-slider-header-title'
31946                         }
31947                     ]
31948                 },
31949                 {
31950                     tag : 'div',
31951                     cls : 'roo-document-slider-body',
31952                     cn : [
31953                         {
31954                             tag : 'div',
31955                             cls : 'roo-document-slider-prev',
31956                             cn : [
31957                                 {
31958                                     tag : 'i',
31959                                     cls : 'fa fa-chevron-left'
31960                                 }
31961                             ]
31962                         },
31963                         {
31964                             tag : 'div',
31965                             cls : 'roo-document-slider-thumb',
31966                             cn : [
31967                                 {
31968                                     tag : 'img',
31969                                     cls : 'roo-document-slider-image'
31970                                 }
31971                             ]
31972                         },
31973                         {
31974                             tag : 'div',
31975                             cls : 'roo-document-slider-next',
31976                             cn : [
31977                                 {
31978                                     tag : 'i',
31979                                     cls : 'fa fa-chevron-right'
31980                                 }
31981                             ]
31982                         }
31983                     ]
31984                 }
31985             ]
31986         };
31987         
31988         return cfg;
31989     },
31990     
31991     initEvents : function()
31992     {
31993         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31994         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31995         
31996         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31997         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31998         
31999         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32000         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32001         
32002         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32003         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32004         
32005         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32006         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32007         
32008         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32009         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32010         
32011         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32012         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32013         
32014         this.thumbEl.on('click', this.onClick, this);
32015         
32016         this.prevIndicator.on('click', this.prev, this);
32017         
32018         this.nextIndicator.on('click', this.next, this);
32019         
32020     },
32021     
32022     initial : function()
32023     {
32024         if(this.files.length){
32025             this.indicator = 1;
32026             this.update()
32027         }
32028         
32029         this.fireEvent('initial', this);
32030     },
32031     
32032     update : function()
32033     {
32034         this.imageEl.attr('src', this.files[this.indicator - 1]);
32035         
32036         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32037         
32038         this.prevIndicator.show();
32039         
32040         if(this.indicator == 1){
32041             this.prevIndicator.hide();
32042         }
32043         
32044         this.nextIndicator.show();
32045         
32046         if(this.indicator == this.files.length){
32047             this.nextIndicator.hide();
32048         }
32049         
32050         this.thumbEl.scrollTo('top');
32051         
32052         this.fireEvent('update', this);
32053     },
32054     
32055     onClick : function(e)
32056     {
32057         e.preventDefault();
32058         
32059         this.fireEvent('click', this);
32060     },
32061     
32062     prev : function(e)
32063     {
32064         e.preventDefault();
32065         
32066         this.indicator = Math.max(1, this.indicator - 1);
32067         
32068         this.update();
32069     },
32070     
32071     next : function(e)
32072     {
32073         e.preventDefault();
32074         
32075         this.indicator = Math.min(this.files.length, this.indicator + 1);
32076         
32077         this.update();
32078     }
32079 });
32080 /*
32081  * Based on:
32082  * Ext JS Library 1.1.1
32083  * Copyright(c) 2006-2007, Ext JS, LLC.
32084  *
32085  * Originally Released Under LGPL - original licence link has changed is not relivant.
32086  *
32087  * Fork - LGPL
32088  * <script type="text/javascript">
32089  */
32090
32091
32092 /**
32093  * @class Roo.bootstrap.SplitBar
32094  * @extends Roo.util.Observable
32095  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32096  * <br><br>
32097  * Usage:
32098  * <pre><code>
32099 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32100                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32101 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32102 split.minSize = 100;
32103 split.maxSize = 600;
32104 split.animate = true;
32105 split.on('moved', splitterMoved);
32106 </code></pre>
32107  * @constructor
32108  * Create a new SplitBar
32109  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32110  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32111  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32112  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32113                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32114                         position of the SplitBar).
32115  */
32116 Roo.bootstrap.SplitBar = function(cfg){
32117     
32118     /** @private */
32119     
32120     //{
32121     //  dragElement : elm
32122     //  resizingElement: el,
32123         // optional..
32124     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32125     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32126         // existingProxy ???
32127     //}
32128     
32129     this.el = Roo.get(cfg.dragElement, true);
32130     this.el.dom.unselectable = "on";
32131     /** @private */
32132     this.resizingEl = Roo.get(cfg.resizingElement, true);
32133
32134     /**
32135      * @private
32136      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32137      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32138      * @type Number
32139      */
32140     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32141     
32142     /**
32143      * The minimum size of the resizing element. (Defaults to 0)
32144      * @type Number
32145      */
32146     this.minSize = 0;
32147     
32148     /**
32149      * The maximum size of the resizing element. (Defaults to 2000)
32150      * @type Number
32151      */
32152     this.maxSize = 2000;
32153     
32154     /**
32155      * Whether to animate the transition to the new size
32156      * @type Boolean
32157      */
32158     this.animate = false;
32159     
32160     /**
32161      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32162      * @type Boolean
32163      */
32164     this.useShim = false;
32165     
32166     /** @private */
32167     this.shim = null;
32168     
32169     if(!cfg.existingProxy){
32170         /** @private */
32171         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32172     }else{
32173         this.proxy = Roo.get(cfg.existingProxy).dom;
32174     }
32175     /** @private */
32176     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32177     
32178     /** @private */
32179     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32180     
32181     /** @private */
32182     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32183     
32184     /** @private */
32185     this.dragSpecs = {};
32186     
32187     /**
32188      * @private The adapter to use to positon and resize elements
32189      */
32190     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32191     this.adapter.init(this);
32192     
32193     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32194         /** @private */
32195         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32196         this.el.addClass("roo-splitbar-h");
32197     }else{
32198         /** @private */
32199         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32200         this.el.addClass("roo-splitbar-v");
32201     }
32202     
32203     this.addEvents({
32204         /**
32205          * @event resize
32206          * Fires when the splitter is moved (alias for {@link #event-moved})
32207          * @param {Roo.bootstrap.SplitBar} this
32208          * @param {Number} newSize the new width or height
32209          */
32210         "resize" : true,
32211         /**
32212          * @event moved
32213          * Fires when the splitter is moved
32214          * @param {Roo.bootstrap.SplitBar} this
32215          * @param {Number} newSize the new width or height
32216          */
32217         "moved" : true,
32218         /**
32219          * @event beforeresize
32220          * Fires before the splitter is dragged
32221          * @param {Roo.bootstrap.SplitBar} this
32222          */
32223         "beforeresize" : true,
32224
32225         "beforeapply" : true
32226     });
32227
32228     Roo.util.Observable.call(this);
32229 };
32230
32231 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32232     onStartProxyDrag : function(x, y){
32233         this.fireEvent("beforeresize", this);
32234         if(!this.overlay){
32235             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32236             o.unselectable();
32237             o.enableDisplayMode("block");
32238             // all splitbars share the same overlay
32239             Roo.bootstrap.SplitBar.prototype.overlay = o;
32240         }
32241         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32242         this.overlay.show();
32243         Roo.get(this.proxy).setDisplayed("block");
32244         var size = this.adapter.getElementSize(this);
32245         this.activeMinSize = this.getMinimumSize();;
32246         this.activeMaxSize = this.getMaximumSize();;
32247         var c1 = size - this.activeMinSize;
32248         var c2 = Math.max(this.activeMaxSize - size, 0);
32249         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32250             this.dd.resetConstraints();
32251             this.dd.setXConstraint(
32252                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32253                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32254             );
32255             this.dd.setYConstraint(0, 0);
32256         }else{
32257             this.dd.resetConstraints();
32258             this.dd.setXConstraint(0, 0);
32259             this.dd.setYConstraint(
32260                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32261                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32262             );
32263          }
32264         this.dragSpecs.startSize = size;
32265         this.dragSpecs.startPoint = [x, y];
32266         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32267     },
32268     
32269     /** 
32270      * @private Called after the drag operation by the DDProxy
32271      */
32272     onEndProxyDrag : function(e){
32273         Roo.get(this.proxy).setDisplayed(false);
32274         var endPoint = Roo.lib.Event.getXY(e);
32275         if(this.overlay){
32276             this.overlay.hide();
32277         }
32278         var newSize;
32279         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32280             newSize = this.dragSpecs.startSize + 
32281                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32282                     endPoint[0] - this.dragSpecs.startPoint[0] :
32283                     this.dragSpecs.startPoint[0] - endPoint[0]
32284                 );
32285         }else{
32286             newSize = this.dragSpecs.startSize + 
32287                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32288                     endPoint[1] - this.dragSpecs.startPoint[1] :
32289                     this.dragSpecs.startPoint[1] - endPoint[1]
32290                 );
32291         }
32292         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32293         if(newSize != this.dragSpecs.startSize){
32294             if(this.fireEvent('beforeapply', this, newSize) !== false){
32295                 this.adapter.setElementSize(this, newSize);
32296                 this.fireEvent("moved", this, newSize);
32297                 this.fireEvent("resize", this, newSize);
32298             }
32299         }
32300     },
32301     
32302     /**
32303      * Get the adapter this SplitBar uses
32304      * @return The adapter object
32305      */
32306     getAdapter : function(){
32307         return this.adapter;
32308     },
32309     
32310     /**
32311      * Set the adapter this SplitBar uses
32312      * @param {Object} adapter A SplitBar adapter object
32313      */
32314     setAdapter : function(adapter){
32315         this.adapter = adapter;
32316         this.adapter.init(this);
32317     },
32318     
32319     /**
32320      * Gets the minimum size for the resizing element
32321      * @return {Number} The minimum size
32322      */
32323     getMinimumSize : function(){
32324         return this.minSize;
32325     },
32326     
32327     /**
32328      * Sets the minimum size for the resizing element
32329      * @param {Number} minSize The minimum size
32330      */
32331     setMinimumSize : function(minSize){
32332         this.minSize = minSize;
32333     },
32334     
32335     /**
32336      * Gets the maximum size for the resizing element
32337      * @return {Number} The maximum size
32338      */
32339     getMaximumSize : function(){
32340         return this.maxSize;
32341     },
32342     
32343     /**
32344      * Sets the maximum size for the resizing element
32345      * @param {Number} maxSize The maximum size
32346      */
32347     setMaximumSize : function(maxSize){
32348         this.maxSize = maxSize;
32349     },
32350     
32351     /**
32352      * Sets the initialize size for the resizing element
32353      * @param {Number} size The initial size
32354      */
32355     setCurrentSize : function(size){
32356         var oldAnimate = this.animate;
32357         this.animate = false;
32358         this.adapter.setElementSize(this, size);
32359         this.animate = oldAnimate;
32360     },
32361     
32362     /**
32363      * Destroy this splitbar. 
32364      * @param {Boolean} removeEl True to remove the element
32365      */
32366     destroy : function(removeEl){
32367         if(this.shim){
32368             this.shim.remove();
32369         }
32370         this.dd.unreg();
32371         this.proxy.parentNode.removeChild(this.proxy);
32372         if(removeEl){
32373             this.el.remove();
32374         }
32375     }
32376 });
32377
32378 /**
32379  * @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.
32380  */
32381 Roo.bootstrap.SplitBar.createProxy = function(dir){
32382     var proxy = new Roo.Element(document.createElement("div"));
32383     proxy.unselectable();
32384     var cls = 'roo-splitbar-proxy';
32385     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32386     document.body.appendChild(proxy.dom);
32387     return proxy.dom;
32388 };
32389
32390 /** 
32391  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32392  * Default Adapter. It assumes the splitter and resizing element are not positioned
32393  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32394  */
32395 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32396 };
32397
32398 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32399     // do nothing for now
32400     init : function(s){
32401     
32402     },
32403     /**
32404      * Called before drag operations to get the current size of the resizing element. 
32405      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32406      */
32407      getElementSize : function(s){
32408         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32409             return s.resizingEl.getWidth();
32410         }else{
32411             return s.resizingEl.getHeight();
32412         }
32413     },
32414     
32415     /**
32416      * Called after drag operations to set the size of the resizing element.
32417      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32418      * @param {Number} newSize The new size to set
32419      * @param {Function} onComplete A function to be invoked when resizing is complete
32420      */
32421     setElementSize : function(s, newSize, onComplete){
32422         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32423             if(!s.animate){
32424                 s.resizingEl.setWidth(newSize);
32425                 if(onComplete){
32426                     onComplete(s, newSize);
32427                 }
32428             }else{
32429                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32430             }
32431         }else{
32432             
32433             if(!s.animate){
32434                 s.resizingEl.setHeight(newSize);
32435                 if(onComplete){
32436                     onComplete(s, newSize);
32437                 }
32438             }else{
32439                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32440             }
32441         }
32442     }
32443 };
32444
32445 /** 
32446  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32447  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32448  * Adapter that  moves the splitter element to align with the resized sizing element. 
32449  * Used with an absolute positioned SplitBar.
32450  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32451  * document.body, make sure you assign an id to the body element.
32452  */
32453 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32454     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32455     this.container = Roo.get(container);
32456 };
32457
32458 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32459     init : function(s){
32460         this.basic.init(s);
32461     },
32462     
32463     getElementSize : function(s){
32464         return this.basic.getElementSize(s);
32465     },
32466     
32467     setElementSize : function(s, newSize, onComplete){
32468         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32469     },
32470     
32471     moveSplitter : function(s){
32472         var yes = Roo.bootstrap.SplitBar;
32473         switch(s.placement){
32474             case yes.LEFT:
32475                 s.el.setX(s.resizingEl.getRight());
32476                 break;
32477             case yes.RIGHT:
32478                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32479                 break;
32480             case yes.TOP:
32481                 s.el.setY(s.resizingEl.getBottom());
32482                 break;
32483             case yes.BOTTOM:
32484                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32485                 break;
32486         }
32487     }
32488 };
32489
32490 /**
32491  * Orientation constant - Create a vertical SplitBar
32492  * @static
32493  * @type Number
32494  */
32495 Roo.bootstrap.SplitBar.VERTICAL = 1;
32496
32497 /**
32498  * Orientation constant - Create a horizontal SplitBar
32499  * @static
32500  * @type Number
32501  */
32502 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32503
32504 /**
32505  * Placement constant - The resizing element is to the left of the splitter element
32506  * @static
32507  * @type Number
32508  */
32509 Roo.bootstrap.SplitBar.LEFT = 1;
32510
32511 /**
32512  * Placement constant - The resizing element is to the right of the splitter element
32513  * @static
32514  * @type Number
32515  */
32516 Roo.bootstrap.SplitBar.RIGHT = 2;
32517
32518 /**
32519  * Placement constant - The resizing element is positioned above the splitter element
32520  * @static
32521  * @type Number
32522  */
32523 Roo.bootstrap.SplitBar.TOP = 3;
32524
32525 /**
32526  * Placement constant - The resizing element is positioned under splitter element
32527  * @static
32528  * @type Number
32529  */
32530 Roo.bootstrap.SplitBar.BOTTOM = 4;
32531 Roo.namespace("Roo.bootstrap.layout");/*
32532  * Based on:
32533  * Ext JS Library 1.1.1
32534  * Copyright(c) 2006-2007, Ext JS, LLC.
32535  *
32536  * Originally Released Under LGPL - original licence link has changed is not relivant.
32537  *
32538  * Fork - LGPL
32539  * <script type="text/javascript">
32540  */
32541
32542 /**
32543  * @class Roo.bootstrap.layout.Manager
32544  * @extends Roo.bootstrap.Component
32545  * Base class for layout managers.
32546  */
32547 Roo.bootstrap.layout.Manager = function(config)
32548 {
32549     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32550
32551
32552
32553
32554
32555     /** false to disable window resize monitoring @type Boolean */
32556     this.monitorWindowResize = true;
32557     this.regions = {};
32558     this.addEvents({
32559         /**
32560          * @event layout
32561          * Fires when a layout is performed.
32562          * @param {Roo.LayoutManager} this
32563          */
32564         "layout" : true,
32565         /**
32566          * @event regionresized
32567          * Fires when the user resizes a region.
32568          * @param {Roo.LayoutRegion} region The resized region
32569          * @param {Number} newSize The new size (width for east/west, height for north/south)
32570          */
32571         "regionresized" : true,
32572         /**
32573          * @event regioncollapsed
32574          * Fires when a region is collapsed.
32575          * @param {Roo.LayoutRegion} region The collapsed region
32576          */
32577         "regioncollapsed" : true,
32578         /**
32579          * @event regionexpanded
32580          * Fires when a region is expanded.
32581          * @param {Roo.LayoutRegion} region The expanded region
32582          */
32583         "regionexpanded" : true
32584     });
32585     this.updating = false;
32586
32587     if (config.el) {
32588         this.el = Roo.get(config.el);
32589         this.initEvents();
32590     }
32591
32592 };
32593
32594 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32595
32596
32597     regions : null,
32598
32599     monitorWindowResize : true,
32600
32601
32602     updating : false,
32603
32604
32605     onRender : function(ct, position)
32606     {
32607         if(!this.el){
32608             this.el = Roo.get(ct);
32609             this.initEvents();
32610         }
32611         //this.fireEvent('render',this);
32612     },
32613
32614
32615     initEvents: function()
32616     {
32617
32618
32619         // ie scrollbar fix
32620         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32621             document.body.scroll = "no";
32622         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32623             this.el.position('relative');
32624         }
32625         this.id = this.el.id;
32626         this.el.addClass("roo-layout-container");
32627         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32628         if(this.el.dom != document.body ) {
32629             this.el.on('resize', this.layout,this);
32630             this.el.on('show', this.layout,this);
32631         }
32632
32633     },
32634
32635     /**
32636      * Returns true if this layout is currently being updated
32637      * @return {Boolean}
32638      */
32639     isUpdating : function(){
32640         return this.updating;
32641     },
32642
32643     /**
32644      * Suspend the LayoutManager from doing auto-layouts while
32645      * making multiple add or remove calls
32646      */
32647     beginUpdate : function(){
32648         this.updating = true;
32649     },
32650
32651     /**
32652      * Restore auto-layouts and optionally disable the manager from performing a layout
32653      * @param {Boolean} noLayout true to disable a layout update
32654      */
32655     endUpdate : function(noLayout){
32656         this.updating = false;
32657         if(!noLayout){
32658             this.layout();
32659         }
32660     },
32661
32662     layout: function(){
32663         // abstract...
32664     },
32665
32666     onRegionResized : function(region, newSize){
32667         this.fireEvent("regionresized", region, newSize);
32668         this.layout();
32669     },
32670
32671     onRegionCollapsed : function(region){
32672         this.fireEvent("regioncollapsed", region);
32673     },
32674
32675     onRegionExpanded : function(region){
32676         this.fireEvent("regionexpanded", region);
32677     },
32678
32679     /**
32680      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32681      * performs box-model adjustments.
32682      * @return {Object} The size as an object {width: (the width), height: (the height)}
32683      */
32684     getViewSize : function()
32685     {
32686         var size;
32687         if(this.el.dom != document.body){
32688             size = this.el.getSize();
32689         }else{
32690             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32691         }
32692         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32693         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32694         return size;
32695     },
32696
32697     /**
32698      * Returns the Element this layout is bound to.
32699      * @return {Roo.Element}
32700      */
32701     getEl : function(){
32702         return this.el;
32703     },
32704
32705     /**
32706      * Returns the specified region.
32707      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32708      * @return {Roo.LayoutRegion}
32709      */
32710     getRegion : function(target){
32711         return this.regions[target.toLowerCase()];
32712     },
32713
32714     onWindowResize : function(){
32715         if(this.monitorWindowResize){
32716             this.layout();
32717         }
32718     }
32719 });
32720 /*
32721  * Based on:
32722  * Ext JS Library 1.1.1
32723  * Copyright(c) 2006-2007, Ext JS, LLC.
32724  *
32725  * Originally Released Under LGPL - original licence link has changed is not relivant.
32726  *
32727  * Fork - LGPL
32728  * <script type="text/javascript">
32729  */
32730 /**
32731  * @class Roo.bootstrap.layout.Border
32732  * @extends Roo.bootstrap.layout.Manager
32733  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32734  * please see: examples/bootstrap/nested.html<br><br>
32735  
32736 <b>The container the layout is rendered into can be either the body element or any other element.
32737 If it is not the body element, the container needs to either be an absolute positioned element,
32738 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32739 the container size if it is not the body element.</b>
32740
32741 * @constructor
32742 * Create a new Border
32743 * @param {Object} config Configuration options
32744  */
32745 Roo.bootstrap.layout.Border = function(config){
32746     config = config || {};
32747     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32748     
32749     
32750     
32751     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32752         if(config[region]){
32753             config[region].region = region;
32754             this.addRegion(config[region]);
32755         }
32756     },this);
32757     
32758 };
32759
32760 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32761
32762 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32763     /**
32764      * Creates and adds a new region if it doesn't already exist.
32765      * @param {String} target The target region key (north, south, east, west or center).
32766      * @param {Object} config The regions config object
32767      * @return {BorderLayoutRegion} The new region
32768      */
32769     addRegion : function(config)
32770     {
32771         if(!this.regions[config.region]){
32772             var r = this.factory(config);
32773             this.bindRegion(r);
32774         }
32775         return this.regions[config.region];
32776     },
32777
32778     // private (kinda)
32779     bindRegion : function(r){
32780         this.regions[r.config.region] = r;
32781         
32782         r.on("visibilitychange",    this.layout, this);
32783         r.on("paneladded",          this.layout, this);
32784         r.on("panelremoved",        this.layout, this);
32785         r.on("invalidated",         this.layout, this);
32786         r.on("resized",             this.onRegionResized, this);
32787         r.on("collapsed",           this.onRegionCollapsed, this);
32788         r.on("expanded",            this.onRegionExpanded, this);
32789     },
32790
32791     /**
32792      * Performs a layout update.
32793      */
32794     layout : function()
32795     {
32796         if(this.updating) {
32797             return;
32798         }
32799         
32800         // render all the rebions if they have not been done alreayd?
32801         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32802             if(this.regions[region] && !this.regions[region].bodyEl){
32803                 this.regions[region].onRender(this.el)
32804             }
32805         },this);
32806         
32807         var size = this.getViewSize();
32808         var w = size.width;
32809         var h = size.height;
32810         var centerW = w;
32811         var centerH = h;
32812         var centerY = 0;
32813         var centerX = 0;
32814         //var x = 0, y = 0;
32815
32816         var rs = this.regions;
32817         var north = rs["north"];
32818         var south = rs["south"]; 
32819         var west = rs["west"];
32820         var east = rs["east"];
32821         var center = rs["center"];
32822         //if(this.hideOnLayout){ // not supported anymore
32823             //c.el.setStyle("display", "none");
32824         //}
32825         if(north && north.isVisible()){
32826             var b = north.getBox();
32827             var m = north.getMargins();
32828             b.width = w - (m.left+m.right);
32829             b.x = m.left;
32830             b.y = m.top;
32831             centerY = b.height + b.y + m.bottom;
32832             centerH -= centerY;
32833             north.updateBox(this.safeBox(b));
32834         }
32835         if(south && south.isVisible()){
32836             var b = south.getBox();
32837             var m = south.getMargins();
32838             b.width = w - (m.left+m.right);
32839             b.x = m.left;
32840             var totalHeight = (b.height + m.top + m.bottom);
32841             b.y = h - totalHeight + m.top;
32842             centerH -= totalHeight;
32843             south.updateBox(this.safeBox(b));
32844         }
32845         if(west && west.isVisible()){
32846             var b = west.getBox();
32847             var m = west.getMargins();
32848             b.height = centerH - (m.top+m.bottom);
32849             b.x = m.left;
32850             b.y = centerY + m.top;
32851             var totalWidth = (b.width + m.left + m.right);
32852             centerX += totalWidth;
32853             centerW -= totalWidth;
32854             west.updateBox(this.safeBox(b));
32855         }
32856         if(east && east.isVisible()){
32857             var b = east.getBox();
32858             var m = east.getMargins();
32859             b.height = centerH - (m.top+m.bottom);
32860             var totalWidth = (b.width + m.left + m.right);
32861             b.x = w - totalWidth + m.left;
32862             b.y = centerY + m.top;
32863             centerW -= totalWidth;
32864             east.updateBox(this.safeBox(b));
32865         }
32866         if(center){
32867             var m = center.getMargins();
32868             var centerBox = {
32869                 x: centerX + m.left,
32870                 y: centerY + m.top,
32871                 width: centerW - (m.left+m.right),
32872                 height: centerH - (m.top+m.bottom)
32873             };
32874             //if(this.hideOnLayout){
32875                 //center.el.setStyle("display", "block");
32876             //}
32877             center.updateBox(this.safeBox(centerBox));
32878         }
32879         this.el.repaint();
32880         this.fireEvent("layout", this);
32881     },
32882
32883     // private
32884     safeBox : function(box){
32885         box.width = Math.max(0, box.width);
32886         box.height = Math.max(0, box.height);
32887         return box;
32888     },
32889
32890     /**
32891      * Adds a ContentPanel (or subclass) to this layout.
32892      * @param {String} target The target region key (north, south, east, west or center).
32893      * @param {Roo.ContentPanel} panel The panel to add
32894      * @return {Roo.ContentPanel} The added panel
32895      */
32896     add : function(target, panel){
32897          
32898         target = target.toLowerCase();
32899         return this.regions[target].add(panel);
32900     },
32901
32902     /**
32903      * Remove a ContentPanel (or subclass) to this layout.
32904      * @param {String} target The target region key (north, south, east, west or center).
32905      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32906      * @return {Roo.ContentPanel} The removed panel
32907      */
32908     remove : function(target, panel){
32909         target = target.toLowerCase();
32910         return this.regions[target].remove(panel);
32911     },
32912
32913     /**
32914      * Searches all regions for a panel with the specified id
32915      * @param {String} panelId
32916      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32917      */
32918     findPanel : function(panelId){
32919         var rs = this.regions;
32920         for(var target in rs){
32921             if(typeof rs[target] != "function"){
32922                 var p = rs[target].getPanel(panelId);
32923                 if(p){
32924                     return p;
32925                 }
32926             }
32927         }
32928         return null;
32929     },
32930
32931     /**
32932      * Searches all regions for a panel with the specified id and activates (shows) it.
32933      * @param {String/ContentPanel} panelId The panels id or the panel itself
32934      * @return {Roo.ContentPanel} The shown panel or null
32935      */
32936     showPanel : function(panelId) {
32937       var rs = this.regions;
32938       for(var target in rs){
32939          var r = rs[target];
32940          if(typeof r != "function"){
32941             if(r.hasPanel(panelId)){
32942                return r.showPanel(panelId);
32943             }
32944          }
32945       }
32946       return null;
32947    },
32948
32949    /**
32950      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32951      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32952      */
32953    /*
32954     restoreState : function(provider){
32955         if(!provider){
32956             provider = Roo.state.Manager;
32957         }
32958         var sm = new Roo.LayoutStateManager();
32959         sm.init(this, provider);
32960     },
32961 */
32962  
32963  
32964     /**
32965      * Adds a xtype elements to the layout.
32966      * <pre><code>
32967
32968 layout.addxtype({
32969        xtype : 'ContentPanel',
32970        region: 'west',
32971        items: [ .... ]
32972    }
32973 );
32974
32975 layout.addxtype({
32976         xtype : 'NestedLayoutPanel',
32977         region: 'west',
32978         layout: {
32979            center: { },
32980            west: { }   
32981         },
32982         items : [ ... list of content panels or nested layout panels.. ]
32983    }
32984 );
32985 </code></pre>
32986      * @param {Object} cfg Xtype definition of item to add.
32987      */
32988     addxtype : function(cfg)
32989     {
32990         // basically accepts a pannel...
32991         // can accept a layout region..!?!?
32992         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32993         
32994         
32995         // theory?  children can only be panels??
32996         
32997         //if (!cfg.xtype.match(/Panel$/)) {
32998         //    return false;
32999         //}
33000         var ret = false;
33001         
33002         if (typeof(cfg.region) == 'undefined') {
33003             Roo.log("Failed to add Panel, region was not set");
33004             Roo.log(cfg);
33005             return false;
33006         }
33007         var region = cfg.region;
33008         delete cfg.region;
33009         
33010           
33011         var xitems = [];
33012         if (cfg.items) {
33013             xitems = cfg.items;
33014             delete cfg.items;
33015         }
33016         var nb = false;
33017         
33018         switch(cfg.xtype) 
33019         {
33020             case 'Content':  // ContentPanel (el, cfg)
33021             case 'Scroll':  // ContentPanel (el, cfg)
33022             case 'View': 
33023                 cfg.autoCreate = true;
33024                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33025                 //} else {
33026                 //    var el = this.el.createChild();
33027                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33028                 //}
33029                 
33030                 this.add(region, ret);
33031                 break;
33032             
33033             /*
33034             case 'TreePanel': // our new panel!
33035                 cfg.el = this.el.createChild();
33036                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33037                 this.add(region, ret);
33038                 break;
33039             */
33040             
33041             case 'Nest': 
33042                 // create a new Layout (which is  a Border Layout...
33043                 
33044                 var clayout = cfg.layout;
33045                 clayout.el  = this.el.createChild();
33046                 clayout.items   = clayout.items  || [];
33047                 
33048                 delete cfg.layout;
33049                 
33050                 // replace this exitems with the clayout ones..
33051                 xitems = clayout.items;
33052                  
33053                 // force background off if it's in center...
33054                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33055                     cfg.background = false;
33056                 }
33057                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33058                 
33059                 
33060                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33061                 //console.log('adding nested layout panel '  + cfg.toSource());
33062                 this.add(region, ret);
33063                 nb = {}; /// find first...
33064                 break;
33065             
33066             case 'Grid':
33067                 
33068                 // needs grid and region
33069                 
33070                 //var el = this.getRegion(region).el.createChild();
33071                 /*
33072                  *var el = this.el.createChild();
33073                 // create the grid first...
33074                 cfg.grid.container = el;
33075                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33076                 */
33077                 
33078                 if (region == 'center' && this.active ) {
33079                     cfg.background = false;
33080                 }
33081                 
33082                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33083                 
33084                 this.add(region, ret);
33085                 /*
33086                 if (cfg.background) {
33087                     // render grid on panel activation (if panel background)
33088                     ret.on('activate', function(gp) {
33089                         if (!gp.grid.rendered) {
33090                     //        gp.grid.render(el);
33091                         }
33092                     });
33093                 } else {
33094                   //  cfg.grid.render(el);
33095                 }
33096                 */
33097                 break;
33098            
33099            
33100             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33101                 // it was the old xcomponent building that caused this before.
33102                 // espeically if border is the top element in the tree.
33103                 ret = this;
33104                 break; 
33105                 
33106                     
33107                 
33108                 
33109                 
33110             default:
33111                 /*
33112                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33113                     
33114                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33115                     this.add(region, ret);
33116                 } else {
33117                 */
33118                     Roo.log(cfg);
33119                     throw "Can not add '" + cfg.xtype + "' to Border";
33120                     return null;
33121              
33122                                 
33123              
33124         }
33125         this.beginUpdate();
33126         // add children..
33127         var region = '';
33128         var abn = {};
33129         Roo.each(xitems, function(i)  {
33130             region = nb && i.region ? i.region : false;
33131             
33132             var add = ret.addxtype(i);
33133            
33134             if (region) {
33135                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33136                 if (!i.background) {
33137                     abn[region] = nb[region] ;
33138                 }
33139             }
33140             
33141         });
33142         this.endUpdate();
33143
33144         // make the last non-background panel active..
33145         //if (nb) { Roo.log(abn); }
33146         if (nb) {
33147             
33148             for(var r in abn) {
33149                 region = this.getRegion(r);
33150                 if (region) {
33151                     // tried using nb[r], but it does not work..
33152                      
33153                     region.showPanel(abn[r]);
33154                    
33155                 }
33156             }
33157         }
33158         return ret;
33159         
33160     },
33161     
33162     
33163 // private
33164     factory : function(cfg)
33165     {
33166         
33167         var validRegions = Roo.bootstrap.layout.Border.regions;
33168
33169         var target = cfg.region;
33170         cfg.mgr = this;
33171         
33172         var r = Roo.bootstrap.layout;
33173         Roo.log(target);
33174         switch(target){
33175             case "north":
33176                 return new r.North(cfg);
33177             case "south":
33178                 return new r.South(cfg);
33179             case "east":
33180                 return new r.East(cfg);
33181             case "west":
33182                 return new r.West(cfg);
33183             case "center":
33184                 return new r.Center(cfg);
33185         }
33186         throw 'Layout region "'+target+'" not supported.';
33187     }
33188     
33189     
33190 });
33191  /*
33192  * Based on:
33193  * Ext JS Library 1.1.1
33194  * Copyright(c) 2006-2007, Ext JS, LLC.
33195  *
33196  * Originally Released Under LGPL - original licence link has changed is not relivant.
33197  *
33198  * Fork - LGPL
33199  * <script type="text/javascript">
33200  */
33201  
33202 /**
33203  * @class Roo.bootstrap.layout.Basic
33204  * @extends Roo.util.Observable
33205  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33206  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33207  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33208  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33209  * @cfg {string}   region  the region that it inhabits..
33210  * @cfg {bool}   skipConfig skip config?
33211  * 
33212
33213  */
33214 Roo.bootstrap.layout.Basic = function(config){
33215     
33216     this.mgr = config.mgr;
33217     
33218     this.position = config.region;
33219     
33220     var skipConfig = config.skipConfig;
33221     
33222     this.events = {
33223         /**
33224          * @scope Roo.BasicLayoutRegion
33225          */
33226         
33227         /**
33228          * @event beforeremove
33229          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33230          * @param {Roo.LayoutRegion} this
33231          * @param {Roo.ContentPanel} panel The panel
33232          * @param {Object} e The cancel event object
33233          */
33234         "beforeremove" : true,
33235         /**
33236          * @event invalidated
33237          * Fires when the layout for this region is changed.
33238          * @param {Roo.LayoutRegion} this
33239          */
33240         "invalidated" : true,
33241         /**
33242          * @event visibilitychange
33243          * Fires when this region is shown or hidden 
33244          * @param {Roo.LayoutRegion} this
33245          * @param {Boolean} visibility true or false
33246          */
33247         "visibilitychange" : true,
33248         /**
33249          * @event paneladded
33250          * Fires when a panel is added. 
33251          * @param {Roo.LayoutRegion} this
33252          * @param {Roo.ContentPanel} panel The panel
33253          */
33254         "paneladded" : true,
33255         /**
33256          * @event panelremoved
33257          * Fires when a panel is removed. 
33258          * @param {Roo.LayoutRegion} this
33259          * @param {Roo.ContentPanel} panel The panel
33260          */
33261         "panelremoved" : true,
33262         /**
33263          * @event beforecollapse
33264          * Fires when this region before collapse.
33265          * @param {Roo.LayoutRegion} this
33266          */
33267         "beforecollapse" : true,
33268         /**
33269          * @event collapsed
33270          * Fires when this region is collapsed.
33271          * @param {Roo.LayoutRegion} this
33272          */
33273         "collapsed" : true,
33274         /**
33275          * @event expanded
33276          * Fires when this region is expanded.
33277          * @param {Roo.LayoutRegion} this
33278          */
33279         "expanded" : true,
33280         /**
33281          * @event slideshow
33282          * Fires when this region is slid into view.
33283          * @param {Roo.LayoutRegion} this
33284          */
33285         "slideshow" : true,
33286         /**
33287          * @event slidehide
33288          * Fires when this region slides out of view. 
33289          * @param {Roo.LayoutRegion} this
33290          */
33291         "slidehide" : true,
33292         /**
33293          * @event panelactivated
33294          * Fires when a panel is activated. 
33295          * @param {Roo.LayoutRegion} this
33296          * @param {Roo.ContentPanel} panel The activated panel
33297          */
33298         "panelactivated" : true,
33299         /**
33300          * @event resized
33301          * Fires when the user resizes this region. 
33302          * @param {Roo.LayoutRegion} this
33303          * @param {Number} newSize The new size (width for east/west, height for north/south)
33304          */
33305         "resized" : true
33306     };
33307     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33308     this.panels = new Roo.util.MixedCollection();
33309     this.panels.getKey = this.getPanelId.createDelegate(this);
33310     this.box = null;
33311     this.activePanel = null;
33312     // ensure listeners are added...
33313     
33314     if (config.listeners || config.events) {
33315         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33316             listeners : config.listeners || {},
33317             events : config.events || {}
33318         });
33319     }
33320     
33321     if(skipConfig !== true){
33322         this.applyConfig(config);
33323     }
33324 };
33325
33326 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33327 {
33328     getPanelId : function(p){
33329         return p.getId();
33330     },
33331     
33332     applyConfig : function(config){
33333         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33334         this.config = config;
33335         
33336     },
33337     
33338     /**
33339      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33340      * the width, for horizontal (north, south) the height.
33341      * @param {Number} newSize The new width or height
33342      */
33343     resizeTo : function(newSize){
33344         var el = this.el ? this.el :
33345                  (this.activePanel ? this.activePanel.getEl() : null);
33346         if(el){
33347             switch(this.position){
33348                 case "east":
33349                 case "west":
33350                     el.setWidth(newSize);
33351                     this.fireEvent("resized", this, newSize);
33352                 break;
33353                 case "north":
33354                 case "south":
33355                     el.setHeight(newSize);
33356                     this.fireEvent("resized", this, newSize);
33357                 break;                
33358             }
33359         }
33360     },
33361     
33362     getBox : function(){
33363         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33364     },
33365     
33366     getMargins : function(){
33367         return this.margins;
33368     },
33369     
33370     updateBox : function(box){
33371         this.box = box;
33372         var el = this.activePanel.getEl();
33373         el.dom.style.left = box.x + "px";
33374         el.dom.style.top = box.y + "px";
33375         this.activePanel.setSize(box.width, box.height);
33376     },
33377     
33378     /**
33379      * Returns the container element for this region.
33380      * @return {Roo.Element}
33381      */
33382     getEl : function(){
33383         return this.activePanel;
33384     },
33385     
33386     /**
33387      * Returns true if this region is currently visible.
33388      * @return {Boolean}
33389      */
33390     isVisible : function(){
33391         return this.activePanel ? true : false;
33392     },
33393     
33394     setActivePanel : function(panel){
33395         panel = this.getPanel(panel);
33396         if(this.activePanel && this.activePanel != panel){
33397             this.activePanel.setActiveState(false);
33398             this.activePanel.getEl().setLeftTop(-10000,-10000);
33399         }
33400         this.activePanel = panel;
33401         panel.setActiveState(true);
33402         if(this.box){
33403             panel.setSize(this.box.width, this.box.height);
33404         }
33405         this.fireEvent("panelactivated", this, panel);
33406         this.fireEvent("invalidated");
33407     },
33408     
33409     /**
33410      * Show the specified panel.
33411      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33412      * @return {Roo.ContentPanel} The shown panel or null
33413      */
33414     showPanel : function(panel){
33415         panel = this.getPanel(panel);
33416         if(panel){
33417             this.setActivePanel(panel);
33418         }
33419         return panel;
33420     },
33421     
33422     /**
33423      * Get the active panel for this region.
33424      * @return {Roo.ContentPanel} The active panel or null
33425      */
33426     getActivePanel : function(){
33427         return this.activePanel;
33428     },
33429     
33430     /**
33431      * Add the passed ContentPanel(s)
33432      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33433      * @return {Roo.ContentPanel} The panel added (if only one was added)
33434      */
33435     add : function(panel){
33436         if(arguments.length > 1){
33437             for(var i = 0, len = arguments.length; i < len; i++) {
33438                 this.add(arguments[i]);
33439             }
33440             return null;
33441         }
33442         if(this.hasPanel(panel)){
33443             this.showPanel(panel);
33444             return panel;
33445         }
33446         var el = panel.getEl();
33447         if(el.dom.parentNode != this.mgr.el.dom){
33448             this.mgr.el.dom.appendChild(el.dom);
33449         }
33450         if(panel.setRegion){
33451             panel.setRegion(this);
33452         }
33453         this.panels.add(panel);
33454         el.setStyle("position", "absolute");
33455         if(!panel.background){
33456             this.setActivePanel(panel);
33457             if(this.config.initialSize && this.panels.getCount()==1){
33458                 this.resizeTo(this.config.initialSize);
33459             }
33460         }
33461         this.fireEvent("paneladded", this, panel);
33462         return panel;
33463     },
33464     
33465     /**
33466      * Returns true if the panel is in this region.
33467      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33468      * @return {Boolean}
33469      */
33470     hasPanel : function(panel){
33471         if(typeof panel == "object"){ // must be panel obj
33472             panel = panel.getId();
33473         }
33474         return this.getPanel(panel) ? true : false;
33475     },
33476     
33477     /**
33478      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33479      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33480      * @param {Boolean} preservePanel Overrides the config preservePanel option
33481      * @return {Roo.ContentPanel} The panel that was removed
33482      */
33483     remove : function(panel, preservePanel){
33484         panel = this.getPanel(panel);
33485         if(!panel){
33486             return null;
33487         }
33488         var e = {};
33489         this.fireEvent("beforeremove", this, panel, e);
33490         if(e.cancel === true){
33491             return null;
33492         }
33493         var panelId = panel.getId();
33494         this.panels.removeKey(panelId);
33495         return panel;
33496     },
33497     
33498     /**
33499      * Returns the panel specified or null if it's not in this region.
33500      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33501      * @return {Roo.ContentPanel}
33502      */
33503     getPanel : function(id){
33504         if(typeof id == "object"){ // must be panel obj
33505             return id;
33506         }
33507         return this.panels.get(id);
33508     },
33509     
33510     /**
33511      * Returns this regions position (north/south/east/west/center).
33512      * @return {String} 
33513      */
33514     getPosition: function(){
33515         return this.position;    
33516     }
33517 });/*
33518  * Based on:
33519  * Ext JS Library 1.1.1
33520  * Copyright(c) 2006-2007, Ext JS, LLC.
33521  *
33522  * Originally Released Under LGPL - original licence link has changed is not relivant.
33523  *
33524  * Fork - LGPL
33525  * <script type="text/javascript">
33526  */
33527  
33528 /**
33529  * @class Roo.bootstrap.layout.Region
33530  * @extends Roo.bootstrap.layout.Basic
33531  * This class represents a region in a layout manager.
33532  
33533  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33534  * @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})
33535  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33536  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33537  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33538  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33539  * @cfg {String}    title           The title for the region (overrides panel titles)
33540  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33541  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33542  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33543  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33544  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33545  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33546  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33547  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33548  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33549  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33550
33551  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33552  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33553  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33554  * @cfg {Number}    width           For East/West panels
33555  * @cfg {Number}    height          For North/South panels
33556  * @cfg {Boolean}   split           To show the splitter
33557  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33558  * 
33559  * @cfg {string}   cls             Extra CSS classes to add to region
33560  * 
33561  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33562  * @cfg {string}   region  the region that it inhabits..
33563  *
33564
33565  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33566  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33567
33568  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33569  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33570  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33571  */
33572 Roo.bootstrap.layout.Region = function(config)
33573 {
33574     this.applyConfig(config);
33575
33576     var mgr = config.mgr;
33577     var pos = config.region;
33578     config.skipConfig = true;
33579     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33580     
33581     if (mgr.el) {
33582         this.onRender(mgr.el);   
33583     }
33584      
33585     this.visible = true;
33586     this.collapsed = false;
33587     this.unrendered_panels = [];
33588 };
33589
33590 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33591
33592     position: '', // set by wrapper (eg. north/south etc..)
33593     unrendered_panels : null,  // unrendered panels.
33594     createBody : function(){
33595         /** This region's body element 
33596         * @type Roo.Element */
33597         this.bodyEl = this.el.createChild({
33598                 tag: "div",
33599                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33600         });
33601     },
33602
33603     onRender: function(ctr, pos)
33604     {
33605         var dh = Roo.DomHelper;
33606         /** This region's container element 
33607         * @type Roo.Element */
33608         this.el = dh.append(ctr.dom, {
33609                 tag: "div",
33610                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33611             }, true);
33612         /** This region's title element 
33613         * @type Roo.Element */
33614     
33615         this.titleEl = dh.append(this.el.dom,
33616             {
33617                     tag: "div",
33618                     unselectable: "on",
33619                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33620                     children:[
33621                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33622                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33623                     ]}, true);
33624         
33625         this.titleEl.enableDisplayMode();
33626         /** This region's title text element 
33627         * @type HTMLElement */
33628         this.titleTextEl = this.titleEl.dom.firstChild;
33629         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33630         /*
33631         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33632         this.closeBtn.enableDisplayMode();
33633         this.closeBtn.on("click", this.closeClicked, this);
33634         this.closeBtn.hide();
33635     */
33636         this.createBody(this.config);
33637         if(this.config.hideWhenEmpty){
33638             this.hide();
33639             this.on("paneladded", this.validateVisibility, this);
33640             this.on("panelremoved", this.validateVisibility, this);
33641         }
33642         if(this.autoScroll){
33643             this.bodyEl.setStyle("overflow", "auto");
33644         }else{
33645             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33646         }
33647         //if(c.titlebar !== false){
33648             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33649                 this.titleEl.hide();
33650             }else{
33651                 this.titleEl.show();
33652                 if(this.config.title){
33653                     this.titleTextEl.innerHTML = this.config.title;
33654                 }
33655             }
33656         //}
33657         if(this.config.collapsed){
33658             this.collapse(true);
33659         }
33660         if(this.config.hidden){
33661             this.hide();
33662         }
33663         
33664         if (this.unrendered_panels && this.unrendered_panels.length) {
33665             for (var i =0;i< this.unrendered_panels.length; i++) {
33666                 this.add(this.unrendered_panels[i]);
33667             }
33668             this.unrendered_panels = null;
33669             
33670         }
33671         
33672     },
33673     
33674     applyConfig : function(c)
33675     {
33676         /*
33677          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33678             var dh = Roo.DomHelper;
33679             if(c.titlebar !== false){
33680                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33681                 this.collapseBtn.on("click", this.collapse, this);
33682                 this.collapseBtn.enableDisplayMode();
33683                 /*
33684                 if(c.showPin === true || this.showPin){
33685                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33686                     this.stickBtn.enableDisplayMode();
33687                     this.stickBtn.on("click", this.expand, this);
33688                     this.stickBtn.hide();
33689                 }
33690                 
33691             }
33692             */
33693             /** This region's collapsed element
33694             * @type Roo.Element */
33695             /*
33696              *
33697             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33698                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33699             ]}, true);
33700             
33701             if(c.floatable !== false){
33702                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33703                this.collapsedEl.on("click", this.collapseClick, this);
33704             }
33705
33706             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33707                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33708                    id: "message", unselectable: "on", style:{"float":"left"}});
33709                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33710              }
33711             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33712             this.expandBtn.on("click", this.expand, this);
33713             
33714         }
33715         
33716         if(this.collapseBtn){
33717             this.collapseBtn.setVisible(c.collapsible == true);
33718         }
33719         
33720         this.cmargins = c.cmargins || this.cmargins ||
33721                          (this.position == "west" || this.position == "east" ?
33722                              {top: 0, left: 2, right:2, bottom: 0} :
33723                              {top: 2, left: 0, right:0, bottom: 2});
33724         */
33725         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33726         
33727         
33728         this.bottomTabs = c.tabPosition != "top";
33729         
33730         this.autoScroll = c.autoScroll || false;
33731         
33732         
33733        
33734         
33735         this.duration = c.duration || .30;
33736         this.slideDuration = c.slideDuration || .45;
33737         this.config = c;
33738        
33739     },
33740     /**
33741      * Returns true if this region is currently visible.
33742      * @return {Boolean}
33743      */
33744     isVisible : function(){
33745         return this.visible;
33746     },
33747
33748     /**
33749      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33750      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33751      */
33752     //setCollapsedTitle : function(title){
33753     //    title = title || "&#160;";
33754      //   if(this.collapsedTitleTextEl){
33755       //      this.collapsedTitleTextEl.innerHTML = title;
33756        // }
33757     //},
33758
33759     getBox : function(){
33760         var b;
33761       //  if(!this.collapsed){
33762             b = this.el.getBox(false, true);
33763        // }else{
33764           //  b = this.collapsedEl.getBox(false, true);
33765         //}
33766         return b;
33767     },
33768
33769     getMargins : function(){
33770         return this.margins;
33771         //return this.collapsed ? this.cmargins : this.margins;
33772     },
33773 /*
33774     highlight : function(){
33775         this.el.addClass("x-layout-panel-dragover");
33776     },
33777
33778     unhighlight : function(){
33779         this.el.removeClass("x-layout-panel-dragover");
33780     },
33781 */
33782     updateBox : function(box)
33783     {
33784         if (!this.bodyEl) {
33785             return; // not rendered yet..
33786         }
33787         
33788         this.box = box;
33789         if(!this.collapsed){
33790             this.el.dom.style.left = box.x + "px";
33791             this.el.dom.style.top = box.y + "px";
33792             this.updateBody(box.width, box.height);
33793         }else{
33794             this.collapsedEl.dom.style.left = box.x + "px";
33795             this.collapsedEl.dom.style.top = box.y + "px";
33796             this.collapsedEl.setSize(box.width, box.height);
33797         }
33798         if(this.tabs){
33799             this.tabs.autoSizeTabs();
33800         }
33801     },
33802
33803     updateBody : function(w, h)
33804     {
33805         if(w !== null){
33806             this.el.setWidth(w);
33807             w -= this.el.getBorderWidth("rl");
33808             if(this.config.adjustments){
33809                 w += this.config.adjustments[0];
33810             }
33811         }
33812         if(h !== null && h > 0){
33813             this.el.setHeight(h);
33814             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33815             h -= this.el.getBorderWidth("tb");
33816             if(this.config.adjustments){
33817                 h += this.config.adjustments[1];
33818             }
33819             this.bodyEl.setHeight(h);
33820             if(this.tabs){
33821                 h = this.tabs.syncHeight(h);
33822             }
33823         }
33824         if(this.panelSize){
33825             w = w !== null ? w : this.panelSize.width;
33826             h = h !== null ? h : this.panelSize.height;
33827         }
33828         if(this.activePanel){
33829             var el = this.activePanel.getEl();
33830             w = w !== null ? w : el.getWidth();
33831             h = h !== null ? h : el.getHeight();
33832             this.panelSize = {width: w, height: h};
33833             this.activePanel.setSize(w, h);
33834         }
33835         if(Roo.isIE && this.tabs){
33836             this.tabs.el.repaint();
33837         }
33838     },
33839
33840     /**
33841      * Returns the container element for this region.
33842      * @return {Roo.Element}
33843      */
33844     getEl : function(){
33845         return this.el;
33846     },
33847
33848     /**
33849      * Hides this region.
33850      */
33851     hide : function(){
33852         //if(!this.collapsed){
33853             this.el.dom.style.left = "-2000px";
33854             this.el.hide();
33855         //}else{
33856          //   this.collapsedEl.dom.style.left = "-2000px";
33857          //   this.collapsedEl.hide();
33858        // }
33859         this.visible = false;
33860         this.fireEvent("visibilitychange", this, false);
33861     },
33862
33863     /**
33864      * Shows this region if it was previously hidden.
33865      */
33866     show : function(){
33867         //if(!this.collapsed){
33868             this.el.show();
33869         //}else{
33870         //    this.collapsedEl.show();
33871        // }
33872         this.visible = true;
33873         this.fireEvent("visibilitychange", this, true);
33874     },
33875 /*
33876     closeClicked : function(){
33877         if(this.activePanel){
33878             this.remove(this.activePanel);
33879         }
33880     },
33881
33882     collapseClick : function(e){
33883         if(this.isSlid){
33884            e.stopPropagation();
33885            this.slideIn();
33886         }else{
33887            e.stopPropagation();
33888            this.slideOut();
33889         }
33890     },
33891 */
33892     /**
33893      * Collapses this region.
33894      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33895      */
33896     /*
33897     collapse : function(skipAnim, skipCheck = false){
33898         if(this.collapsed) {
33899             return;
33900         }
33901         
33902         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33903             
33904             this.collapsed = true;
33905             if(this.split){
33906                 this.split.el.hide();
33907             }
33908             if(this.config.animate && skipAnim !== true){
33909                 this.fireEvent("invalidated", this);
33910                 this.animateCollapse();
33911             }else{
33912                 this.el.setLocation(-20000,-20000);
33913                 this.el.hide();
33914                 this.collapsedEl.show();
33915                 this.fireEvent("collapsed", this);
33916                 this.fireEvent("invalidated", this);
33917             }
33918         }
33919         
33920     },
33921 */
33922     animateCollapse : function(){
33923         // overridden
33924     },
33925
33926     /**
33927      * Expands this region if it was previously collapsed.
33928      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33929      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33930      */
33931     /*
33932     expand : function(e, skipAnim){
33933         if(e) {
33934             e.stopPropagation();
33935         }
33936         if(!this.collapsed || this.el.hasActiveFx()) {
33937             return;
33938         }
33939         if(this.isSlid){
33940             this.afterSlideIn();
33941             skipAnim = true;
33942         }
33943         this.collapsed = false;
33944         if(this.config.animate && skipAnim !== true){
33945             this.animateExpand();
33946         }else{
33947             this.el.show();
33948             if(this.split){
33949                 this.split.el.show();
33950             }
33951             this.collapsedEl.setLocation(-2000,-2000);
33952             this.collapsedEl.hide();
33953             this.fireEvent("invalidated", this);
33954             this.fireEvent("expanded", this);
33955         }
33956     },
33957 */
33958     animateExpand : function(){
33959         // overridden
33960     },
33961
33962     initTabs : function()
33963     {
33964         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33965         
33966         var ts = new Roo.bootstrap.panel.Tabs({
33967                 el: this.bodyEl.dom,
33968                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33969                 disableTooltips: this.config.disableTabTips,
33970                 toolbar : this.config.toolbar
33971             });
33972         
33973         if(this.config.hideTabs){
33974             ts.stripWrap.setDisplayed(false);
33975         }
33976         this.tabs = ts;
33977         ts.resizeTabs = this.config.resizeTabs === true;
33978         ts.minTabWidth = this.config.minTabWidth || 40;
33979         ts.maxTabWidth = this.config.maxTabWidth || 250;
33980         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33981         ts.monitorResize = false;
33982         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33983         ts.bodyEl.addClass('roo-layout-tabs-body');
33984         this.panels.each(this.initPanelAsTab, this);
33985     },
33986
33987     initPanelAsTab : function(panel){
33988         var ti = this.tabs.addTab(
33989             panel.getEl().id,
33990             panel.getTitle(),
33991             null,
33992             this.config.closeOnTab && panel.isClosable(),
33993             panel.tpl
33994         );
33995         if(panel.tabTip !== undefined){
33996             ti.setTooltip(panel.tabTip);
33997         }
33998         ti.on("activate", function(){
33999               this.setActivePanel(panel);
34000         }, this);
34001         
34002         if(this.config.closeOnTab){
34003             ti.on("beforeclose", function(t, e){
34004                 e.cancel = true;
34005                 this.remove(panel);
34006             }, this);
34007         }
34008         
34009         panel.tabItem = ti;
34010         
34011         return ti;
34012     },
34013
34014     updatePanelTitle : function(panel, title)
34015     {
34016         if(this.activePanel == panel){
34017             this.updateTitle(title);
34018         }
34019         if(this.tabs){
34020             var ti = this.tabs.getTab(panel.getEl().id);
34021             ti.setText(title);
34022             if(panel.tabTip !== undefined){
34023                 ti.setTooltip(panel.tabTip);
34024             }
34025         }
34026     },
34027
34028     updateTitle : function(title){
34029         if(this.titleTextEl && !this.config.title){
34030             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34031         }
34032     },
34033
34034     setActivePanel : function(panel)
34035     {
34036         panel = this.getPanel(panel);
34037         if(this.activePanel && this.activePanel != panel){
34038             this.activePanel.setActiveState(false);
34039         }
34040         this.activePanel = panel;
34041         panel.setActiveState(true);
34042         if(this.panelSize){
34043             panel.setSize(this.panelSize.width, this.panelSize.height);
34044         }
34045         if(this.closeBtn){
34046             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34047         }
34048         this.updateTitle(panel.getTitle());
34049         if(this.tabs){
34050             this.fireEvent("invalidated", this);
34051         }
34052         this.fireEvent("panelactivated", this, panel);
34053     },
34054
34055     /**
34056      * Shows the specified panel.
34057      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34058      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34059      */
34060     showPanel : function(panel)
34061     {
34062         panel = this.getPanel(panel);
34063         if(panel){
34064             if(this.tabs){
34065                 var tab = this.tabs.getTab(panel.getEl().id);
34066                 if(tab.isHidden()){
34067                     this.tabs.unhideTab(tab.id);
34068                 }
34069                 tab.activate();
34070             }else{
34071                 this.setActivePanel(panel);
34072             }
34073         }
34074         return panel;
34075     },
34076
34077     /**
34078      * Get the active panel for this region.
34079      * @return {Roo.ContentPanel} The active panel or null
34080      */
34081     getActivePanel : function(){
34082         return this.activePanel;
34083     },
34084
34085     validateVisibility : function(){
34086         if(this.panels.getCount() < 1){
34087             this.updateTitle("&#160;");
34088             this.closeBtn.hide();
34089             this.hide();
34090         }else{
34091             if(!this.isVisible()){
34092                 this.show();
34093             }
34094         }
34095     },
34096
34097     /**
34098      * Adds the passed ContentPanel(s) to this region.
34099      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34100      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34101      */
34102     add : function(panel)
34103     {
34104         if(arguments.length > 1){
34105             for(var i = 0, len = arguments.length; i < len; i++) {
34106                 this.add(arguments[i]);
34107             }
34108             return null;
34109         }
34110         
34111         // if we have not been rendered yet, then we can not really do much of this..
34112         if (!this.bodyEl) {
34113             this.unrendered_panels.push(panel);
34114             return panel;
34115         }
34116         
34117         
34118         
34119         
34120         if(this.hasPanel(panel)){
34121             this.showPanel(panel);
34122             return panel;
34123         }
34124         panel.setRegion(this);
34125         this.panels.add(panel);
34126        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34127             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34128             // and hide them... ???
34129             this.bodyEl.dom.appendChild(panel.getEl().dom);
34130             if(panel.background !== true){
34131                 this.setActivePanel(panel);
34132             }
34133             this.fireEvent("paneladded", this, panel);
34134             return panel;
34135         }
34136         */
34137         if(!this.tabs){
34138             this.initTabs();
34139         }else{
34140             this.initPanelAsTab(panel);
34141         }
34142         
34143         
34144         if(panel.background !== true){
34145             this.tabs.activate(panel.getEl().id);
34146         }
34147         this.fireEvent("paneladded", this, panel);
34148         return panel;
34149     },
34150
34151     /**
34152      * Hides the tab for the specified panel.
34153      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34154      */
34155     hidePanel : function(panel){
34156         if(this.tabs && (panel = this.getPanel(panel))){
34157             this.tabs.hideTab(panel.getEl().id);
34158         }
34159     },
34160
34161     /**
34162      * Unhides the tab for a previously hidden panel.
34163      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34164      */
34165     unhidePanel : function(panel){
34166         if(this.tabs && (panel = this.getPanel(panel))){
34167             this.tabs.unhideTab(panel.getEl().id);
34168         }
34169     },
34170
34171     clearPanels : function(){
34172         while(this.panels.getCount() > 0){
34173              this.remove(this.panels.first());
34174         }
34175     },
34176
34177     /**
34178      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34179      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34180      * @param {Boolean} preservePanel Overrides the config preservePanel option
34181      * @return {Roo.ContentPanel} The panel that was removed
34182      */
34183     remove : function(panel, preservePanel)
34184     {
34185         panel = this.getPanel(panel);
34186         if(!panel){
34187             return null;
34188         }
34189         var e = {};
34190         this.fireEvent("beforeremove", this, panel, e);
34191         if(e.cancel === true){
34192             return null;
34193         }
34194         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34195         var panelId = panel.getId();
34196         this.panels.removeKey(panelId);
34197         if(preservePanel){
34198             document.body.appendChild(panel.getEl().dom);
34199         }
34200         if(this.tabs){
34201             this.tabs.removeTab(panel.getEl().id);
34202         }else if (!preservePanel){
34203             this.bodyEl.dom.removeChild(panel.getEl().dom);
34204         }
34205         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34206             var p = this.panels.first();
34207             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34208             tempEl.appendChild(p.getEl().dom);
34209             this.bodyEl.update("");
34210             this.bodyEl.dom.appendChild(p.getEl().dom);
34211             tempEl = null;
34212             this.updateTitle(p.getTitle());
34213             this.tabs = null;
34214             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34215             this.setActivePanel(p);
34216         }
34217         panel.setRegion(null);
34218         if(this.activePanel == panel){
34219             this.activePanel = null;
34220         }
34221         if(this.config.autoDestroy !== false && preservePanel !== true){
34222             try{panel.destroy();}catch(e){}
34223         }
34224         this.fireEvent("panelremoved", this, panel);
34225         return panel;
34226     },
34227
34228     /**
34229      * Returns the TabPanel component used by this region
34230      * @return {Roo.TabPanel}
34231      */
34232     getTabs : function(){
34233         return this.tabs;
34234     },
34235
34236     createTool : function(parentEl, className){
34237         var btn = Roo.DomHelper.append(parentEl, {
34238             tag: "div",
34239             cls: "x-layout-tools-button",
34240             children: [ {
34241                 tag: "div",
34242                 cls: "roo-layout-tools-button-inner " + className,
34243                 html: "&#160;"
34244             }]
34245         }, true);
34246         btn.addClassOnOver("roo-layout-tools-button-over");
34247         return btn;
34248     }
34249 });/*
34250  * Based on:
34251  * Ext JS Library 1.1.1
34252  * Copyright(c) 2006-2007, Ext JS, LLC.
34253  *
34254  * Originally Released Under LGPL - original licence link has changed is not relivant.
34255  *
34256  * Fork - LGPL
34257  * <script type="text/javascript">
34258  */
34259  
34260
34261
34262 /**
34263  * @class Roo.SplitLayoutRegion
34264  * @extends Roo.LayoutRegion
34265  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34266  */
34267 Roo.bootstrap.layout.Split = function(config){
34268     this.cursor = config.cursor;
34269     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34270 };
34271
34272 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34273 {
34274     splitTip : "Drag to resize.",
34275     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34276     useSplitTips : false,
34277
34278     applyConfig : function(config){
34279         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34280     },
34281     
34282     onRender : function(ctr,pos) {
34283         
34284         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34285         if(!this.config.split){
34286             return;
34287         }
34288         if(!this.split){
34289             
34290             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34291                             tag: "div",
34292                             id: this.el.id + "-split",
34293                             cls: "roo-layout-split roo-layout-split-"+this.position,
34294                             html: "&#160;"
34295             });
34296             /** The SplitBar for this region 
34297             * @type Roo.SplitBar */
34298             // does not exist yet...
34299             Roo.log([this.position, this.orientation]);
34300             
34301             this.split = new Roo.bootstrap.SplitBar({
34302                 dragElement : splitEl,
34303                 resizingElement: this.el,
34304                 orientation : this.orientation
34305             });
34306             
34307             this.split.on("moved", this.onSplitMove, this);
34308             this.split.useShim = this.config.useShim === true;
34309             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34310             if(this.useSplitTips){
34311                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34312             }
34313             //if(config.collapsible){
34314             //    this.split.el.on("dblclick", this.collapse,  this);
34315             //}
34316         }
34317         if(typeof this.config.minSize != "undefined"){
34318             this.split.minSize = this.config.minSize;
34319         }
34320         if(typeof this.config.maxSize != "undefined"){
34321             this.split.maxSize = this.config.maxSize;
34322         }
34323         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34324             this.hideSplitter();
34325         }
34326         
34327     },
34328
34329     getHMaxSize : function(){
34330          var cmax = this.config.maxSize || 10000;
34331          var center = this.mgr.getRegion("center");
34332          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34333     },
34334
34335     getVMaxSize : function(){
34336          var cmax = this.config.maxSize || 10000;
34337          var center = this.mgr.getRegion("center");
34338          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34339     },
34340
34341     onSplitMove : function(split, newSize){
34342         this.fireEvent("resized", this, newSize);
34343     },
34344     
34345     /** 
34346      * Returns the {@link Roo.SplitBar} for this region.
34347      * @return {Roo.SplitBar}
34348      */
34349     getSplitBar : function(){
34350         return this.split;
34351     },
34352     
34353     hide : function(){
34354         this.hideSplitter();
34355         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34356     },
34357
34358     hideSplitter : function(){
34359         if(this.split){
34360             this.split.el.setLocation(-2000,-2000);
34361             this.split.el.hide();
34362         }
34363     },
34364
34365     show : function(){
34366         if(this.split){
34367             this.split.el.show();
34368         }
34369         Roo.bootstrap.layout.Split.superclass.show.call(this);
34370     },
34371     
34372     beforeSlide: function(){
34373         if(Roo.isGecko){// firefox overflow auto bug workaround
34374             this.bodyEl.clip();
34375             if(this.tabs) {
34376                 this.tabs.bodyEl.clip();
34377             }
34378             if(this.activePanel){
34379                 this.activePanel.getEl().clip();
34380                 
34381                 if(this.activePanel.beforeSlide){
34382                     this.activePanel.beforeSlide();
34383                 }
34384             }
34385         }
34386     },
34387     
34388     afterSlide : function(){
34389         if(Roo.isGecko){// firefox overflow auto bug workaround
34390             this.bodyEl.unclip();
34391             if(this.tabs) {
34392                 this.tabs.bodyEl.unclip();
34393             }
34394             if(this.activePanel){
34395                 this.activePanel.getEl().unclip();
34396                 if(this.activePanel.afterSlide){
34397                     this.activePanel.afterSlide();
34398                 }
34399             }
34400         }
34401     },
34402
34403     initAutoHide : function(){
34404         if(this.autoHide !== false){
34405             if(!this.autoHideHd){
34406                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34407                 this.autoHideHd = {
34408                     "mouseout": function(e){
34409                         if(!e.within(this.el, true)){
34410                             st.delay(500);
34411                         }
34412                     },
34413                     "mouseover" : function(e){
34414                         st.cancel();
34415                     },
34416                     scope : this
34417                 };
34418             }
34419             this.el.on(this.autoHideHd);
34420         }
34421     },
34422
34423     clearAutoHide : function(){
34424         if(this.autoHide !== false){
34425             this.el.un("mouseout", this.autoHideHd.mouseout);
34426             this.el.un("mouseover", this.autoHideHd.mouseover);
34427         }
34428     },
34429
34430     clearMonitor : function(){
34431         Roo.get(document).un("click", this.slideInIf, this);
34432     },
34433
34434     // these names are backwards but not changed for compat
34435     slideOut : function(){
34436         if(this.isSlid || this.el.hasActiveFx()){
34437             return;
34438         }
34439         this.isSlid = true;
34440         if(this.collapseBtn){
34441             this.collapseBtn.hide();
34442         }
34443         this.closeBtnState = this.closeBtn.getStyle('display');
34444         this.closeBtn.hide();
34445         if(this.stickBtn){
34446             this.stickBtn.show();
34447         }
34448         this.el.show();
34449         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34450         this.beforeSlide();
34451         this.el.setStyle("z-index", 10001);
34452         this.el.slideIn(this.getSlideAnchor(), {
34453             callback: function(){
34454                 this.afterSlide();
34455                 this.initAutoHide();
34456                 Roo.get(document).on("click", this.slideInIf, this);
34457                 this.fireEvent("slideshow", this);
34458             },
34459             scope: this,
34460             block: true
34461         });
34462     },
34463
34464     afterSlideIn : function(){
34465         this.clearAutoHide();
34466         this.isSlid = false;
34467         this.clearMonitor();
34468         this.el.setStyle("z-index", "");
34469         if(this.collapseBtn){
34470             this.collapseBtn.show();
34471         }
34472         this.closeBtn.setStyle('display', this.closeBtnState);
34473         if(this.stickBtn){
34474             this.stickBtn.hide();
34475         }
34476         this.fireEvent("slidehide", this);
34477     },
34478
34479     slideIn : function(cb){
34480         if(!this.isSlid || this.el.hasActiveFx()){
34481             Roo.callback(cb);
34482             return;
34483         }
34484         this.isSlid = false;
34485         this.beforeSlide();
34486         this.el.slideOut(this.getSlideAnchor(), {
34487             callback: function(){
34488                 this.el.setLeftTop(-10000, -10000);
34489                 this.afterSlide();
34490                 this.afterSlideIn();
34491                 Roo.callback(cb);
34492             },
34493             scope: this,
34494             block: true
34495         });
34496     },
34497     
34498     slideInIf : function(e){
34499         if(!e.within(this.el)){
34500             this.slideIn();
34501         }
34502     },
34503
34504     animateCollapse : function(){
34505         this.beforeSlide();
34506         this.el.setStyle("z-index", 20000);
34507         var anchor = this.getSlideAnchor();
34508         this.el.slideOut(anchor, {
34509             callback : function(){
34510                 this.el.setStyle("z-index", "");
34511                 this.collapsedEl.slideIn(anchor, {duration:.3});
34512                 this.afterSlide();
34513                 this.el.setLocation(-10000,-10000);
34514                 this.el.hide();
34515                 this.fireEvent("collapsed", this);
34516             },
34517             scope: this,
34518             block: true
34519         });
34520     },
34521
34522     animateExpand : function(){
34523         this.beforeSlide();
34524         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34525         this.el.setStyle("z-index", 20000);
34526         this.collapsedEl.hide({
34527             duration:.1
34528         });
34529         this.el.slideIn(this.getSlideAnchor(), {
34530             callback : function(){
34531                 this.el.setStyle("z-index", "");
34532                 this.afterSlide();
34533                 if(this.split){
34534                     this.split.el.show();
34535                 }
34536                 this.fireEvent("invalidated", this);
34537                 this.fireEvent("expanded", this);
34538             },
34539             scope: this,
34540             block: true
34541         });
34542     },
34543
34544     anchors : {
34545         "west" : "left",
34546         "east" : "right",
34547         "north" : "top",
34548         "south" : "bottom"
34549     },
34550
34551     sanchors : {
34552         "west" : "l",
34553         "east" : "r",
34554         "north" : "t",
34555         "south" : "b"
34556     },
34557
34558     canchors : {
34559         "west" : "tl-tr",
34560         "east" : "tr-tl",
34561         "north" : "tl-bl",
34562         "south" : "bl-tl"
34563     },
34564
34565     getAnchor : function(){
34566         return this.anchors[this.position];
34567     },
34568
34569     getCollapseAnchor : function(){
34570         return this.canchors[this.position];
34571     },
34572
34573     getSlideAnchor : function(){
34574         return this.sanchors[this.position];
34575     },
34576
34577     getAlignAdj : function(){
34578         var cm = this.cmargins;
34579         switch(this.position){
34580             case "west":
34581                 return [0, 0];
34582             break;
34583             case "east":
34584                 return [0, 0];
34585             break;
34586             case "north":
34587                 return [0, 0];
34588             break;
34589             case "south":
34590                 return [0, 0];
34591             break;
34592         }
34593     },
34594
34595     getExpandAdj : function(){
34596         var c = this.collapsedEl, cm = this.cmargins;
34597         switch(this.position){
34598             case "west":
34599                 return [-(cm.right+c.getWidth()+cm.left), 0];
34600             break;
34601             case "east":
34602                 return [cm.right+c.getWidth()+cm.left, 0];
34603             break;
34604             case "north":
34605                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34606             break;
34607             case "south":
34608                 return [0, cm.top+cm.bottom+c.getHeight()];
34609             break;
34610         }
34611     }
34612 });/*
34613  * Based on:
34614  * Ext JS Library 1.1.1
34615  * Copyright(c) 2006-2007, Ext JS, LLC.
34616  *
34617  * Originally Released Under LGPL - original licence link has changed is not relivant.
34618  *
34619  * Fork - LGPL
34620  * <script type="text/javascript">
34621  */
34622 /*
34623  * These classes are private internal classes
34624  */
34625 Roo.bootstrap.layout.Center = function(config){
34626     config.region = "center";
34627     Roo.bootstrap.layout.Region.call(this, config);
34628     this.visible = true;
34629     this.minWidth = config.minWidth || 20;
34630     this.minHeight = config.minHeight || 20;
34631 };
34632
34633 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34634     hide : function(){
34635         // center panel can't be hidden
34636     },
34637     
34638     show : function(){
34639         // center panel can't be hidden
34640     },
34641     
34642     getMinWidth: function(){
34643         return this.minWidth;
34644     },
34645     
34646     getMinHeight: function(){
34647         return this.minHeight;
34648     }
34649 });
34650
34651
34652
34653
34654  
34655
34656
34657
34658
34659
34660 Roo.bootstrap.layout.North = function(config)
34661 {
34662     config.region = 'north';
34663     config.cursor = 'n-resize';
34664     
34665     Roo.bootstrap.layout.Split.call(this, config);
34666     
34667     
34668     if(this.split){
34669         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34670         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34671         this.split.el.addClass("roo-layout-split-v");
34672     }
34673     var size = config.initialSize || config.height;
34674     if(typeof size != "undefined"){
34675         this.el.setHeight(size);
34676     }
34677 };
34678 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34679 {
34680     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34681     
34682     
34683     
34684     getBox : function(){
34685         if(this.collapsed){
34686             return this.collapsedEl.getBox();
34687         }
34688         var box = this.el.getBox();
34689         if(this.split){
34690             box.height += this.split.el.getHeight();
34691         }
34692         return box;
34693     },
34694     
34695     updateBox : function(box){
34696         if(this.split && !this.collapsed){
34697             box.height -= this.split.el.getHeight();
34698             this.split.el.setLeft(box.x);
34699             this.split.el.setTop(box.y+box.height);
34700             this.split.el.setWidth(box.width);
34701         }
34702         if(this.collapsed){
34703             this.updateBody(box.width, null);
34704         }
34705         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34706     }
34707 });
34708
34709
34710
34711
34712
34713 Roo.bootstrap.layout.South = function(config){
34714     config.region = 'south';
34715     config.cursor = 's-resize';
34716     Roo.bootstrap.layout.Split.call(this, config);
34717     if(this.split){
34718         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34719         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34720         this.split.el.addClass("roo-layout-split-v");
34721     }
34722     var size = config.initialSize || config.height;
34723     if(typeof size != "undefined"){
34724         this.el.setHeight(size);
34725     }
34726 };
34727
34728 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34729     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34730     getBox : function(){
34731         if(this.collapsed){
34732             return this.collapsedEl.getBox();
34733         }
34734         var box = this.el.getBox();
34735         if(this.split){
34736             var sh = this.split.el.getHeight();
34737             box.height += sh;
34738             box.y -= sh;
34739         }
34740         return box;
34741     },
34742     
34743     updateBox : function(box){
34744         if(this.split && !this.collapsed){
34745             var sh = this.split.el.getHeight();
34746             box.height -= sh;
34747             box.y += sh;
34748             this.split.el.setLeft(box.x);
34749             this.split.el.setTop(box.y-sh);
34750             this.split.el.setWidth(box.width);
34751         }
34752         if(this.collapsed){
34753             this.updateBody(box.width, null);
34754         }
34755         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34756     }
34757 });
34758
34759 Roo.bootstrap.layout.East = function(config){
34760     config.region = "east";
34761     config.cursor = "e-resize";
34762     Roo.bootstrap.layout.Split.call(this, config);
34763     if(this.split){
34764         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34765         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34766         this.split.el.addClass("roo-layout-split-h");
34767     }
34768     var size = config.initialSize || config.width;
34769     if(typeof size != "undefined"){
34770         this.el.setWidth(size);
34771     }
34772 };
34773 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34774     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34775     getBox : function(){
34776         if(this.collapsed){
34777             return this.collapsedEl.getBox();
34778         }
34779         var box = this.el.getBox();
34780         if(this.split){
34781             var sw = this.split.el.getWidth();
34782             box.width += sw;
34783             box.x -= sw;
34784         }
34785         return box;
34786     },
34787
34788     updateBox : function(box){
34789         if(this.split && !this.collapsed){
34790             var sw = this.split.el.getWidth();
34791             box.width -= sw;
34792             this.split.el.setLeft(box.x);
34793             this.split.el.setTop(box.y);
34794             this.split.el.setHeight(box.height);
34795             box.x += sw;
34796         }
34797         if(this.collapsed){
34798             this.updateBody(null, box.height);
34799         }
34800         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34801     }
34802 });
34803
34804 Roo.bootstrap.layout.West = function(config){
34805     config.region = "west";
34806     config.cursor = "w-resize";
34807     
34808     Roo.bootstrap.layout.Split.call(this, config);
34809     if(this.split){
34810         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34811         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34812         this.split.el.addClass("roo-layout-split-h");
34813     }
34814     
34815 };
34816 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34817     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34818     
34819     onRender: function(ctr, pos)
34820     {
34821         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34822         var size = this.config.initialSize || this.config.width;
34823         if(typeof size != "undefined"){
34824             this.el.setWidth(size);
34825         }
34826     },
34827     
34828     getBox : function(){
34829         if(this.collapsed){
34830             return this.collapsedEl.getBox();
34831         }
34832         var box = this.el.getBox();
34833         if(this.split){
34834             box.width += this.split.el.getWidth();
34835         }
34836         return box;
34837     },
34838     
34839     updateBox : function(box){
34840         if(this.split && !this.collapsed){
34841             var sw = this.split.el.getWidth();
34842             box.width -= sw;
34843             this.split.el.setLeft(box.x+box.width);
34844             this.split.el.setTop(box.y);
34845             this.split.el.setHeight(box.height);
34846         }
34847         if(this.collapsed){
34848             this.updateBody(null, box.height);
34849         }
34850         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34851     }
34852 });
34853 Roo.namespace("Roo.bootstrap.panel");/*
34854  * Based on:
34855  * Ext JS Library 1.1.1
34856  * Copyright(c) 2006-2007, Ext JS, LLC.
34857  *
34858  * Originally Released Under LGPL - original licence link has changed is not relivant.
34859  *
34860  * Fork - LGPL
34861  * <script type="text/javascript">
34862  */
34863 /**
34864  * @class Roo.ContentPanel
34865  * @extends Roo.util.Observable
34866  * A basic ContentPanel element.
34867  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34868  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34869  * @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
34870  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34871  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34872  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34873  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34874  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34875  * @cfg {String} title          The title for this panel
34876  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34877  * @cfg {String} url            Calls {@link #setUrl} with this value
34878  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34879  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34880  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34881  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34882  * @cfg {Boolean} badges render the badges
34883
34884  * @constructor
34885  * Create a new ContentPanel.
34886  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34887  * @param {String/Object} config A string to set only the title or a config object
34888  * @param {String} content (optional) Set the HTML content for this panel
34889  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34890  */
34891 Roo.bootstrap.panel.Content = function( config){
34892     
34893     this.tpl = config.tpl || false;
34894     
34895     var el = config.el;
34896     var content = config.content;
34897
34898     if(config.autoCreate){ // xtype is available if this is called from factory
34899         el = Roo.id();
34900     }
34901     this.el = Roo.get(el);
34902     if(!this.el && config && config.autoCreate){
34903         if(typeof config.autoCreate == "object"){
34904             if(!config.autoCreate.id){
34905                 config.autoCreate.id = config.id||el;
34906             }
34907             this.el = Roo.DomHelper.append(document.body,
34908                         config.autoCreate, true);
34909         }else{
34910             var elcfg =  {   tag: "div",
34911                             cls: "roo-layout-inactive-content",
34912                             id: config.id||el
34913                             };
34914             if (config.html) {
34915                 elcfg.html = config.html;
34916                 
34917             }
34918                         
34919             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34920         }
34921     } 
34922     this.closable = false;
34923     this.loaded = false;
34924     this.active = false;
34925    
34926       
34927     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34928         
34929         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34930         
34931         this.wrapEl = this.el; //this.el.wrap();
34932         var ti = [];
34933         if (config.toolbar.items) {
34934             ti = config.toolbar.items ;
34935             delete config.toolbar.items ;
34936         }
34937         
34938         var nitems = [];
34939         this.toolbar.render(this.wrapEl, 'before');
34940         for(var i =0;i < ti.length;i++) {
34941           //  Roo.log(['add child', items[i]]);
34942             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34943         }
34944         this.toolbar.items = nitems;
34945         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34946         delete config.toolbar;
34947         
34948     }
34949     /*
34950     // xtype created footer. - not sure if will work as we normally have to render first..
34951     if (this.footer && !this.footer.el && this.footer.xtype) {
34952         if (!this.wrapEl) {
34953             this.wrapEl = this.el.wrap();
34954         }
34955     
34956         this.footer.container = this.wrapEl.createChild();
34957          
34958         this.footer = Roo.factory(this.footer, Roo);
34959         
34960     }
34961     */
34962     
34963      if(typeof config == "string"){
34964         this.title = config;
34965     }else{
34966         Roo.apply(this, config);
34967     }
34968     
34969     if(this.resizeEl){
34970         this.resizeEl = Roo.get(this.resizeEl, true);
34971     }else{
34972         this.resizeEl = this.el;
34973     }
34974     // handle view.xtype
34975     
34976  
34977     
34978     
34979     this.addEvents({
34980         /**
34981          * @event activate
34982          * Fires when this panel is activated. 
34983          * @param {Roo.ContentPanel} this
34984          */
34985         "activate" : true,
34986         /**
34987          * @event deactivate
34988          * Fires when this panel is activated. 
34989          * @param {Roo.ContentPanel} this
34990          */
34991         "deactivate" : true,
34992
34993         /**
34994          * @event resize
34995          * Fires when this panel is resized if fitToFrame is true.
34996          * @param {Roo.ContentPanel} this
34997          * @param {Number} width The width after any component adjustments
34998          * @param {Number} height The height after any component adjustments
34999          */
35000         "resize" : true,
35001         
35002          /**
35003          * @event render
35004          * Fires when this tab is created
35005          * @param {Roo.ContentPanel} this
35006          */
35007         "render" : true
35008         
35009         
35010         
35011     });
35012     
35013
35014     
35015     
35016     if(this.autoScroll){
35017         this.resizeEl.setStyle("overflow", "auto");
35018     } else {
35019         // fix randome scrolling
35020         //this.el.on('scroll', function() {
35021         //    Roo.log('fix random scolling');
35022         //    this.scrollTo('top',0); 
35023         //});
35024     }
35025     content = content || this.content;
35026     if(content){
35027         this.setContent(content);
35028     }
35029     if(config && config.url){
35030         this.setUrl(this.url, this.params, this.loadOnce);
35031     }
35032     
35033     
35034     
35035     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35036     
35037     if (this.view && typeof(this.view.xtype) != 'undefined') {
35038         this.view.el = this.el.appendChild(document.createElement("div"));
35039         this.view = Roo.factory(this.view); 
35040         this.view.render  &&  this.view.render(false, '');  
35041     }
35042     
35043     
35044     this.fireEvent('render', this);
35045 };
35046
35047 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35048     
35049     tabTip : '',
35050     
35051     setRegion : function(region){
35052         this.region = region;
35053         this.setActiveClass(region && !this.background);
35054     },
35055     
35056     
35057     setActiveClass: function(state)
35058     {
35059         if(state){
35060            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35061            this.el.setStyle('position','relative');
35062         }else{
35063            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35064            this.el.setStyle('position', 'absolute');
35065         } 
35066     },
35067     
35068     /**
35069      * Returns the toolbar for this Panel if one was configured. 
35070      * @return {Roo.Toolbar} 
35071      */
35072     getToolbar : function(){
35073         return this.toolbar;
35074     },
35075     
35076     setActiveState : function(active)
35077     {
35078         this.active = active;
35079         this.setActiveClass(active);
35080         if(!active){
35081             this.fireEvent("deactivate", this);
35082         }else{
35083             this.fireEvent("activate", this);
35084         }
35085     },
35086     /**
35087      * Updates this panel's element
35088      * @param {String} content The new content
35089      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35090     */
35091     setContent : function(content, loadScripts){
35092         this.el.update(content, loadScripts);
35093     },
35094
35095     ignoreResize : function(w, h){
35096         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35097             return true;
35098         }else{
35099             this.lastSize = {width: w, height: h};
35100             return false;
35101         }
35102     },
35103     /**
35104      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35105      * @return {Roo.UpdateManager} The UpdateManager
35106      */
35107     getUpdateManager : function(){
35108         return this.el.getUpdateManager();
35109     },
35110      /**
35111      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35112      * @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:
35113 <pre><code>
35114 panel.load({
35115     url: "your-url.php",
35116     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35117     callback: yourFunction,
35118     scope: yourObject, //(optional scope)
35119     discardUrl: false,
35120     nocache: false,
35121     text: "Loading...",
35122     timeout: 30,
35123     scripts: false
35124 });
35125 </code></pre>
35126      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35127      * 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.
35128      * @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}
35129      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35130      * @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.
35131      * @return {Roo.ContentPanel} this
35132      */
35133     load : function(){
35134         var um = this.el.getUpdateManager();
35135         um.update.apply(um, arguments);
35136         return this;
35137     },
35138
35139
35140     /**
35141      * 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.
35142      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35143      * @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)
35144      * @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)
35145      * @return {Roo.UpdateManager} The UpdateManager
35146      */
35147     setUrl : function(url, params, loadOnce){
35148         if(this.refreshDelegate){
35149             this.removeListener("activate", this.refreshDelegate);
35150         }
35151         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35152         this.on("activate", this.refreshDelegate);
35153         return this.el.getUpdateManager();
35154     },
35155     
35156     _handleRefresh : function(url, params, loadOnce){
35157         if(!loadOnce || !this.loaded){
35158             var updater = this.el.getUpdateManager();
35159             updater.update(url, params, this._setLoaded.createDelegate(this));
35160         }
35161     },
35162     
35163     _setLoaded : function(){
35164         this.loaded = true;
35165     }, 
35166     
35167     /**
35168      * Returns this panel's id
35169      * @return {String} 
35170      */
35171     getId : function(){
35172         return this.el.id;
35173     },
35174     
35175     /** 
35176      * Returns this panel's element - used by regiosn to add.
35177      * @return {Roo.Element} 
35178      */
35179     getEl : function(){
35180         return this.wrapEl || this.el;
35181     },
35182     
35183    
35184     
35185     adjustForComponents : function(width, height)
35186     {
35187         //Roo.log('adjustForComponents ');
35188         if(this.resizeEl != this.el){
35189             width -= this.el.getFrameWidth('lr');
35190             height -= this.el.getFrameWidth('tb');
35191         }
35192         if(this.toolbar){
35193             var te = this.toolbar.getEl();
35194             height -= te.getHeight();
35195             te.setWidth(width);
35196         }
35197         if(this.footer){
35198             var te = this.footer.getEl();
35199             Roo.log("footer:" + te.getHeight());
35200             
35201             height -= te.getHeight();
35202             te.setWidth(width);
35203         }
35204         
35205         
35206         if(this.adjustments){
35207             width += this.adjustments[0];
35208             height += this.adjustments[1];
35209         }
35210         return {"width": width, "height": height};
35211     },
35212     
35213     setSize : function(width, height){
35214         if(this.fitToFrame && !this.ignoreResize(width, height)){
35215             if(this.fitContainer && this.resizeEl != this.el){
35216                 this.el.setSize(width, height);
35217             }
35218             var size = this.adjustForComponents(width, height);
35219             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35220             this.fireEvent('resize', this, size.width, size.height);
35221         }
35222     },
35223     
35224     /**
35225      * Returns this panel's title
35226      * @return {String} 
35227      */
35228     getTitle : function(){
35229         return this.title;
35230     },
35231     
35232     /**
35233      * Set this panel's title
35234      * @param {String} title
35235      */
35236     setTitle : function(title){
35237         this.title = title;
35238         if(this.region){
35239             this.region.updatePanelTitle(this, title);
35240         }
35241     },
35242     
35243     /**
35244      * Returns true is this panel was configured to be closable
35245      * @return {Boolean} 
35246      */
35247     isClosable : function(){
35248         return this.closable;
35249     },
35250     
35251     beforeSlide : function(){
35252         this.el.clip();
35253         this.resizeEl.clip();
35254     },
35255     
35256     afterSlide : function(){
35257         this.el.unclip();
35258         this.resizeEl.unclip();
35259     },
35260     
35261     /**
35262      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35263      *   Will fail silently if the {@link #setUrl} method has not been called.
35264      *   This does not activate the panel, just updates its content.
35265      */
35266     refresh : function(){
35267         if(this.refreshDelegate){
35268            this.loaded = false;
35269            this.refreshDelegate();
35270         }
35271     },
35272     
35273     /**
35274      * Destroys this panel
35275      */
35276     destroy : function(){
35277         this.el.removeAllListeners();
35278         var tempEl = document.createElement("span");
35279         tempEl.appendChild(this.el.dom);
35280         tempEl.innerHTML = "";
35281         this.el.remove();
35282         this.el = null;
35283     },
35284     
35285     /**
35286      * form - if the content panel contains a form - this is a reference to it.
35287      * @type {Roo.form.Form}
35288      */
35289     form : false,
35290     /**
35291      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35292      *    This contains a reference to it.
35293      * @type {Roo.View}
35294      */
35295     view : false,
35296     
35297       /**
35298      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35299      * <pre><code>
35300
35301 layout.addxtype({
35302        xtype : 'Form',
35303        items: [ .... ]
35304    }
35305 );
35306
35307 </code></pre>
35308      * @param {Object} cfg Xtype definition of item to add.
35309      */
35310     
35311     
35312     getChildContainer: function () {
35313         return this.getEl();
35314     }
35315     
35316     
35317     /*
35318         var  ret = new Roo.factory(cfg);
35319         return ret;
35320         
35321         
35322         // add form..
35323         if (cfg.xtype.match(/^Form$/)) {
35324             
35325             var el;
35326             //if (this.footer) {
35327             //    el = this.footer.container.insertSibling(false, 'before');
35328             //} else {
35329                 el = this.el.createChild();
35330             //}
35331
35332             this.form = new  Roo.form.Form(cfg);
35333             
35334             
35335             if ( this.form.allItems.length) {
35336                 this.form.render(el.dom);
35337             }
35338             return this.form;
35339         }
35340         // should only have one of theses..
35341         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35342             // views.. should not be just added - used named prop 'view''
35343             
35344             cfg.el = this.el.appendChild(document.createElement("div"));
35345             // factory?
35346             
35347             var ret = new Roo.factory(cfg);
35348              
35349              ret.render && ret.render(false, ''); // render blank..
35350             this.view = ret;
35351             return ret;
35352         }
35353         return false;
35354     }
35355     \*/
35356 });
35357  
35358 /**
35359  * @class Roo.bootstrap.panel.Grid
35360  * @extends Roo.bootstrap.panel.Content
35361  * @constructor
35362  * Create a new GridPanel.
35363  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35364  * @param {Object} config A the config object
35365   
35366  */
35367
35368
35369
35370 Roo.bootstrap.panel.Grid = function(config)
35371 {
35372     
35373       
35374     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35375         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35376
35377     config.el = this.wrapper;
35378     //this.el = this.wrapper;
35379     
35380       if (config.container) {
35381         // ctor'ed from a Border/panel.grid
35382         
35383         
35384         this.wrapper.setStyle("overflow", "hidden");
35385         this.wrapper.addClass('roo-grid-container');
35386
35387     }
35388     
35389     
35390     if(config.toolbar){
35391         var tool_el = this.wrapper.createChild();    
35392         this.toolbar = Roo.factory(config.toolbar);
35393         var ti = [];
35394         if (config.toolbar.items) {
35395             ti = config.toolbar.items ;
35396             delete config.toolbar.items ;
35397         }
35398         
35399         var nitems = [];
35400         this.toolbar.render(tool_el);
35401         for(var i =0;i < ti.length;i++) {
35402           //  Roo.log(['add child', items[i]]);
35403             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35404         }
35405         this.toolbar.items = nitems;
35406         
35407         delete config.toolbar;
35408     }
35409     
35410     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35411     config.grid.scrollBody = true;;
35412     config.grid.monitorWindowResize = false; // turn off autosizing
35413     config.grid.autoHeight = false;
35414     config.grid.autoWidth = false;
35415     
35416     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35417     
35418     if (config.background) {
35419         // render grid on panel activation (if panel background)
35420         this.on('activate', function(gp) {
35421             if (!gp.grid.rendered) {
35422                 gp.grid.render(this.wrapper);
35423                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35424             }
35425         });
35426             
35427     } else {
35428         this.grid.render(this.wrapper);
35429         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35430
35431     }
35432     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35433     // ??? needed ??? config.el = this.wrapper;
35434     
35435     
35436     
35437   
35438     // xtype created footer. - not sure if will work as we normally have to render first..
35439     if (this.footer && !this.footer.el && this.footer.xtype) {
35440         
35441         var ctr = this.grid.getView().getFooterPanel(true);
35442         this.footer.dataSource = this.grid.dataSource;
35443         this.footer = Roo.factory(this.footer, Roo);
35444         this.footer.render(ctr);
35445         
35446     }
35447     
35448     
35449     
35450     
35451      
35452 };
35453
35454 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35455     getId : function(){
35456         return this.grid.id;
35457     },
35458     
35459     /**
35460      * Returns the grid for this panel
35461      * @return {Roo.bootstrap.Table} 
35462      */
35463     getGrid : function(){
35464         return this.grid;    
35465     },
35466     
35467     setSize : function(width, height){
35468         if(!this.ignoreResize(width, height)){
35469             var grid = this.grid;
35470             var size = this.adjustForComponents(width, height);
35471             var gridel = grid.getGridEl();
35472             gridel.setSize(size.width, size.height);
35473             /*
35474             var thd = grid.getGridEl().select('thead',true).first();
35475             var tbd = grid.getGridEl().select('tbody', true).first();
35476             if (tbd) {
35477                 tbd.setSize(width, height - thd.getHeight());
35478             }
35479             */
35480             grid.autoSize();
35481         }
35482     },
35483      
35484     
35485     
35486     beforeSlide : function(){
35487         this.grid.getView().scroller.clip();
35488     },
35489     
35490     afterSlide : function(){
35491         this.grid.getView().scroller.unclip();
35492     },
35493     
35494     destroy : function(){
35495         this.grid.destroy();
35496         delete this.grid;
35497         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35498     }
35499 });
35500
35501 /**
35502  * @class Roo.bootstrap.panel.Nest
35503  * @extends Roo.bootstrap.panel.Content
35504  * @constructor
35505  * Create a new Panel, that can contain a layout.Border.
35506  * 
35507  * 
35508  * @param {Roo.BorderLayout} layout The layout for this panel
35509  * @param {String/Object} config A string to set only the title or a config object
35510  */
35511 Roo.bootstrap.panel.Nest = function(config)
35512 {
35513     // construct with only one argument..
35514     /* FIXME - implement nicer consturctors
35515     if (layout.layout) {
35516         config = layout;
35517         layout = config.layout;
35518         delete config.layout;
35519     }
35520     if (layout.xtype && !layout.getEl) {
35521         // then layout needs constructing..
35522         layout = Roo.factory(layout, Roo);
35523     }
35524     */
35525     
35526     config.el =  config.layout.getEl();
35527     
35528     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35529     
35530     config.layout.monitorWindowResize = false; // turn off autosizing
35531     this.layout = config.layout;
35532     this.layout.getEl().addClass("roo-layout-nested-layout");
35533     
35534     
35535     
35536     
35537 };
35538
35539 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35540
35541     setSize : function(width, height){
35542         if(!this.ignoreResize(width, height)){
35543             var size = this.adjustForComponents(width, height);
35544             var el = this.layout.getEl();
35545             if (size.height < 1) {
35546                 el.setWidth(size.width);   
35547             } else {
35548                 el.setSize(size.width, size.height);
35549             }
35550             var touch = el.dom.offsetWidth;
35551             this.layout.layout();
35552             // ie requires a double layout on the first pass
35553             if(Roo.isIE && !this.initialized){
35554                 this.initialized = true;
35555                 this.layout.layout();
35556             }
35557         }
35558     },
35559     
35560     // activate all subpanels if not currently active..
35561     
35562     setActiveState : function(active){
35563         this.active = active;
35564         this.setActiveClass(active);
35565         
35566         if(!active){
35567             this.fireEvent("deactivate", this);
35568             return;
35569         }
35570         
35571         this.fireEvent("activate", this);
35572         // not sure if this should happen before or after..
35573         if (!this.layout) {
35574             return; // should not happen..
35575         }
35576         var reg = false;
35577         for (var r in this.layout.regions) {
35578             reg = this.layout.getRegion(r);
35579             if (reg.getActivePanel()) {
35580                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35581                 reg.setActivePanel(reg.getActivePanel());
35582                 continue;
35583             }
35584             if (!reg.panels.length) {
35585                 continue;
35586             }
35587             reg.showPanel(reg.getPanel(0));
35588         }
35589         
35590         
35591         
35592         
35593     },
35594     
35595     /**
35596      * Returns the nested BorderLayout for this panel
35597      * @return {Roo.BorderLayout} 
35598      */
35599     getLayout : function(){
35600         return this.layout;
35601     },
35602     
35603      /**
35604      * Adds a xtype elements to the layout of the nested panel
35605      * <pre><code>
35606
35607 panel.addxtype({
35608        xtype : 'ContentPanel',
35609        region: 'west',
35610        items: [ .... ]
35611    }
35612 );
35613
35614 panel.addxtype({
35615         xtype : 'NestedLayoutPanel',
35616         region: 'west',
35617         layout: {
35618            center: { },
35619            west: { }   
35620         },
35621         items : [ ... list of content panels or nested layout panels.. ]
35622    }
35623 );
35624 </code></pre>
35625      * @param {Object} cfg Xtype definition of item to add.
35626      */
35627     addxtype : function(cfg) {
35628         return this.layout.addxtype(cfg);
35629     
35630     }
35631 });        /*
35632  * Based on:
35633  * Ext JS Library 1.1.1
35634  * Copyright(c) 2006-2007, Ext JS, LLC.
35635  *
35636  * Originally Released Under LGPL - original licence link has changed is not relivant.
35637  *
35638  * Fork - LGPL
35639  * <script type="text/javascript">
35640  */
35641 /**
35642  * @class Roo.TabPanel
35643  * @extends Roo.util.Observable
35644  * A lightweight tab container.
35645  * <br><br>
35646  * Usage:
35647  * <pre><code>
35648 // basic tabs 1, built from existing content
35649 var tabs = new Roo.TabPanel("tabs1");
35650 tabs.addTab("script", "View Script");
35651 tabs.addTab("markup", "View Markup");
35652 tabs.activate("script");
35653
35654 // more advanced tabs, built from javascript
35655 var jtabs = new Roo.TabPanel("jtabs");
35656 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35657
35658 // set up the UpdateManager
35659 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35660 var updater = tab2.getUpdateManager();
35661 updater.setDefaultUrl("ajax1.htm");
35662 tab2.on('activate', updater.refresh, updater, true);
35663
35664 // Use setUrl for Ajax loading
35665 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35666 tab3.setUrl("ajax2.htm", null, true);
35667
35668 // Disabled tab
35669 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35670 tab4.disable();
35671
35672 jtabs.activate("jtabs-1");
35673  * </code></pre>
35674  * @constructor
35675  * Create a new TabPanel.
35676  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35677  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35678  */
35679 Roo.bootstrap.panel.Tabs = function(config){
35680     /**
35681     * The container element for this TabPanel.
35682     * @type Roo.Element
35683     */
35684     this.el = Roo.get(config.el);
35685     delete config.el;
35686     if(config){
35687         if(typeof config == "boolean"){
35688             this.tabPosition = config ? "bottom" : "top";
35689         }else{
35690             Roo.apply(this, config);
35691         }
35692     }
35693     
35694     if(this.tabPosition == "bottom"){
35695         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35696         this.el.addClass("roo-tabs-bottom");
35697     }
35698     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35699     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35700     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35701     if(Roo.isIE){
35702         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35703     }
35704     if(this.tabPosition != "bottom"){
35705         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35706          * @type Roo.Element
35707          */
35708         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35709         this.el.addClass("roo-tabs-top");
35710     }
35711     this.items = [];
35712
35713     this.bodyEl.setStyle("position", "relative");
35714
35715     this.active = null;
35716     this.activateDelegate = this.activate.createDelegate(this);
35717
35718     this.addEvents({
35719         /**
35720          * @event tabchange
35721          * Fires when the active tab changes
35722          * @param {Roo.TabPanel} this
35723          * @param {Roo.TabPanelItem} activePanel The new active tab
35724          */
35725         "tabchange": true,
35726         /**
35727          * @event beforetabchange
35728          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35729          * @param {Roo.TabPanel} this
35730          * @param {Object} e Set cancel to true on this object to cancel the tab change
35731          * @param {Roo.TabPanelItem} tab The tab being changed to
35732          */
35733         "beforetabchange" : true
35734     });
35735
35736     Roo.EventManager.onWindowResize(this.onResize, this);
35737     this.cpad = this.el.getPadding("lr");
35738     this.hiddenCount = 0;
35739
35740
35741     // toolbar on the tabbar support...
35742     if (this.toolbar) {
35743         alert("no toolbar support yet");
35744         this.toolbar  = false;
35745         /*
35746         var tcfg = this.toolbar;
35747         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35748         this.toolbar = new Roo.Toolbar(tcfg);
35749         if (Roo.isSafari) {
35750             var tbl = tcfg.container.child('table', true);
35751             tbl.setAttribute('width', '100%');
35752         }
35753         */
35754         
35755     }
35756    
35757
35758
35759     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35760 };
35761
35762 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35763     /*
35764      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35765      */
35766     tabPosition : "top",
35767     /*
35768      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35769      */
35770     currentTabWidth : 0,
35771     /*
35772      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35773      */
35774     minTabWidth : 40,
35775     /*
35776      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35777      */
35778     maxTabWidth : 250,
35779     /*
35780      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35781      */
35782     preferredTabWidth : 175,
35783     /*
35784      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35785      */
35786     resizeTabs : false,
35787     /*
35788      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35789      */
35790     monitorResize : true,
35791     /*
35792      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35793      */
35794     toolbar : false,
35795
35796     /**
35797      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35798      * @param {String} id The id of the div to use <b>or create</b>
35799      * @param {String} text The text for the tab
35800      * @param {String} content (optional) Content to put in the TabPanelItem body
35801      * @param {Boolean} closable (optional) True to create a close icon on the tab
35802      * @return {Roo.TabPanelItem} The created TabPanelItem
35803      */
35804     addTab : function(id, text, content, closable, tpl)
35805     {
35806         var item = new Roo.bootstrap.panel.TabItem({
35807             panel: this,
35808             id : id,
35809             text : text,
35810             closable : closable,
35811             tpl : tpl
35812         });
35813         this.addTabItem(item);
35814         if(content){
35815             item.setContent(content);
35816         }
35817         return item;
35818     },
35819
35820     /**
35821      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35822      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35823      * @return {Roo.TabPanelItem}
35824      */
35825     getTab : function(id){
35826         return this.items[id];
35827     },
35828
35829     /**
35830      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35831      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35832      */
35833     hideTab : function(id){
35834         var t = this.items[id];
35835         if(!t.isHidden()){
35836            t.setHidden(true);
35837            this.hiddenCount++;
35838            this.autoSizeTabs();
35839         }
35840     },
35841
35842     /**
35843      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35844      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35845      */
35846     unhideTab : function(id){
35847         var t = this.items[id];
35848         if(t.isHidden()){
35849            t.setHidden(false);
35850            this.hiddenCount--;
35851            this.autoSizeTabs();
35852         }
35853     },
35854
35855     /**
35856      * Adds an existing {@link Roo.TabPanelItem}.
35857      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35858      */
35859     addTabItem : function(item){
35860         this.items[item.id] = item;
35861         this.items.push(item);
35862       //  if(this.resizeTabs){
35863     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35864   //         this.autoSizeTabs();
35865 //        }else{
35866 //            item.autoSize();
35867        // }
35868     },
35869
35870     /**
35871      * Removes a {@link Roo.TabPanelItem}.
35872      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35873      */
35874     removeTab : function(id){
35875         var items = this.items;
35876         var tab = items[id];
35877         if(!tab) { return; }
35878         var index = items.indexOf(tab);
35879         if(this.active == tab && items.length > 1){
35880             var newTab = this.getNextAvailable(index);
35881             if(newTab) {
35882                 newTab.activate();
35883             }
35884         }
35885         this.stripEl.dom.removeChild(tab.pnode.dom);
35886         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35887             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35888         }
35889         items.splice(index, 1);
35890         delete this.items[tab.id];
35891         tab.fireEvent("close", tab);
35892         tab.purgeListeners();
35893         this.autoSizeTabs();
35894     },
35895
35896     getNextAvailable : function(start){
35897         var items = this.items;
35898         var index = start;
35899         // look for a next tab that will slide over to
35900         // replace the one being removed
35901         while(index < items.length){
35902             var item = items[++index];
35903             if(item && !item.isHidden()){
35904                 return item;
35905             }
35906         }
35907         // if one isn't found select the previous tab (on the left)
35908         index = start;
35909         while(index >= 0){
35910             var item = items[--index];
35911             if(item && !item.isHidden()){
35912                 return item;
35913             }
35914         }
35915         return null;
35916     },
35917
35918     /**
35919      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35920      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35921      */
35922     disableTab : function(id){
35923         var tab = this.items[id];
35924         if(tab && this.active != tab){
35925             tab.disable();
35926         }
35927     },
35928
35929     /**
35930      * Enables a {@link Roo.TabPanelItem} that is disabled.
35931      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35932      */
35933     enableTab : function(id){
35934         var tab = this.items[id];
35935         tab.enable();
35936     },
35937
35938     /**
35939      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35940      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35941      * @return {Roo.TabPanelItem} The TabPanelItem.
35942      */
35943     activate : function(id){
35944         var tab = this.items[id];
35945         if(!tab){
35946             return null;
35947         }
35948         if(tab == this.active || tab.disabled){
35949             return tab;
35950         }
35951         var e = {};
35952         this.fireEvent("beforetabchange", this, e, tab);
35953         if(e.cancel !== true && !tab.disabled){
35954             if(this.active){
35955                 this.active.hide();
35956             }
35957             this.active = this.items[id];
35958             this.active.show();
35959             this.fireEvent("tabchange", this, this.active);
35960         }
35961         return tab;
35962     },
35963
35964     /**
35965      * Gets the active {@link Roo.TabPanelItem}.
35966      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35967      */
35968     getActiveTab : function(){
35969         return this.active;
35970     },
35971
35972     /**
35973      * Updates the tab body element to fit the height of the container element
35974      * for overflow scrolling
35975      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35976      */
35977     syncHeight : function(targetHeight){
35978         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35979         var bm = this.bodyEl.getMargins();
35980         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35981         this.bodyEl.setHeight(newHeight);
35982         return newHeight;
35983     },
35984
35985     onResize : function(){
35986         if(this.monitorResize){
35987             this.autoSizeTabs();
35988         }
35989     },
35990
35991     /**
35992      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35993      */
35994     beginUpdate : function(){
35995         this.updating = true;
35996     },
35997
35998     /**
35999      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36000      */
36001     endUpdate : function(){
36002         this.updating = false;
36003         this.autoSizeTabs();
36004     },
36005
36006     /**
36007      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36008      */
36009     autoSizeTabs : function(){
36010         var count = this.items.length;
36011         var vcount = count - this.hiddenCount;
36012         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36013             return;
36014         }
36015         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36016         var availWidth = Math.floor(w / vcount);
36017         var b = this.stripBody;
36018         if(b.getWidth() > w){
36019             var tabs = this.items;
36020             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36021             if(availWidth < this.minTabWidth){
36022                 /*if(!this.sleft){    // incomplete scrolling code
36023                     this.createScrollButtons();
36024                 }
36025                 this.showScroll();
36026                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36027             }
36028         }else{
36029             if(this.currentTabWidth < this.preferredTabWidth){
36030                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36031             }
36032         }
36033     },
36034
36035     /**
36036      * Returns the number of tabs in this TabPanel.
36037      * @return {Number}
36038      */
36039      getCount : function(){
36040          return this.items.length;
36041      },
36042
36043     /**
36044      * Resizes all the tabs to the passed width
36045      * @param {Number} The new width
36046      */
36047     setTabWidth : function(width){
36048         this.currentTabWidth = width;
36049         for(var i = 0, len = this.items.length; i < len; i++) {
36050                 if(!this.items[i].isHidden()) {
36051                 this.items[i].setWidth(width);
36052             }
36053         }
36054     },
36055
36056     /**
36057      * Destroys this TabPanel
36058      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36059      */
36060     destroy : function(removeEl){
36061         Roo.EventManager.removeResizeListener(this.onResize, this);
36062         for(var i = 0, len = this.items.length; i < len; i++){
36063             this.items[i].purgeListeners();
36064         }
36065         if(removeEl === true){
36066             this.el.update("");
36067             this.el.remove();
36068         }
36069     },
36070     
36071     createStrip : function(container)
36072     {
36073         var strip = document.createElement("nav");
36074         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36075         container.appendChild(strip);
36076         return strip;
36077     },
36078     
36079     createStripList : function(strip)
36080     {
36081         // div wrapper for retard IE
36082         // returns the "tr" element.
36083         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36084         //'<div class="x-tabs-strip-wrap">'+
36085           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36086           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36087         return strip.firstChild; //.firstChild.firstChild.firstChild;
36088     },
36089     createBody : function(container)
36090     {
36091         var body = document.createElement("div");
36092         Roo.id(body, "tab-body");
36093         //Roo.fly(body).addClass("x-tabs-body");
36094         Roo.fly(body).addClass("tab-content");
36095         container.appendChild(body);
36096         return body;
36097     },
36098     createItemBody :function(bodyEl, id){
36099         var body = Roo.getDom(id);
36100         if(!body){
36101             body = document.createElement("div");
36102             body.id = id;
36103         }
36104         //Roo.fly(body).addClass("x-tabs-item-body");
36105         Roo.fly(body).addClass("tab-pane");
36106          bodyEl.insertBefore(body, bodyEl.firstChild);
36107         return body;
36108     },
36109     /** @private */
36110     createStripElements :  function(stripEl, text, closable, tpl)
36111     {
36112         var td = document.createElement("li"); // was td..
36113         
36114         
36115         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36116         
36117         
36118         stripEl.appendChild(td);
36119         /*if(closable){
36120             td.className = "x-tabs-closable";
36121             if(!this.closeTpl){
36122                 this.closeTpl = new Roo.Template(
36123                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36124                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36125                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36126                 );
36127             }
36128             var el = this.closeTpl.overwrite(td, {"text": text});
36129             var close = el.getElementsByTagName("div")[0];
36130             var inner = el.getElementsByTagName("em")[0];
36131             return {"el": el, "close": close, "inner": inner};
36132         } else {
36133         */
36134         // not sure what this is..
36135 //            if(!this.tabTpl){
36136                 //this.tabTpl = new Roo.Template(
36137                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36138                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36139                 //);
36140 //                this.tabTpl = new Roo.Template(
36141 //                   '<a href="#">' +
36142 //                   '<span unselectable="on"' +
36143 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36144 //                            ' >{text}</span></a>'
36145 //                );
36146 //                
36147 //            }
36148
36149
36150             var template = tpl || this.tabTpl || false;
36151             
36152             if(!template){
36153                 
36154                 template = new Roo.Template(
36155                    '<a href="#">' +
36156                    '<span unselectable="on"' +
36157                             (this.disableTooltips ? '' : ' title="{text}"') +
36158                             ' >{text}</span></a>'
36159                 );
36160             }
36161             
36162             switch (typeof(template)) {
36163                 case 'object' :
36164                     break;
36165                 case 'string' :
36166                     template = new Roo.Template(template);
36167                     break;
36168                 default :
36169                     break;
36170             }
36171             
36172             var el = template.overwrite(td, {"text": text});
36173             
36174             var inner = el.getElementsByTagName("span")[0];
36175             
36176             return {"el": el, "inner": inner};
36177             
36178     }
36179         
36180     
36181 });
36182
36183 /**
36184  * @class Roo.TabPanelItem
36185  * @extends Roo.util.Observable
36186  * Represents an individual item (tab plus body) in a TabPanel.
36187  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36188  * @param {String} id The id of this TabPanelItem
36189  * @param {String} text The text for the tab of this TabPanelItem
36190  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36191  */
36192 Roo.bootstrap.panel.TabItem = function(config){
36193     /**
36194      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36195      * @type Roo.TabPanel
36196      */
36197     this.tabPanel = config.panel;
36198     /**
36199      * The id for this TabPanelItem
36200      * @type String
36201      */
36202     this.id = config.id;
36203     /** @private */
36204     this.disabled = false;
36205     /** @private */
36206     this.text = config.text;
36207     /** @private */
36208     this.loaded = false;
36209     this.closable = config.closable;
36210
36211     /**
36212      * The body element for this TabPanelItem.
36213      * @type Roo.Element
36214      */
36215     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36216     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36217     this.bodyEl.setStyle("display", "block");
36218     this.bodyEl.setStyle("zoom", "1");
36219     //this.hideAction();
36220
36221     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36222     /** @private */
36223     this.el = Roo.get(els.el);
36224     this.inner = Roo.get(els.inner, true);
36225     this.textEl = Roo.get(this.el.dom.firstChild, true);
36226     this.pnode = Roo.get(els.el.parentNode, true);
36227     this.el.on("mousedown", this.onTabMouseDown, this);
36228     this.el.on("click", this.onTabClick, this);
36229     /** @private */
36230     if(config.closable){
36231         var c = Roo.get(els.close, true);
36232         c.dom.title = this.closeText;
36233         c.addClassOnOver("close-over");
36234         c.on("click", this.closeClick, this);
36235      }
36236
36237     this.addEvents({
36238          /**
36239          * @event activate
36240          * Fires when this tab becomes the active tab.
36241          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36242          * @param {Roo.TabPanelItem} this
36243          */
36244         "activate": true,
36245         /**
36246          * @event beforeclose
36247          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36248          * @param {Roo.TabPanelItem} this
36249          * @param {Object} e Set cancel to true on this object to cancel the close.
36250          */
36251         "beforeclose": true,
36252         /**
36253          * @event close
36254          * Fires when this tab is closed.
36255          * @param {Roo.TabPanelItem} this
36256          */
36257          "close": true,
36258         /**
36259          * @event deactivate
36260          * Fires when this tab is no longer the active tab.
36261          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36262          * @param {Roo.TabPanelItem} this
36263          */
36264          "deactivate" : true
36265     });
36266     this.hidden = false;
36267
36268     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36269 };
36270
36271 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36272            {
36273     purgeListeners : function(){
36274        Roo.util.Observable.prototype.purgeListeners.call(this);
36275        this.el.removeAllListeners();
36276     },
36277     /**
36278      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36279      */
36280     show : function(){
36281         this.pnode.addClass("active");
36282         this.showAction();
36283         if(Roo.isOpera){
36284             this.tabPanel.stripWrap.repaint();
36285         }
36286         this.fireEvent("activate", this.tabPanel, this);
36287     },
36288
36289     /**
36290      * Returns true if this tab is the active tab.
36291      * @return {Boolean}
36292      */
36293     isActive : function(){
36294         return this.tabPanel.getActiveTab() == this;
36295     },
36296
36297     /**
36298      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36299      */
36300     hide : function(){
36301         this.pnode.removeClass("active");
36302         this.hideAction();
36303         this.fireEvent("deactivate", this.tabPanel, this);
36304     },
36305
36306     hideAction : function(){
36307         this.bodyEl.hide();
36308         this.bodyEl.setStyle("position", "absolute");
36309         this.bodyEl.setLeft("-20000px");
36310         this.bodyEl.setTop("-20000px");
36311     },
36312
36313     showAction : function(){
36314         this.bodyEl.setStyle("position", "relative");
36315         this.bodyEl.setTop("");
36316         this.bodyEl.setLeft("");
36317         this.bodyEl.show();
36318     },
36319
36320     /**
36321      * Set the tooltip for the tab.
36322      * @param {String} tooltip The tab's tooltip
36323      */
36324     setTooltip : function(text){
36325         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36326             this.textEl.dom.qtip = text;
36327             this.textEl.dom.removeAttribute('title');
36328         }else{
36329             this.textEl.dom.title = text;
36330         }
36331     },
36332
36333     onTabClick : function(e){
36334         e.preventDefault();
36335         this.tabPanel.activate(this.id);
36336     },
36337
36338     onTabMouseDown : function(e){
36339         e.preventDefault();
36340         this.tabPanel.activate(this.id);
36341     },
36342 /*
36343     getWidth : function(){
36344         return this.inner.getWidth();
36345     },
36346
36347     setWidth : function(width){
36348         var iwidth = width - this.pnode.getPadding("lr");
36349         this.inner.setWidth(iwidth);
36350         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36351         this.pnode.setWidth(width);
36352     },
36353 */
36354     /**
36355      * Show or hide the tab
36356      * @param {Boolean} hidden True to hide or false to show.
36357      */
36358     setHidden : function(hidden){
36359         this.hidden = hidden;
36360         this.pnode.setStyle("display", hidden ? "none" : "");
36361     },
36362
36363     /**
36364      * Returns true if this tab is "hidden"
36365      * @return {Boolean}
36366      */
36367     isHidden : function(){
36368         return this.hidden;
36369     },
36370
36371     /**
36372      * Returns the text for this tab
36373      * @return {String}
36374      */
36375     getText : function(){
36376         return this.text;
36377     },
36378     /*
36379     autoSize : function(){
36380         //this.el.beginMeasure();
36381         this.textEl.setWidth(1);
36382         /*
36383          *  #2804 [new] Tabs in Roojs
36384          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36385          */
36386         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36387         //this.el.endMeasure();
36388     //},
36389
36390     /**
36391      * Sets the text for the tab (Note: this also sets the tooltip text)
36392      * @param {String} text The tab's text and tooltip
36393      */
36394     setText : function(text){
36395         this.text = text;
36396         this.textEl.update(text);
36397         this.setTooltip(text);
36398         //if(!this.tabPanel.resizeTabs){
36399         //    this.autoSize();
36400         //}
36401     },
36402     /**
36403      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36404      */
36405     activate : function(){
36406         this.tabPanel.activate(this.id);
36407     },
36408
36409     /**
36410      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36411      */
36412     disable : function(){
36413         if(this.tabPanel.active != this){
36414             this.disabled = true;
36415             this.pnode.addClass("disabled");
36416         }
36417     },
36418
36419     /**
36420      * Enables this TabPanelItem if it was previously disabled.
36421      */
36422     enable : function(){
36423         this.disabled = false;
36424         this.pnode.removeClass("disabled");
36425     },
36426
36427     /**
36428      * Sets the content for this TabPanelItem.
36429      * @param {String} content The content
36430      * @param {Boolean} loadScripts true to look for and load scripts
36431      */
36432     setContent : function(content, loadScripts){
36433         this.bodyEl.update(content, loadScripts);
36434     },
36435
36436     /**
36437      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36438      * @return {Roo.UpdateManager} The UpdateManager
36439      */
36440     getUpdateManager : function(){
36441         return this.bodyEl.getUpdateManager();
36442     },
36443
36444     /**
36445      * Set a URL to be used to load the content for this TabPanelItem.
36446      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36447      * @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)
36448      * @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)
36449      * @return {Roo.UpdateManager} The UpdateManager
36450      */
36451     setUrl : function(url, params, loadOnce){
36452         if(this.refreshDelegate){
36453             this.un('activate', this.refreshDelegate);
36454         }
36455         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36456         this.on("activate", this.refreshDelegate);
36457         return this.bodyEl.getUpdateManager();
36458     },
36459
36460     /** @private */
36461     _handleRefresh : function(url, params, loadOnce){
36462         if(!loadOnce || !this.loaded){
36463             var updater = this.bodyEl.getUpdateManager();
36464             updater.update(url, params, this._setLoaded.createDelegate(this));
36465         }
36466     },
36467
36468     /**
36469      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36470      *   Will fail silently if the setUrl method has not been called.
36471      *   This does not activate the panel, just updates its content.
36472      */
36473     refresh : function(){
36474         if(this.refreshDelegate){
36475            this.loaded = false;
36476            this.refreshDelegate();
36477         }
36478     },
36479
36480     /** @private */
36481     _setLoaded : function(){
36482         this.loaded = true;
36483     },
36484
36485     /** @private */
36486     closeClick : function(e){
36487         var o = {};
36488         e.stopEvent();
36489         this.fireEvent("beforeclose", this, o);
36490         if(o.cancel !== true){
36491             this.tabPanel.removeTab(this.id);
36492         }
36493     },
36494     /**
36495      * The text displayed in the tooltip for the close icon.
36496      * @type String
36497      */
36498     closeText : "Close this tab"
36499 });