docs/default.css
[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('zIndex', Roo.bootstrap.Modal.zIndex++);
2761         this.maskEl.show();
2762         this.el.setStyle('zIndex', Roo.bootstrap.Modal.zIndex++);
2763
2764         this.fireEvent('show', this);
2765
2766         this.resize();
2767
2768         (function () {
2769             this.items.forEach( function(e) {
2770                 e.layout ? e.layout() : false;
2771
2772             });
2773         }).defer(100,this);
2774
2775     },
2776     hide : function()
2777     {
2778         if(this.fireEvent("beforehide", this) !== false){
2779             this.maskEl.hide();
2780             Roo.get(document.body).removeClass("x-body-masked");
2781             this.el.removeClass('in');
2782             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2783
2784             if(this.animate){ // why
2785                 var _this = this;
2786                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2787             }else{
2788                 this.el.setStyle('display', 'none');
2789             }
2790             this.fireEvent('hide', this);
2791         }
2792     },
2793
2794     addButton : function(str, cb)
2795     {
2796
2797
2798         var b = Roo.apply({}, { html : str } );
2799         b.xns = b.xns || Roo.bootstrap;
2800         b.xtype = b.xtype || 'Button';
2801         if (typeof(b.listeners) == 'undefined') {
2802             b.listeners = { click : cb.createDelegate(this)  };
2803         }
2804
2805         var btn = Roo.factory(b);
2806
2807         btn.render(this.el.select('.modal-footer div').first());
2808
2809         return btn;
2810
2811     },
2812
2813     setDefaultButton : function(btn)
2814     {
2815         //this.el.select('.modal-footer').()
2816     },
2817     diff : false,
2818
2819     resizeTo: function(w,h)
2820     {
2821         // skip.. ?? why??
2822
2823         this.dialogEl.setWidth(w);
2824         if (this.diff === false) {
2825             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2826         }
2827
2828         this.bodyEl.setHeight(h-this.diff);
2829
2830
2831     },
2832     setContentSize  : function(w, h)
2833     {
2834
2835     },
2836     onButtonClick: function(btn,e)
2837     {
2838         //Roo.log([a,b,c]);
2839         this.fireEvent('btnclick', btn.name, e);
2840     },
2841      /**
2842      * Set the title of the Dialog
2843      * @param {String} str new Title
2844      */
2845     setTitle: function(str) {
2846         this.titleEl.dom.innerHTML = str;
2847     },
2848     /**
2849      * Set the body of the Dialog
2850      * @param {String} str new Title
2851      */
2852     setBody: function(str) {
2853         this.bodyEl.dom.innerHTML = str;
2854     },
2855     /**
2856      * Set the body of the Dialog using the template
2857      * @param {Obj} data - apply this data to the template and replace the body contents.
2858      */
2859     applyBody: function(obj)
2860     {
2861         if (!this.tmpl) {
2862             Roo.log("Error - using apply Body without a template");
2863             //code
2864         }
2865         this.tmpl.overwrite(this.bodyEl, obj);
2866     }
2867
2868 });
2869
2870
2871 Roo.apply(Roo.bootstrap.Modal,  {
2872     /**
2873          * Button config that displays a single OK button
2874          * @type Object
2875          */
2876         OK :  [{
2877             name : 'ok',
2878             weight : 'primary',
2879             html : 'OK'
2880         }],
2881         /**
2882          * Button config that displays Yes and No buttons
2883          * @type Object
2884          */
2885         YESNO : [
2886             {
2887                 name  : 'no',
2888                 html : 'No'
2889             },
2890             {
2891                 name  :'yes',
2892                 weight : 'primary',
2893                 html : 'Yes'
2894             }
2895         ],
2896
2897         /**
2898          * Button config that displays OK and Cancel buttons
2899          * @type Object
2900          */
2901         OKCANCEL : [
2902             {
2903                name : 'cancel',
2904                 html : 'Cancel'
2905             },
2906             {
2907                 name : 'ok',
2908                 weight : 'primary',
2909                 html : 'OK'
2910             }
2911         ],
2912         /**
2913          * Button config that displays Yes, No and Cancel buttons
2914          * @type Object
2915          */
2916         YESNOCANCEL : [
2917             {
2918                 name : 'yes',
2919                 weight : 'primary',
2920                 html : 'Yes'
2921             },
2922             {
2923                 name : 'no',
2924                 html : 'No'
2925             },
2926             {
2927                 name : 'cancel',
2928                 html : 'Cancel'
2929             }
2930         ],
2931         
2932         zIndex : 10001
2933 });
2934 /*
2935  * - LGPL
2936  *
2937  * messagebox - can be used as a replace
2938  * 
2939  */
2940 /**
2941  * @class Roo.MessageBox
2942  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2943  * Example usage:
2944  *<pre><code>
2945 // Basic alert:
2946 Roo.Msg.alert('Status', 'Changes saved successfully.');
2947
2948 // Prompt for user data:
2949 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2950     if (btn == 'ok'){
2951         // process text value...
2952     }
2953 });
2954
2955 // Show a dialog using config options:
2956 Roo.Msg.show({
2957    title:'Save Changes?',
2958    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2959    buttons: Roo.Msg.YESNOCANCEL,
2960    fn: processResult,
2961    animEl: 'elId'
2962 });
2963 </code></pre>
2964  * @singleton
2965  */
2966 Roo.bootstrap.MessageBox = function(){
2967     var dlg, opt, mask, waitTimer;
2968     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2969     var buttons, activeTextEl, bwidth;
2970
2971     
2972     // private
2973     var handleButton = function(button){
2974         dlg.hide();
2975         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2976     };
2977
2978     // private
2979     var handleHide = function(){
2980         if(opt && opt.cls){
2981             dlg.el.removeClass(opt.cls);
2982         }
2983         //if(waitTimer){
2984         //    Roo.TaskMgr.stop(waitTimer);
2985         //    waitTimer = null;
2986         //}
2987     };
2988
2989     // private
2990     var updateButtons = function(b){
2991         var width = 0;
2992         if(!b){
2993             buttons["ok"].hide();
2994             buttons["cancel"].hide();
2995             buttons["yes"].hide();
2996             buttons["no"].hide();
2997             //dlg.footer.dom.style.display = 'none';
2998             return width;
2999         }
3000         dlg.footerEl.dom.style.display = '';
3001         for(var k in buttons){
3002             if(typeof buttons[k] != "function"){
3003                 if(b[k]){
3004                     buttons[k].show();
3005                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3006                     width += buttons[k].el.getWidth()+15;
3007                 }else{
3008                     buttons[k].hide();
3009                 }
3010             }
3011         }
3012         return width;
3013     };
3014
3015     // private
3016     var handleEsc = function(d, k, e){
3017         if(opt && opt.closable !== false){
3018             dlg.hide();
3019         }
3020         if(e){
3021             e.stopEvent();
3022         }
3023     };
3024
3025     return {
3026         /**
3027          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3028          * @return {Roo.BasicDialog} The BasicDialog element
3029          */
3030         getDialog : function(){
3031            if(!dlg){
3032                 dlg = new Roo.bootstrap.Modal( {
3033                     //draggable: true,
3034                     //resizable:false,
3035                     //constraintoviewport:false,
3036                     //fixedcenter:true,
3037                     //collapsible : false,
3038                     //shim:true,
3039                     //modal: true,
3040                   //  width:400,
3041                   //  height:100,
3042                     //buttonAlign:"center",
3043                     closeClick : function(){
3044                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3045                             handleButton("no");
3046                         }else{
3047                             handleButton("cancel");
3048                         }
3049                     }
3050                 });
3051                 dlg.render();
3052                 dlg.on("hide", handleHide);
3053                 mask = dlg.mask;
3054                 //dlg.addKeyListener(27, handleEsc);
3055                 buttons = {};
3056                 this.buttons = buttons;
3057                 var bt = this.buttonText;
3058                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3059                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3060                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3061                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3062                 //Roo.log(buttons);
3063                 bodyEl = dlg.bodyEl.createChild({
3064
3065                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3066                         '<textarea class="roo-mb-textarea"></textarea>' +
3067                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3068                 });
3069                 msgEl = bodyEl.dom.firstChild;
3070                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3071                 textboxEl.enableDisplayMode();
3072                 textboxEl.addKeyListener([10,13], function(){
3073                     if(dlg.isVisible() && opt && opt.buttons){
3074                         if(opt.buttons.ok){
3075                             handleButton("ok");
3076                         }else if(opt.buttons.yes){
3077                             handleButton("yes");
3078                         }
3079                     }
3080                 });
3081                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3082                 textareaEl.enableDisplayMode();
3083                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3084                 progressEl.enableDisplayMode();
3085                 
3086                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3087                 //var pf = progressEl.dom.firstChild;
3088                 //if (pf) {
3089                     //pp = Roo.get(pf.firstChild);
3090                     //pp.setHeight(pf.offsetHeight);
3091                 //}
3092                 
3093             }
3094             return dlg;
3095         },
3096
3097         /**
3098          * Updates the message box body text
3099          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3100          * the XHTML-compliant non-breaking space character '&amp;#160;')
3101          * @return {Roo.MessageBox} This message box
3102          */
3103         updateText : function(text)
3104         {
3105             if(!dlg.isVisible() && !opt.width){
3106                 dlg.dialogEl.setWidth(this.maxWidth);
3107                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3108             }
3109             msgEl.innerHTML = text || '&#160;';
3110       
3111             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3112             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3113             var w = Math.max(
3114                     Math.min(opt.width || cw , this.maxWidth), 
3115                     Math.max(opt.minWidth || this.minWidth, bwidth)
3116             );
3117             if(opt.prompt){
3118                 activeTextEl.setWidth(w);
3119             }
3120             if(dlg.isVisible()){
3121                 dlg.fixedcenter = false;
3122             }
3123             // to big, make it scroll. = But as usual stupid IE does not support
3124             // !important..
3125             
3126             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3127                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3128                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3129             } else {
3130                 bodyEl.dom.style.height = '';
3131                 bodyEl.dom.style.overflowY = '';
3132             }
3133             if (cw > w) {
3134                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3135             } else {
3136                 bodyEl.dom.style.overflowX = '';
3137             }
3138             
3139             dlg.setContentSize(w, bodyEl.getHeight());
3140             if(dlg.isVisible()){
3141                 dlg.fixedcenter = true;
3142             }
3143             return this;
3144         },
3145
3146         /**
3147          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3148          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3149          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3150          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3151          * @return {Roo.MessageBox} This message box
3152          */
3153         updateProgress : function(value, text){
3154             if(text){
3155                 this.updateText(text);
3156             }
3157             if (pp) { // weird bug on my firefox - for some reason this is not defined
3158                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3159             }
3160             return this;
3161         },        
3162
3163         /**
3164          * Returns true if the message box is currently displayed
3165          * @return {Boolean} True if the message box is visible, else false
3166          */
3167         isVisible : function(){
3168             return dlg && dlg.isVisible();  
3169         },
3170
3171         /**
3172          * Hides the message box if it is displayed
3173          */
3174         hide : function(){
3175             if(this.isVisible()){
3176                 dlg.hide();
3177             }  
3178         },
3179
3180         /**
3181          * Displays a new message box, or reinitializes an existing message box, based on the config options
3182          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3183          * The following config object properties are supported:
3184          * <pre>
3185 Property    Type             Description
3186 ----------  ---------------  ------------------------------------------------------------------------------------
3187 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3188                                    closes (defaults to undefined)
3189 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3190                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3191 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3192                                    progress and wait dialogs will ignore this property and always hide the
3193                                    close button as they can only be closed programmatically.
3194 cls               String           A custom CSS class to apply to the message box element
3195 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3196                                    displayed (defaults to 75)
3197 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3198                                    function will be btn (the name of the button that was clicked, if applicable,
3199                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3200                                    Progress and wait dialogs will ignore this option since they do not respond to
3201                                    user actions and can only be closed programmatically, so any required function
3202                                    should be called by the same code after it closes the dialog.
3203 icon              String           A CSS class that provides a background image to be used as an icon for
3204                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3205 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3206 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3207 modal             Boolean          False to allow user interaction with the page while the message box is
3208                                    displayed (defaults to true)
3209 msg               String           A string that will replace the existing message box body text (defaults
3210                                    to the XHTML-compliant non-breaking space character '&#160;')
3211 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3212 progress          Boolean          True to display a progress bar (defaults to false)
3213 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3214 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3215 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3216 title             String           The title text
3217 value             String           The string value to set into the active textbox element if displayed
3218 wait              Boolean          True to display a progress bar (defaults to false)
3219 width             Number           The width of the dialog in pixels
3220 </pre>
3221          *
3222          * Example usage:
3223          * <pre><code>
3224 Roo.Msg.show({
3225    title: 'Address',
3226    msg: 'Please enter your address:',
3227    width: 300,
3228    buttons: Roo.MessageBox.OKCANCEL,
3229    multiline: true,
3230    fn: saveAddress,
3231    animEl: 'addAddressBtn'
3232 });
3233 </code></pre>
3234          * @param {Object} config Configuration options
3235          * @return {Roo.MessageBox} This message box
3236          */
3237         show : function(options)
3238         {
3239             
3240             // this causes nightmares if you show one dialog after another
3241             // especially on callbacks..
3242              
3243             if(this.isVisible()){
3244                 
3245                 this.hide();
3246                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3247                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3248                 Roo.log("New Dialog Message:" +  options.msg )
3249                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3250                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3251                 
3252             }
3253             var d = this.getDialog();
3254             opt = options;
3255             d.setTitle(opt.title || "&#160;");
3256             d.closeEl.setDisplayed(opt.closable !== false);
3257             activeTextEl = textboxEl;
3258             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3259             if(opt.prompt){
3260                 if(opt.multiline){
3261                     textboxEl.hide();
3262                     textareaEl.show();
3263                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3264                         opt.multiline : this.defaultTextHeight);
3265                     activeTextEl = textareaEl;
3266                 }else{
3267                     textboxEl.show();
3268                     textareaEl.hide();
3269                 }
3270             }else{
3271                 textboxEl.hide();
3272                 textareaEl.hide();
3273             }
3274             progressEl.setDisplayed(opt.progress === true);
3275             this.updateProgress(0);
3276             activeTextEl.dom.value = opt.value || "";
3277             if(opt.prompt){
3278                 dlg.setDefaultButton(activeTextEl);
3279             }else{
3280                 var bs = opt.buttons;
3281                 var db = null;
3282                 if(bs && bs.ok){
3283                     db = buttons["ok"];
3284                 }else if(bs && bs.yes){
3285                     db = buttons["yes"];
3286                 }
3287                 dlg.setDefaultButton(db);
3288             }
3289             bwidth = updateButtons(opt.buttons);
3290             this.updateText(opt.msg);
3291             if(opt.cls){
3292                 d.el.addClass(opt.cls);
3293             }
3294             d.proxyDrag = opt.proxyDrag === true;
3295             d.modal = opt.modal !== false;
3296             d.mask = opt.modal !== false ? mask : false;
3297             if(!d.isVisible()){
3298                 // force it to the end of the z-index stack so it gets a cursor in FF
3299                 document.body.appendChild(dlg.el.dom);
3300                 d.animateTarget = null;
3301                 d.show(options.animEl);
3302             }
3303             return this;
3304         },
3305
3306         /**
3307          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3308          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3309          * and closing the message box when the process is complete.
3310          * @param {String} title The title bar text
3311          * @param {String} msg The message box body text
3312          * @return {Roo.MessageBox} This message box
3313          */
3314         progress : function(title, msg){
3315             this.show({
3316                 title : title,
3317                 msg : msg,
3318                 buttons: false,
3319                 progress:true,
3320                 closable:false,
3321                 minWidth: this.minProgressWidth,
3322                 modal : true
3323             });
3324             return this;
3325         },
3326
3327         /**
3328          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3329          * If a callback function is passed it will be called after the user clicks the button, and the
3330          * id of the button that was clicked will be passed as the only parameter to the callback
3331          * (could also be the top-right close button).
3332          * @param {String} title The title bar text
3333          * @param {String} msg The message box body text
3334          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3335          * @param {Object} scope (optional) The scope of the callback function
3336          * @return {Roo.MessageBox} This message box
3337          */
3338         alert : function(title, msg, fn, scope)
3339         {
3340             this.show({
3341                 title : title,
3342                 msg : msg,
3343                 buttons: this.OK,
3344                 fn: fn,
3345                 closable : false,
3346                 scope : scope,
3347                 modal : true
3348             });
3349             return this;
3350         },
3351
3352         /**
3353          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3354          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3355          * You are responsible for closing the message box when the process is complete.
3356          * @param {String} msg The message box body text
3357          * @param {String} title (optional) The title bar text
3358          * @return {Roo.MessageBox} This message box
3359          */
3360         wait : function(msg, title){
3361             this.show({
3362                 title : title,
3363                 msg : msg,
3364                 buttons: false,
3365                 closable:false,
3366                 progress:true,
3367                 modal:true,
3368                 width:300,
3369                 wait:true
3370             });
3371             waitTimer = Roo.TaskMgr.start({
3372                 run: function(i){
3373                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3374                 },
3375                 interval: 1000
3376             });
3377             return this;
3378         },
3379
3380         /**
3381          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3382          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3383          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3384          * @param {String} title The title bar text
3385          * @param {String} msg The message box body text
3386          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3387          * @param {Object} scope (optional) The scope of the callback function
3388          * @return {Roo.MessageBox} This message box
3389          */
3390         confirm : function(title, msg, fn, scope){
3391             this.show({
3392                 title : title,
3393                 msg : msg,
3394                 buttons: this.YESNO,
3395                 fn: fn,
3396                 scope : scope,
3397                 modal : true
3398             });
3399             return this;
3400         },
3401
3402         /**
3403          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3404          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3405          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3406          * (could also be the top-right close button) and the text that was entered will be passed as the two
3407          * parameters to the callback.
3408          * @param {String} title The title bar text
3409          * @param {String} msg The message box body text
3410          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3411          * @param {Object} scope (optional) The scope of the callback function
3412          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3413          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3414          * @return {Roo.MessageBox} This message box
3415          */
3416         prompt : function(title, msg, fn, scope, multiline){
3417             this.show({
3418                 title : title,
3419                 msg : msg,
3420                 buttons: this.OKCANCEL,
3421                 fn: fn,
3422                 minWidth:250,
3423                 scope : scope,
3424                 prompt:true,
3425                 multiline: multiline,
3426                 modal : true
3427             });
3428             return this;
3429         },
3430
3431         /**
3432          * Button config that displays a single OK button
3433          * @type Object
3434          */
3435         OK : {ok:true},
3436         /**
3437          * Button config that displays Yes and No buttons
3438          * @type Object
3439          */
3440         YESNO : {yes:true, no:true},
3441         /**
3442          * Button config that displays OK and Cancel buttons
3443          * @type Object
3444          */
3445         OKCANCEL : {ok:true, cancel:true},
3446         /**
3447          * Button config that displays Yes, No and Cancel buttons
3448          * @type Object
3449          */
3450         YESNOCANCEL : {yes:true, no:true, cancel:true},
3451
3452         /**
3453          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3454          * @type Number
3455          */
3456         defaultTextHeight : 75,
3457         /**
3458          * The maximum width in pixels of the message box (defaults to 600)
3459          * @type Number
3460          */
3461         maxWidth : 600,
3462         /**
3463          * The minimum width in pixels of the message box (defaults to 100)
3464          * @type Number
3465          */
3466         minWidth : 100,
3467         /**
3468          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3469          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3470          * @type Number
3471          */
3472         minProgressWidth : 250,
3473         /**
3474          * An object containing the default button text strings that can be overriden for localized language support.
3475          * Supported properties are: ok, cancel, yes and no.
3476          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3477          * @type Object
3478          */
3479         buttonText : {
3480             ok : "OK",
3481             cancel : "Cancel",
3482             yes : "Yes",
3483             no : "No"
3484         }
3485     };
3486 }();
3487
3488 /**
3489  * Shorthand for {@link Roo.MessageBox}
3490  */
3491 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3492 Roo.Msg = Roo.Msg || Roo.MessageBox;
3493 /*
3494  * - LGPL
3495  *
3496  * navbar
3497  * 
3498  */
3499
3500 /**
3501  * @class Roo.bootstrap.Navbar
3502  * @extends Roo.bootstrap.Component
3503  * Bootstrap Navbar class
3504
3505  * @constructor
3506  * Create a new Navbar
3507  * @param {Object} config The config object
3508  */
3509
3510
3511 Roo.bootstrap.Navbar = function(config){
3512     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3513     this.addEvents({
3514         // raw events
3515         /**
3516          * @event beforetoggle
3517          * Fire before toggle the menu
3518          * @param {Roo.EventObject} e
3519          */
3520         "beforetoggle" : true
3521     });
3522 };
3523
3524 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3525     
3526     
3527    
3528     // private
3529     navItems : false,
3530     loadMask : false,
3531     
3532     
3533     getAutoCreate : function(){
3534         
3535         
3536         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3537         
3538     },
3539     
3540     initEvents :function ()
3541     {
3542         //Roo.log(this.el.select('.navbar-toggle',true));
3543         this.el.select('.navbar-toggle',true).on('click', function() {
3544             if(this.fireEvent('beforetoggle', this) !== false){
3545                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3546             }
3547             
3548         }, this);
3549         
3550         var mark = {
3551             tag: "div",
3552             cls:"x-dlg-mask"
3553         };
3554         
3555         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3556         
3557         var size = this.el.getSize();
3558         this.maskEl.setSize(size.width, size.height);
3559         this.maskEl.enableDisplayMode("block");
3560         this.maskEl.hide();
3561         
3562         if(this.loadMask){
3563             this.maskEl.show();
3564         }
3565     },
3566     
3567     
3568     getChildContainer : function()
3569     {
3570         if (this.el.select('.collapse').getCount()) {
3571             return this.el.select('.collapse',true).first();
3572         }
3573         
3574         return this.el;
3575     },
3576     
3577     mask : function()
3578     {
3579         this.maskEl.show();
3580     },
3581     
3582     unmask : function()
3583     {
3584         this.maskEl.hide();
3585     } 
3586     
3587     
3588     
3589     
3590 });
3591
3592
3593
3594  
3595
3596  /*
3597  * - LGPL
3598  *
3599  * navbar
3600  * 
3601  */
3602
3603 /**
3604  * @class Roo.bootstrap.NavSimplebar
3605  * @extends Roo.bootstrap.Navbar
3606  * Bootstrap Sidebar class
3607  *
3608  * @cfg {Boolean} inverse is inverted color
3609  * 
3610  * @cfg {String} type (nav | pills | tabs)
3611  * @cfg {Boolean} arrangement stacked | justified
3612  * @cfg {String} align (left | right) alignment
3613  * 
3614  * @cfg {Boolean} main (true|false) main nav bar? default false
3615  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3616  * 
3617  * @cfg {String} tag (header|footer|nav|div) default is nav 
3618
3619  * 
3620  * 
3621  * 
3622  * @constructor
3623  * Create a new Sidebar
3624  * @param {Object} config The config object
3625  */
3626
3627
3628 Roo.bootstrap.NavSimplebar = function(config){
3629     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3630 };
3631
3632 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3633     
3634     inverse: false,
3635     
3636     type: false,
3637     arrangement: '',
3638     align : false,
3639     
3640     
3641     
3642     main : false,
3643     
3644     
3645     tag : false,
3646     
3647     
3648     getAutoCreate : function(){
3649         
3650         
3651         var cfg = {
3652             tag : this.tag || 'div',
3653             cls : 'navbar'
3654         };
3655           
3656         
3657         cfg.cn = [
3658             {
3659                 cls: 'nav',
3660                 tag : 'ul'
3661             }
3662         ];
3663         
3664          
3665         this.type = this.type || 'nav';
3666         if (['tabs','pills'].indexOf(this.type)!==-1) {
3667             cfg.cn[0].cls += ' nav-' + this.type
3668         
3669         
3670         } else {
3671             if (this.type!=='nav') {
3672                 Roo.log('nav type must be nav/tabs/pills')
3673             }
3674             cfg.cn[0].cls += ' navbar-nav'
3675         }
3676         
3677         
3678         
3679         
3680         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3681             cfg.cn[0].cls += ' nav-' + this.arrangement;
3682         }
3683         
3684         
3685         if (this.align === 'right') {
3686             cfg.cn[0].cls += ' navbar-right';
3687         }
3688         
3689         if (this.inverse) {
3690             cfg.cls += ' navbar-inverse';
3691             
3692         }
3693         
3694         
3695         return cfg;
3696     
3697         
3698     }
3699     
3700     
3701     
3702 });
3703
3704
3705
3706  
3707
3708  
3709        /*
3710  * - LGPL
3711  *
3712  * navbar
3713  * 
3714  */
3715
3716 /**
3717  * @class Roo.bootstrap.NavHeaderbar
3718  * @extends Roo.bootstrap.NavSimplebar
3719  * Bootstrap Sidebar class
3720  *
3721  * @cfg {String} brand what is brand
3722  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3723  * @cfg {String} brand_href href of the brand
3724  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3725  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3726  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3727  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3728  * 
3729  * @constructor
3730  * Create a new Sidebar
3731  * @param {Object} config The config object
3732  */
3733
3734
3735 Roo.bootstrap.NavHeaderbar = function(config){
3736     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3737       
3738 };
3739
3740 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3741     
3742     position: '',
3743     brand: '',
3744     brand_href: false,
3745     srButton : true,
3746     autohide : false,
3747     desktopCenter : false,
3748    
3749     
3750     getAutoCreate : function(){
3751         
3752         var   cfg = {
3753             tag: this.nav || 'nav',
3754             cls: 'navbar',
3755             role: 'navigation',
3756             cn: []
3757         };
3758         
3759         var cn = cfg.cn;
3760         if (this.desktopCenter) {
3761             cn.push({cls : 'container', cn : []});
3762             cn = cn[0].cn;
3763         }
3764         
3765         if(this.srButton){
3766             cn.push({
3767                 tag: 'div',
3768                 cls: 'navbar-header',
3769                 cn: [
3770                     {
3771                         tag: 'button',
3772                         type: 'button',
3773                         cls: 'navbar-toggle',
3774                         'data-toggle': 'collapse',
3775                         cn: [
3776                             {
3777                                 tag: 'span',
3778                                 cls: 'sr-only',
3779                                 html: 'Toggle navigation'
3780                             },
3781                             {
3782                                 tag: 'span',
3783                                 cls: 'icon-bar'
3784                             },
3785                             {
3786                                 tag: 'span',
3787                                 cls: 'icon-bar'
3788                             },
3789                             {
3790                                 tag: 'span',
3791                                 cls: 'icon-bar'
3792                             }
3793                         ]
3794                     }
3795                 ]
3796             });
3797         }
3798         
3799         cn.push({
3800             tag: 'div',
3801             cls: 'collapse navbar-collapse',
3802             cn : []
3803         });
3804         
3805         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3806         
3807         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3808             cfg.cls += ' navbar-' + this.position;
3809             
3810             // tag can override this..
3811             
3812             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3813         }
3814         
3815         if (this.brand !== '') {
3816             cn[0].cn.push({
3817                 tag: 'a',
3818                 href: this.brand_href ? this.brand_href : '#',
3819                 cls: 'navbar-brand',
3820                 cn: [
3821                 this.brand
3822                 ]
3823             });
3824         }
3825         
3826         if(this.main){
3827             cfg.cls += ' main-nav';
3828         }
3829         
3830         
3831         return cfg;
3832
3833         
3834     },
3835     getHeaderChildContainer : function()
3836     {
3837         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3838             return this.el.select('.navbar-header',true).first();
3839         }
3840         
3841         return this.getChildContainer();
3842     },
3843     
3844     
3845     initEvents : function()
3846     {
3847         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3848         
3849         if (this.autohide) {
3850             
3851             var prevScroll = 0;
3852             var ft = this.el;
3853             
3854             Roo.get(document).on('scroll',function(e) {
3855                 var ns = Roo.get(document).getScroll().top;
3856                 var os = prevScroll;
3857                 prevScroll = ns;
3858                 
3859                 if(ns > os){
3860                     ft.removeClass('slideDown');
3861                     ft.addClass('slideUp');
3862                     return;
3863                 }
3864                 ft.removeClass('slideUp');
3865                 ft.addClass('slideDown');
3866                  
3867               
3868           },this);
3869         }
3870     }    
3871     
3872 });
3873
3874
3875
3876  
3877
3878  /*
3879  * - LGPL
3880  *
3881  * navbar
3882  * 
3883  */
3884
3885 /**
3886  * @class Roo.bootstrap.NavSidebar
3887  * @extends Roo.bootstrap.Navbar
3888  * Bootstrap Sidebar class
3889  * 
3890  * @constructor
3891  * Create a new Sidebar
3892  * @param {Object} config The config object
3893  */
3894
3895
3896 Roo.bootstrap.NavSidebar = function(config){
3897     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3898 };
3899
3900 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3901     
3902     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3903     
3904     getAutoCreate : function(){
3905         
3906         
3907         return  {
3908             tag: 'div',
3909             cls: 'sidebar sidebar-nav'
3910         };
3911     
3912         
3913     }
3914     
3915     
3916     
3917 });
3918
3919
3920
3921  
3922
3923  /*
3924  * - LGPL
3925  *
3926  * nav group
3927  * 
3928  */
3929
3930 /**
3931  * @class Roo.bootstrap.NavGroup
3932  * @extends Roo.bootstrap.Component
3933  * Bootstrap NavGroup class
3934  * @cfg {String} align (left|right)
3935  * @cfg {Boolean} inverse
3936  * @cfg {String} type (nav|pills|tab) default nav
3937  * @cfg {String} navId - reference Id for navbar.
3938
3939  * 
3940  * @constructor
3941  * Create a new nav group
3942  * @param {Object} config The config object
3943  */
3944
3945 Roo.bootstrap.NavGroup = function(config){
3946     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3947     this.navItems = [];
3948    
3949     Roo.bootstrap.NavGroup.register(this);
3950      this.addEvents({
3951         /**
3952              * @event changed
3953              * Fires when the active item changes
3954              * @param {Roo.bootstrap.NavGroup} this
3955              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3956              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3957          */
3958         'changed': true
3959      });
3960     
3961 };
3962
3963 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3964     
3965     align: '',
3966     inverse: false,
3967     form: false,
3968     type: 'nav',
3969     navId : '',
3970     // private
3971     
3972     navItems : false, 
3973     
3974     getAutoCreate : function()
3975     {
3976         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3977         
3978         cfg = {
3979             tag : 'ul',
3980             cls: 'nav' 
3981         };
3982         
3983         if (['tabs','pills'].indexOf(this.type)!==-1) {
3984             cfg.cls += ' nav-' + this.type
3985         } else {
3986             if (this.type!=='nav') {
3987                 Roo.log('nav type must be nav/tabs/pills')
3988             }
3989             cfg.cls += ' navbar-nav'
3990         }
3991         
3992         if (this.parent().sidebar) {
3993             cfg = {
3994                 tag: 'ul',
3995                 cls: 'dashboard-menu sidebar-menu'
3996             };
3997             
3998             return cfg;
3999         }
4000         
4001         if (this.form === true) {
4002             cfg = {
4003                 tag: 'form',
4004                 cls: 'navbar-form'
4005             };
4006             
4007             if (this.align === 'right') {
4008                 cfg.cls += ' navbar-right';
4009             } else {
4010                 cfg.cls += ' navbar-left';
4011             }
4012         }
4013         
4014         if (this.align === 'right') {
4015             cfg.cls += ' navbar-right';
4016         }
4017         
4018         if (this.inverse) {
4019             cfg.cls += ' navbar-inverse';
4020             
4021         }
4022         
4023         
4024         return cfg;
4025     },
4026     /**
4027     * sets the active Navigation item
4028     * @param {Roo.bootstrap.NavItem} the new current navitem
4029     */
4030     setActiveItem : function(item)
4031     {
4032         var prev = false;
4033         Roo.each(this.navItems, function(v){
4034             if (v == item) {
4035                 return ;
4036             }
4037             if (v.isActive()) {
4038                 v.setActive(false, true);
4039                 prev = v;
4040                 
4041             }
4042             
4043         });
4044
4045         item.setActive(true, true);
4046         this.fireEvent('changed', this, item, prev);
4047         
4048         
4049     },
4050     /**
4051     * gets the active Navigation item
4052     * @return {Roo.bootstrap.NavItem} the current navitem
4053     */
4054     getActive : function()
4055     {
4056         
4057         var prev = false;
4058         Roo.each(this.navItems, function(v){
4059             
4060             if (v.isActive()) {
4061                 prev = v;
4062                 
4063             }
4064             
4065         });
4066         return prev;
4067     },
4068     
4069     indexOfNav : function()
4070     {
4071         
4072         var prev = false;
4073         Roo.each(this.navItems, function(v,i){
4074             
4075             if (v.isActive()) {
4076                 prev = i;
4077                 
4078             }
4079             
4080         });
4081         return prev;
4082     },
4083     /**
4084     * adds a Navigation item
4085     * @param {Roo.bootstrap.NavItem} the navitem to add
4086     */
4087     addItem : function(cfg)
4088     {
4089         var cn = new Roo.bootstrap.NavItem(cfg);
4090         this.register(cn);
4091         cn.parentId = this.id;
4092         cn.onRender(this.el, null);
4093         return cn;
4094     },
4095     /**
4096     * register a Navigation item
4097     * @param {Roo.bootstrap.NavItem} the navitem to add
4098     */
4099     register : function(item)
4100     {
4101         this.navItems.push( item);
4102         item.navId = this.navId;
4103     
4104     },
4105     
4106     /**
4107     * clear all the Navigation item
4108     */
4109    
4110     clearAll : function()
4111     {
4112         this.navItems = [];
4113         this.el.dom.innerHTML = '';
4114     },
4115     
4116     getNavItem: function(tabId)
4117     {
4118         var ret = false;
4119         Roo.each(this.navItems, function(e) {
4120             if (e.tabId == tabId) {
4121                ret =  e;
4122                return false;
4123             }
4124             return true;
4125             
4126         });
4127         return ret;
4128     },
4129     
4130     setActiveNext : function()
4131     {
4132         var i = this.indexOfNav(this.getActive());
4133         if (i > this.navItems.length) {
4134             return;
4135         }
4136         this.setActiveItem(this.navItems[i+1]);
4137     },
4138     setActivePrev : function()
4139     {
4140         var i = this.indexOfNav(this.getActive());
4141         if (i  < 1) {
4142             return;
4143         }
4144         this.setActiveItem(this.navItems[i-1]);
4145     },
4146     clearWasActive : function(except) {
4147         Roo.each(this.navItems, function(e) {
4148             if (e.tabId != except.tabId && e.was_active) {
4149                e.was_active = false;
4150                return false;
4151             }
4152             return true;
4153             
4154         });
4155     },
4156     getWasActive : function ()
4157     {
4158         var r = false;
4159         Roo.each(this.navItems, function(e) {
4160             if (e.was_active) {
4161                r = e;
4162                return false;
4163             }
4164             return true;
4165             
4166         });
4167         return r;
4168     }
4169     
4170     
4171 });
4172
4173  
4174 Roo.apply(Roo.bootstrap.NavGroup, {
4175     
4176     groups: {},
4177      /**
4178     * register a Navigation Group
4179     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4180     */
4181     register : function(navgrp)
4182     {
4183         this.groups[navgrp.navId] = navgrp;
4184         
4185     },
4186     /**
4187     * fetch a Navigation Group based on the navigation ID
4188     * @param {string} the navgroup to add
4189     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4190     */
4191     get: function(navId) {
4192         if (typeof(this.groups[navId]) == 'undefined') {
4193             return false;
4194             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4195         }
4196         return this.groups[navId] ;
4197     }
4198     
4199     
4200     
4201 });
4202
4203  /*
4204  * - LGPL
4205  *
4206  * row
4207  * 
4208  */
4209
4210 /**
4211  * @class Roo.bootstrap.NavItem
4212  * @extends Roo.bootstrap.Component
4213  * Bootstrap Navbar.NavItem class
4214  * @cfg {String} href  link to
4215  * @cfg {String} html content of button
4216  * @cfg {String} badge text inside badge
4217  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4218  * @cfg {String} glyphicon name of glyphicon
4219  * @cfg {String} icon name of font awesome icon
4220  * @cfg {Boolean} active Is item active
4221  * @cfg {Boolean} disabled Is item disabled
4222  
4223  * @cfg {Boolean} preventDefault (true | false) default false
4224  * @cfg {String} tabId the tab that this item activates.
4225  * @cfg {String} tagtype (a|span) render as a href or span?
4226  * @cfg {Boolean} animateRef (true|false) link to element default false  
4227   
4228  * @constructor
4229  * Create a new Navbar Item
4230  * @param {Object} config The config object
4231  */
4232 Roo.bootstrap.NavItem = function(config){
4233     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4234     this.addEvents({
4235         // raw events
4236         /**
4237          * @event click
4238          * The raw click event for the entire grid.
4239          * @param {Roo.EventObject} e
4240          */
4241         "click" : true,
4242          /**
4243             * @event changed
4244             * Fires when the active item active state changes
4245             * @param {Roo.bootstrap.NavItem} this
4246             * @param {boolean} state the new state
4247              
4248          */
4249         'changed': true,
4250         /**
4251             * @event scrollto
4252             * Fires when scroll to element
4253             * @param {Roo.bootstrap.NavItem} this
4254             * @param {Object} options
4255             * @param {Roo.EventObject} e
4256              
4257          */
4258         'scrollto': true
4259     });
4260    
4261 };
4262
4263 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4264     
4265     href: false,
4266     html: '',
4267     badge: '',
4268     icon: false,
4269     glyphicon: false,
4270     active: false,
4271     preventDefault : false,
4272     tabId : false,
4273     tagtype : 'a',
4274     disabled : false,
4275     animateRef : false,
4276     was_active : false,
4277     
4278     getAutoCreate : function(){
4279          
4280         var cfg = {
4281             tag: 'li',
4282             cls: 'nav-item'
4283             
4284         };
4285         
4286         if (this.active) {
4287             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4288         }
4289         if (this.disabled) {
4290             cfg.cls += ' disabled';
4291         }
4292         
4293         if (this.href || this.html || this.glyphicon || this.icon) {
4294             cfg.cn = [
4295                 {
4296                     tag: this.tagtype,
4297                     href : this.href || "#",
4298                     html: this.html || ''
4299                 }
4300             ];
4301             
4302             if (this.icon) {
4303                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4304             }
4305
4306             if(this.glyphicon) {
4307                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4308             }
4309             
4310             if (this.menu) {
4311                 
4312                 cfg.cn[0].html += " <span class='caret'></span>";
4313              
4314             }
4315             
4316             if (this.badge !== '') {
4317                  
4318                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4319             }
4320         }
4321         
4322         
4323         
4324         return cfg;
4325     },
4326     initEvents: function() 
4327     {
4328         if (typeof (this.menu) != 'undefined') {
4329             this.menu.parentType = this.xtype;
4330             this.menu.triggerEl = this.el;
4331             this.menu = this.addxtype(Roo.apply({}, this.menu));
4332         }
4333         
4334         this.el.select('a',true).on('click', this.onClick, this);
4335         
4336         if(this.tagtype == 'span'){
4337             this.el.select('span',true).on('click', this.onClick, this);
4338         }
4339        
4340         // at this point parent should be available..
4341         this.parent().register(this);
4342     },
4343     
4344     onClick : function(e)
4345     {
4346         if (e.getTarget('.dropdown-menu-item')) {
4347             // did you click on a menu itemm.... - then don't trigger onclick..
4348             return;
4349         }
4350         
4351         if(
4352                 this.preventDefault || 
4353                 this.href == '#' 
4354         ){
4355             Roo.log("NavItem - prevent Default?");
4356             e.preventDefault();
4357         }
4358         
4359         if (this.disabled) {
4360             return;
4361         }
4362         
4363         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4364         if (tg && tg.transition) {
4365             Roo.log("waiting for the transitionend");
4366             return;
4367         }
4368         
4369         
4370         
4371         //Roo.log("fire event clicked");
4372         if(this.fireEvent('click', this, e) === false){
4373             return;
4374         };
4375         
4376         if(this.tagtype == 'span'){
4377             return;
4378         }
4379         
4380         //Roo.log(this.href);
4381         var ael = this.el.select('a',true).first();
4382         //Roo.log(ael);
4383         
4384         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4385             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4386             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4387                 return; // ignore... - it's a 'hash' to another page.
4388             }
4389             Roo.log("NavItem - prevent Default?");
4390             e.preventDefault();
4391             this.scrollToElement(e);
4392         }
4393         
4394         
4395         var p =  this.parent();
4396    
4397         if (['tabs','pills'].indexOf(p.type)!==-1) {
4398             if (typeof(p.setActiveItem) !== 'undefined') {
4399                 p.setActiveItem(this);
4400             }
4401         }
4402         
4403         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4404         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4405             // remove the collapsed menu expand...
4406             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4407         }
4408     },
4409     
4410     isActive: function () {
4411         return this.active
4412     },
4413     setActive : function(state, fire, is_was_active)
4414     {
4415         if (this.active && !state && this.navId) {
4416             this.was_active = true;
4417             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4418             if (nv) {
4419                 nv.clearWasActive(this);
4420             }
4421             
4422         }
4423         this.active = state;
4424         
4425         if (!state ) {
4426             this.el.removeClass('active');
4427         } else if (!this.el.hasClass('active')) {
4428             this.el.addClass('active');
4429         }
4430         if (fire) {
4431             this.fireEvent('changed', this, state);
4432         }
4433         
4434         // show a panel if it's registered and related..
4435         
4436         if (!this.navId || !this.tabId || !state || is_was_active) {
4437             return;
4438         }
4439         
4440         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4441         if (!tg) {
4442             return;
4443         }
4444         var pan = tg.getPanelByName(this.tabId);
4445         if (!pan) {
4446             return;
4447         }
4448         // if we can not flip to new panel - go back to old nav highlight..
4449         if (false == tg.showPanel(pan)) {
4450             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4451             if (nv) {
4452                 var onav = nv.getWasActive();
4453                 if (onav) {
4454                     onav.setActive(true, false, true);
4455                 }
4456             }
4457             
4458         }
4459         
4460         
4461         
4462     },
4463      // this should not be here...
4464     setDisabled : function(state)
4465     {
4466         this.disabled = state;
4467         if (!state ) {
4468             this.el.removeClass('disabled');
4469         } else if (!this.el.hasClass('disabled')) {
4470             this.el.addClass('disabled');
4471         }
4472         
4473     },
4474     
4475     /**
4476      * Fetch the element to display the tooltip on.
4477      * @return {Roo.Element} defaults to this.el
4478      */
4479     tooltipEl : function()
4480     {
4481         return this.el.select('' + this.tagtype + '', true).first();
4482     },
4483     
4484     scrollToElement : function(e)
4485     {
4486         var c = document.body;
4487         
4488         /*
4489          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4490          */
4491         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4492             c = document.documentElement;
4493         }
4494         
4495         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4496         
4497         if(!target){
4498             return;
4499         }
4500
4501         var o = target.calcOffsetsTo(c);
4502         
4503         var options = {
4504             target : target,
4505             value : o[1]
4506         };
4507         
4508         this.fireEvent('scrollto', this, options, e);
4509         
4510         Roo.get(c).scrollTo('top', options.value, true);
4511         
4512         return;
4513     }
4514 });
4515  
4516
4517  /*
4518  * - LGPL
4519  *
4520  * sidebar item
4521  *
4522  *  li
4523  *    <span> icon </span>
4524  *    <span> text </span>
4525  *    <span>badge </span>
4526  */
4527
4528 /**
4529  * @class Roo.bootstrap.NavSidebarItem
4530  * @extends Roo.bootstrap.NavItem
4531  * Bootstrap Navbar.NavSidebarItem class
4532  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4533  * {bool} open is the menu open
4534  * @constructor
4535  * Create a new Navbar Button
4536  * @param {Object} config The config object
4537  */
4538 Roo.bootstrap.NavSidebarItem = function(config){
4539     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4540     this.addEvents({
4541         // raw events
4542         /**
4543          * @event click
4544          * The raw click event for the entire grid.
4545          * @param {Roo.EventObject} e
4546          */
4547         "click" : true,
4548          /**
4549             * @event changed
4550             * Fires when the active item active state changes
4551             * @param {Roo.bootstrap.NavSidebarItem} this
4552             * @param {boolean} state the new state
4553              
4554          */
4555         'changed': true
4556     });
4557    
4558 };
4559
4560 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4561     
4562     badgeWeight : 'default',
4563     
4564     open: false,
4565     
4566     getAutoCreate : function(){
4567         
4568         
4569         var a = {
4570                 tag: 'a',
4571                 href : this.href || '#',
4572                 cls: '',
4573                 html : '',
4574                 cn : []
4575         };
4576         var cfg = {
4577             tag: 'li',
4578             cls: '',
4579             cn: [ a ]
4580         };
4581         var span = {
4582             tag: 'span',
4583             html : this.html || ''
4584         };
4585         
4586         
4587         if (this.active) {
4588             cfg.cls += ' active';
4589         }
4590         
4591         if (this.disabled) {
4592             cfg.cls += ' disabled';
4593         }
4594         if (this.open) {
4595             cfg.cls += ' open x-open';
4596         }
4597         // left icon..
4598         if (this.glyphicon || this.icon) {
4599             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4600             a.cn.push({ tag : 'i', cls : c }) ;
4601         }
4602         // html..
4603         a.cn.push(span);
4604         // then badge..
4605         if (this.badge !== '') {
4606             
4607             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4608         }
4609         // fi
4610         if (this.menu) {
4611             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4612             a.cls += 'dropdown-toggle treeview' ;
4613         }
4614         
4615         return cfg;
4616          
4617            
4618     },
4619     
4620     initEvents : function()
4621     { 
4622         if (typeof (this.menu) != 'undefined') {
4623             this.menu.parentType = this.xtype;
4624             this.menu.triggerEl = this.el;
4625             this.menu = this.addxtype(Roo.apply({}, this.menu));
4626         }
4627         
4628         this.el.on('click', this.onClick, this);
4629        
4630     
4631         if(this.badge !== ''){
4632  
4633             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4634         }
4635         
4636     },
4637     
4638     onClick : function(e)
4639     {
4640         if(this.disabled){
4641             e.preventDefault();
4642             return;
4643         }
4644         
4645         if(this.preventDefault){
4646             e.preventDefault();
4647         }
4648         
4649         this.fireEvent('click', this);
4650     },
4651     
4652     disable : function()
4653     {
4654         this.setDisabled(true);
4655     },
4656     
4657     enable : function()
4658     {
4659         this.setDisabled(false);
4660     },
4661     
4662     setDisabled : function(state)
4663     {
4664         if(this.disabled == state){
4665             return;
4666         }
4667         
4668         this.disabled = state;
4669         
4670         if (state) {
4671             this.el.addClass('disabled');
4672             return;
4673         }
4674         
4675         this.el.removeClass('disabled');
4676         
4677         return;
4678     },
4679     
4680     setActive : function(state)
4681     {
4682         if(this.active == state){
4683             return;
4684         }
4685         
4686         this.active = state;
4687         
4688         if (state) {
4689             this.el.addClass('active');
4690             return;
4691         }
4692         
4693         this.el.removeClass('active');
4694         
4695         return;
4696     },
4697     
4698     isActive: function () 
4699     {
4700         return this.active;
4701     },
4702     
4703     setBadge : function(str)
4704     {
4705         if(!this.badgeEl){
4706             return;
4707         }
4708         
4709         this.badgeEl.dom.innerHTML = str;
4710     }
4711     
4712    
4713      
4714  
4715 });
4716  
4717
4718  /*
4719  * - LGPL
4720  *
4721  * row
4722  * 
4723  */
4724
4725 /**
4726  * @class Roo.bootstrap.Row
4727  * @extends Roo.bootstrap.Component
4728  * Bootstrap Row class (contains columns...)
4729  * 
4730  * @constructor
4731  * Create a new Row
4732  * @param {Object} config The config object
4733  */
4734
4735 Roo.bootstrap.Row = function(config){
4736     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4737 };
4738
4739 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4740     
4741     getAutoCreate : function(){
4742        return {
4743             cls: 'row clearfix'
4744        };
4745     }
4746     
4747     
4748 });
4749
4750  
4751
4752  /*
4753  * - LGPL
4754  *
4755  * element
4756  * 
4757  */
4758
4759 /**
4760  * @class Roo.bootstrap.Element
4761  * @extends Roo.bootstrap.Component
4762  * Bootstrap Element class
4763  * @cfg {String} html contents of the element
4764  * @cfg {String} tag tag of the element
4765  * @cfg {String} cls class of the element
4766  * @cfg {Boolean} preventDefault (true|false) default false
4767  * @cfg {Boolean} clickable (true|false) default false
4768  * 
4769  * @constructor
4770  * Create a new Element
4771  * @param {Object} config The config object
4772  */
4773
4774 Roo.bootstrap.Element = function(config){
4775     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4776     
4777     this.addEvents({
4778         // raw events
4779         /**
4780          * @event click
4781          * When a element is chick
4782          * @param {Roo.bootstrap.Element} this
4783          * @param {Roo.EventObject} e
4784          */
4785         "click" : true
4786     });
4787 };
4788
4789 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4790     
4791     tag: 'div',
4792     cls: '',
4793     html: '',
4794     preventDefault: false, 
4795     clickable: false,
4796     
4797     getAutoCreate : function(){
4798         
4799         var cfg = {
4800             tag: this.tag,
4801             cls: this.cls,
4802             html: this.html
4803         };
4804         
4805         return cfg;
4806     },
4807     
4808     initEvents: function() 
4809     {
4810         Roo.bootstrap.Element.superclass.initEvents.call(this);
4811         
4812         if(this.clickable){
4813             this.el.on('click', this.onClick, this);
4814         }
4815         
4816     },
4817     
4818     onClick : function(e)
4819     {
4820         if(this.preventDefault){
4821             e.preventDefault();
4822         }
4823         
4824         this.fireEvent('click', this, e);
4825     },
4826     
4827     getValue : function()
4828     {
4829         return this.el.dom.innerHTML;
4830     },
4831     
4832     setValue : function(value)
4833     {
4834         this.el.dom.innerHTML = value;
4835     }
4836    
4837 });
4838
4839  
4840
4841  /*
4842  * - LGPL
4843  *
4844  * pagination
4845  * 
4846  */
4847
4848 /**
4849  * @class Roo.bootstrap.Pagination
4850  * @extends Roo.bootstrap.Component
4851  * Bootstrap Pagination class
4852  * @cfg {String} size xs | sm | md | lg
4853  * @cfg {Boolean} inverse false | true
4854  * 
4855  * @constructor
4856  * Create a new Pagination
4857  * @param {Object} config The config object
4858  */
4859
4860 Roo.bootstrap.Pagination = function(config){
4861     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4862 };
4863
4864 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4865     
4866     cls: false,
4867     size: false,
4868     inverse: false,
4869     
4870     getAutoCreate : function(){
4871         var cfg = {
4872             tag: 'ul',
4873                 cls: 'pagination'
4874         };
4875         if (this.inverse) {
4876             cfg.cls += ' inverse';
4877         }
4878         if (this.html) {
4879             cfg.html=this.html;
4880         }
4881         if (this.cls) {
4882             cfg.cls += " " + this.cls;
4883         }
4884         return cfg;
4885     }
4886    
4887 });
4888
4889  
4890
4891  /*
4892  * - LGPL
4893  *
4894  * Pagination item
4895  * 
4896  */
4897
4898
4899 /**
4900  * @class Roo.bootstrap.PaginationItem
4901  * @extends Roo.bootstrap.Component
4902  * Bootstrap PaginationItem class
4903  * @cfg {String} html text
4904  * @cfg {String} href the link
4905  * @cfg {Boolean} preventDefault (true | false) default true
4906  * @cfg {Boolean} active (true | false) default false
4907  * @cfg {Boolean} disabled default false
4908  * 
4909  * 
4910  * @constructor
4911  * Create a new PaginationItem
4912  * @param {Object} config The config object
4913  */
4914
4915
4916 Roo.bootstrap.PaginationItem = function(config){
4917     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4918     this.addEvents({
4919         // raw events
4920         /**
4921          * @event click
4922          * The raw click event for the entire grid.
4923          * @param {Roo.EventObject} e
4924          */
4925         "click" : true
4926     });
4927 };
4928
4929 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4930     
4931     href : false,
4932     html : false,
4933     preventDefault: true,
4934     active : false,
4935     cls : false,
4936     disabled: false,
4937     
4938     getAutoCreate : function(){
4939         var cfg= {
4940             tag: 'li',
4941             cn: [
4942                 {
4943                     tag : 'a',
4944                     href : this.href ? this.href : '#',
4945                     html : this.html ? this.html : ''
4946                 }
4947             ]
4948         };
4949         
4950         if(this.cls){
4951             cfg.cls = this.cls;
4952         }
4953         
4954         if(this.disabled){
4955             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4956         }
4957         
4958         if(this.active){
4959             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4960         }
4961         
4962         return cfg;
4963     },
4964     
4965     initEvents: function() {
4966         
4967         this.el.on('click', this.onClick, this);
4968         
4969     },
4970     onClick : function(e)
4971     {
4972         Roo.log('PaginationItem on click ');
4973         if(this.preventDefault){
4974             e.preventDefault();
4975         }
4976         
4977         if(this.disabled){
4978             return;
4979         }
4980         
4981         this.fireEvent('click', this, e);
4982     }
4983    
4984 });
4985
4986  
4987
4988  /*
4989  * - LGPL
4990  *
4991  * slider
4992  * 
4993  */
4994
4995
4996 /**
4997  * @class Roo.bootstrap.Slider
4998  * @extends Roo.bootstrap.Component
4999  * Bootstrap Slider class
5000  *    
5001  * @constructor
5002  * Create a new Slider
5003  * @param {Object} config The config object
5004  */
5005
5006 Roo.bootstrap.Slider = function(config){
5007     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5008 };
5009
5010 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5011     
5012     getAutoCreate : function(){
5013         
5014         var cfg = {
5015             tag: 'div',
5016             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5017             cn: [
5018                 {
5019                     tag: 'a',
5020                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5021                 }
5022             ]
5023         };
5024         
5025         return cfg;
5026     }
5027    
5028 });
5029
5030  /*
5031  * Based on:
5032  * Ext JS Library 1.1.1
5033  * Copyright(c) 2006-2007, Ext JS, LLC.
5034  *
5035  * Originally Released Under LGPL - original licence link has changed is not relivant.
5036  *
5037  * Fork - LGPL
5038  * <script type="text/javascript">
5039  */
5040  
5041
5042 /**
5043  * @class Roo.grid.ColumnModel
5044  * @extends Roo.util.Observable
5045  * This is the default implementation of a ColumnModel used by the Grid. It defines
5046  * the columns in the grid.
5047  * <br>Usage:<br>
5048  <pre><code>
5049  var colModel = new Roo.grid.ColumnModel([
5050         {header: "Ticker", width: 60, sortable: true, locked: true},
5051         {header: "Company Name", width: 150, sortable: true},
5052         {header: "Market Cap.", width: 100, sortable: true},
5053         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5054         {header: "Employees", width: 100, sortable: true, resizable: false}
5055  ]);
5056  </code></pre>
5057  * <p>
5058  
5059  * The config options listed for this class are options which may appear in each
5060  * individual column definition.
5061  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5062  * @constructor
5063  * @param {Object} config An Array of column config objects. See this class's
5064  * config objects for details.
5065 */
5066 Roo.grid.ColumnModel = function(config){
5067         /**
5068      * The config passed into the constructor
5069      */
5070     this.config = config;
5071     this.lookup = {};
5072
5073     // if no id, create one
5074     // if the column does not have a dataIndex mapping,
5075     // map it to the order it is in the config
5076     for(var i = 0, len = config.length; i < len; i++){
5077         var c = config[i];
5078         if(typeof c.dataIndex == "undefined"){
5079             c.dataIndex = i;
5080         }
5081         if(typeof c.renderer == "string"){
5082             c.renderer = Roo.util.Format[c.renderer];
5083         }
5084         if(typeof c.id == "undefined"){
5085             c.id = Roo.id();
5086         }
5087         if(c.editor && c.editor.xtype){
5088             c.editor  = Roo.factory(c.editor, Roo.grid);
5089         }
5090         if(c.editor && c.editor.isFormField){
5091             c.editor = new Roo.grid.GridEditor(c.editor);
5092         }
5093         this.lookup[c.id] = c;
5094     }
5095
5096     /**
5097      * The width of columns which have no width specified (defaults to 100)
5098      * @type Number
5099      */
5100     this.defaultWidth = 100;
5101
5102     /**
5103      * Default sortable of columns which have no sortable specified (defaults to false)
5104      * @type Boolean
5105      */
5106     this.defaultSortable = false;
5107
5108     this.addEvents({
5109         /**
5110              * @event widthchange
5111              * Fires when the width of a column changes.
5112              * @param {ColumnModel} this
5113              * @param {Number} columnIndex The column index
5114              * @param {Number} newWidth The new width
5115              */
5116             "widthchange": true,
5117         /**
5118              * @event headerchange
5119              * Fires when the text of a header changes.
5120              * @param {ColumnModel} this
5121              * @param {Number} columnIndex The column index
5122              * @param {Number} newText The new header text
5123              */
5124             "headerchange": true,
5125         /**
5126              * @event hiddenchange
5127              * Fires when a column is hidden or "unhidden".
5128              * @param {ColumnModel} this
5129              * @param {Number} columnIndex The column index
5130              * @param {Boolean} hidden true if hidden, false otherwise
5131              */
5132             "hiddenchange": true,
5133             /**
5134          * @event columnmoved
5135          * Fires when a column is moved.
5136          * @param {ColumnModel} this
5137          * @param {Number} oldIndex
5138          * @param {Number} newIndex
5139          */
5140         "columnmoved" : true,
5141         /**
5142          * @event columlockchange
5143          * Fires when a column's locked state is changed
5144          * @param {ColumnModel} this
5145          * @param {Number} colIndex
5146          * @param {Boolean} locked true if locked
5147          */
5148         "columnlockchange" : true
5149     });
5150     Roo.grid.ColumnModel.superclass.constructor.call(this);
5151 };
5152 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5153     /**
5154      * @cfg {String} header The header text to display in the Grid view.
5155      */
5156     /**
5157      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5158      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5159      * specified, the column's index is used as an index into the Record's data Array.
5160      */
5161     /**
5162      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5163      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5164      */
5165     /**
5166      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5167      * Defaults to the value of the {@link #defaultSortable} property.
5168      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5169      */
5170     /**
5171      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5172      */
5173     /**
5174      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5175      */
5176     /**
5177      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5178      */
5179     /**
5180      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5181      */
5182     /**
5183      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5184      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5185      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5186      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5187      */
5188        /**
5189      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5190      */
5191     /**
5192      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5193      */
5194     /**
5195      * @cfg {String} cursor (Optional)
5196      */
5197     /**
5198      * @cfg {String} tooltip (Optional)
5199      */
5200     /**
5201      * @cfg {Number} xs (Optional)
5202      */
5203     /**
5204      * @cfg {Number} sm (Optional)
5205      */
5206     /**
5207      * @cfg {Number} md (Optional)
5208      */
5209     /**
5210      * @cfg {Number} lg (Optional)
5211      */
5212     /**
5213      * Returns the id of the column at the specified index.
5214      * @param {Number} index The column index
5215      * @return {String} the id
5216      */
5217     getColumnId : function(index){
5218         return this.config[index].id;
5219     },
5220
5221     /**
5222      * Returns the column for a specified id.
5223      * @param {String} id The column id
5224      * @return {Object} the column
5225      */
5226     getColumnById : function(id){
5227         return this.lookup[id];
5228     },
5229
5230     
5231     /**
5232      * Returns the column for a specified dataIndex.
5233      * @param {String} dataIndex The column dataIndex
5234      * @return {Object|Boolean} the column or false if not found
5235      */
5236     getColumnByDataIndex: function(dataIndex){
5237         var index = this.findColumnIndex(dataIndex);
5238         return index > -1 ? this.config[index] : false;
5239     },
5240     
5241     /**
5242      * Returns the index for a specified column id.
5243      * @param {String} id The column id
5244      * @return {Number} the index, or -1 if not found
5245      */
5246     getIndexById : function(id){
5247         for(var i = 0, len = this.config.length; i < len; i++){
5248             if(this.config[i].id == id){
5249                 return i;
5250             }
5251         }
5252         return -1;
5253     },
5254     
5255     /**
5256      * Returns the index for a specified column dataIndex.
5257      * @param {String} dataIndex The column dataIndex
5258      * @return {Number} the index, or -1 if not found
5259      */
5260     
5261     findColumnIndex : function(dataIndex){
5262         for(var i = 0, len = this.config.length; i < len; i++){
5263             if(this.config[i].dataIndex == dataIndex){
5264                 return i;
5265             }
5266         }
5267         return -1;
5268     },
5269     
5270     
5271     moveColumn : function(oldIndex, newIndex){
5272         var c = this.config[oldIndex];
5273         this.config.splice(oldIndex, 1);
5274         this.config.splice(newIndex, 0, c);
5275         this.dataMap = null;
5276         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5277     },
5278
5279     isLocked : function(colIndex){
5280         return this.config[colIndex].locked === true;
5281     },
5282
5283     setLocked : function(colIndex, value, suppressEvent){
5284         if(this.isLocked(colIndex) == value){
5285             return;
5286         }
5287         this.config[colIndex].locked = value;
5288         if(!suppressEvent){
5289             this.fireEvent("columnlockchange", this, colIndex, value);
5290         }
5291     },
5292
5293     getTotalLockedWidth : function(){
5294         var totalWidth = 0;
5295         for(var i = 0; i < this.config.length; i++){
5296             if(this.isLocked(i) && !this.isHidden(i)){
5297                 this.totalWidth += this.getColumnWidth(i);
5298             }
5299         }
5300         return totalWidth;
5301     },
5302
5303     getLockedCount : function(){
5304         for(var i = 0, len = this.config.length; i < len; i++){
5305             if(!this.isLocked(i)){
5306                 return i;
5307             }
5308         }
5309         
5310         return this.config.length;
5311     },
5312
5313     /**
5314      * Returns the number of columns.
5315      * @return {Number}
5316      */
5317     getColumnCount : function(visibleOnly){
5318         if(visibleOnly === true){
5319             var c = 0;
5320             for(var i = 0, len = this.config.length; i < len; i++){
5321                 if(!this.isHidden(i)){
5322                     c++;
5323                 }
5324             }
5325             return c;
5326         }
5327         return this.config.length;
5328     },
5329
5330     /**
5331      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5332      * @param {Function} fn
5333      * @param {Object} scope (optional)
5334      * @return {Array} result
5335      */
5336     getColumnsBy : function(fn, scope){
5337         var r = [];
5338         for(var i = 0, len = this.config.length; i < len; i++){
5339             var c = this.config[i];
5340             if(fn.call(scope||this, c, i) === true){
5341                 r[r.length] = c;
5342             }
5343         }
5344         return r;
5345     },
5346
5347     /**
5348      * Returns true if the specified column is sortable.
5349      * @param {Number} col The column index
5350      * @return {Boolean}
5351      */
5352     isSortable : function(col){
5353         if(typeof this.config[col].sortable == "undefined"){
5354             return this.defaultSortable;
5355         }
5356         return this.config[col].sortable;
5357     },
5358
5359     /**
5360      * Returns the rendering (formatting) function defined for the column.
5361      * @param {Number} col The column index.
5362      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5363      */
5364     getRenderer : function(col){
5365         if(!this.config[col].renderer){
5366             return Roo.grid.ColumnModel.defaultRenderer;
5367         }
5368         return this.config[col].renderer;
5369     },
5370
5371     /**
5372      * Sets the rendering (formatting) function for a column.
5373      * @param {Number} col The column index
5374      * @param {Function} fn The function to use to process the cell's raw data
5375      * to return HTML markup for the grid view. The render function is called with
5376      * the following parameters:<ul>
5377      * <li>Data value.</li>
5378      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5379      * <li>css A CSS style string to apply to the table cell.</li>
5380      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5381      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5382      * <li>Row index</li>
5383      * <li>Column index</li>
5384      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5385      */
5386     setRenderer : function(col, fn){
5387         this.config[col].renderer = fn;
5388     },
5389
5390     /**
5391      * Returns the width for the specified column.
5392      * @param {Number} col The column index
5393      * @return {Number}
5394      */
5395     getColumnWidth : function(col){
5396         return this.config[col].width * 1 || this.defaultWidth;
5397     },
5398
5399     /**
5400      * Sets the width for a column.
5401      * @param {Number} col The column index
5402      * @param {Number} width The new width
5403      */
5404     setColumnWidth : function(col, width, suppressEvent){
5405         this.config[col].width = width;
5406         this.totalWidth = null;
5407         if(!suppressEvent){
5408              this.fireEvent("widthchange", this, col, width);
5409         }
5410     },
5411
5412     /**
5413      * Returns the total width of all columns.
5414      * @param {Boolean} includeHidden True to include hidden column widths
5415      * @return {Number}
5416      */
5417     getTotalWidth : function(includeHidden){
5418         if(!this.totalWidth){
5419             this.totalWidth = 0;
5420             for(var i = 0, len = this.config.length; i < len; i++){
5421                 if(includeHidden || !this.isHidden(i)){
5422                     this.totalWidth += this.getColumnWidth(i);
5423                 }
5424             }
5425         }
5426         return this.totalWidth;
5427     },
5428
5429     /**
5430      * Returns the header for the specified column.
5431      * @param {Number} col The column index
5432      * @return {String}
5433      */
5434     getColumnHeader : function(col){
5435         return this.config[col].header;
5436     },
5437
5438     /**
5439      * Sets the header for a column.
5440      * @param {Number} col The column index
5441      * @param {String} header The new header
5442      */
5443     setColumnHeader : function(col, header){
5444         this.config[col].header = header;
5445         this.fireEvent("headerchange", this, col, header);
5446     },
5447
5448     /**
5449      * Returns the tooltip for the specified column.
5450      * @param {Number} col The column index
5451      * @return {String}
5452      */
5453     getColumnTooltip : function(col){
5454             return this.config[col].tooltip;
5455     },
5456     /**
5457      * Sets the tooltip for a column.
5458      * @param {Number} col The column index
5459      * @param {String} tooltip The new tooltip
5460      */
5461     setColumnTooltip : function(col, tooltip){
5462             this.config[col].tooltip = tooltip;
5463     },
5464
5465     /**
5466      * Returns the dataIndex for the specified column.
5467      * @param {Number} col The column index
5468      * @return {Number}
5469      */
5470     getDataIndex : function(col){
5471         return this.config[col].dataIndex;
5472     },
5473
5474     /**
5475      * Sets the dataIndex for a column.
5476      * @param {Number} col The column index
5477      * @param {Number} dataIndex The new dataIndex
5478      */
5479     setDataIndex : function(col, dataIndex){
5480         this.config[col].dataIndex = dataIndex;
5481     },
5482
5483     
5484     
5485     /**
5486      * Returns true if the cell is editable.
5487      * @param {Number} colIndex The column index
5488      * @param {Number} rowIndex The row index - this is nto actually used..?
5489      * @return {Boolean}
5490      */
5491     isCellEditable : function(colIndex, rowIndex){
5492         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5493     },
5494
5495     /**
5496      * Returns the editor defined for the cell/column.
5497      * return false or null to disable editing.
5498      * @param {Number} colIndex The column index
5499      * @param {Number} rowIndex The row index
5500      * @return {Object}
5501      */
5502     getCellEditor : function(colIndex, rowIndex){
5503         return this.config[colIndex].editor;
5504     },
5505
5506     /**
5507      * Sets if a column is editable.
5508      * @param {Number} col The column index
5509      * @param {Boolean} editable True if the column is editable
5510      */
5511     setEditable : function(col, editable){
5512         this.config[col].editable = editable;
5513     },
5514
5515
5516     /**
5517      * Returns true if the column is hidden.
5518      * @param {Number} colIndex The column index
5519      * @return {Boolean}
5520      */
5521     isHidden : function(colIndex){
5522         return this.config[colIndex].hidden;
5523     },
5524
5525
5526     /**
5527      * Returns true if the column width cannot be changed
5528      */
5529     isFixed : function(colIndex){
5530         return this.config[colIndex].fixed;
5531     },
5532
5533     /**
5534      * Returns true if the column can be resized
5535      * @return {Boolean}
5536      */
5537     isResizable : function(colIndex){
5538         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5539     },
5540     /**
5541      * Sets if a column is hidden.
5542      * @param {Number} colIndex The column index
5543      * @param {Boolean} hidden True if the column is hidden
5544      */
5545     setHidden : function(colIndex, hidden){
5546         this.config[colIndex].hidden = hidden;
5547         this.totalWidth = null;
5548         this.fireEvent("hiddenchange", this, colIndex, hidden);
5549     },
5550
5551     /**
5552      * Sets the editor for a column.
5553      * @param {Number} col The column index
5554      * @param {Object} editor The editor object
5555      */
5556     setEditor : function(col, editor){
5557         this.config[col].editor = editor;
5558     }
5559 });
5560
5561 Roo.grid.ColumnModel.defaultRenderer = function(value)
5562 {
5563     if(typeof value == "object") {
5564         return value;
5565     }
5566         if(typeof value == "string" && value.length < 1){
5567             return "&#160;";
5568         }
5569     
5570         return String.format("{0}", value);
5571 };
5572
5573 // Alias for backwards compatibility
5574 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5575 /*
5576  * Based on:
5577  * Ext JS Library 1.1.1
5578  * Copyright(c) 2006-2007, Ext JS, LLC.
5579  *
5580  * Originally Released Under LGPL - original licence link has changed is not relivant.
5581  *
5582  * Fork - LGPL
5583  * <script type="text/javascript">
5584  */
5585  
5586 /**
5587  * @class Roo.LoadMask
5588  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5589  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5590  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5591  * element's UpdateManager load indicator and will be destroyed after the initial load.
5592  * @constructor
5593  * Create a new LoadMask
5594  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5595  * @param {Object} config The config object
5596  */
5597 Roo.LoadMask = function(el, config){
5598     this.el = Roo.get(el);
5599     Roo.apply(this, config);
5600     if(this.store){
5601         this.store.on('beforeload', this.onBeforeLoad, this);
5602         this.store.on('load', this.onLoad, this);
5603         this.store.on('loadexception', this.onLoadException, this);
5604         this.removeMask = false;
5605     }else{
5606         var um = this.el.getUpdateManager();
5607         um.showLoadIndicator = false; // disable the default indicator
5608         um.on('beforeupdate', this.onBeforeLoad, this);
5609         um.on('update', this.onLoad, this);
5610         um.on('failure', this.onLoad, this);
5611         this.removeMask = true;
5612     }
5613 };
5614
5615 Roo.LoadMask.prototype = {
5616     /**
5617      * @cfg {Boolean} removeMask
5618      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5619      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5620      */
5621     /**
5622      * @cfg {String} msg
5623      * The text to display in a centered loading message box (defaults to 'Loading...')
5624      */
5625     msg : 'Loading...',
5626     /**
5627      * @cfg {String} msgCls
5628      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5629      */
5630     msgCls : 'x-mask-loading',
5631
5632     /**
5633      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5634      * @type Boolean
5635      */
5636     disabled: false,
5637
5638     /**
5639      * Disables the mask to prevent it from being displayed
5640      */
5641     disable : function(){
5642        this.disabled = true;
5643     },
5644
5645     /**
5646      * Enables the mask so that it can be displayed
5647      */
5648     enable : function(){
5649         this.disabled = false;
5650     },
5651     
5652     onLoadException : function()
5653     {
5654         Roo.log(arguments);
5655         
5656         if (typeof(arguments[3]) != 'undefined') {
5657             Roo.MessageBox.alert("Error loading",arguments[3]);
5658         } 
5659         /*
5660         try {
5661             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5662                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5663             }   
5664         } catch(e) {
5665             
5666         }
5667         */
5668     
5669         
5670         
5671         this.el.unmask(this.removeMask);
5672     },
5673     // private
5674     onLoad : function()
5675     {
5676         this.el.unmask(this.removeMask);
5677     },
5678
5679     // private
5680     onBeforeLoad : function(){
5681         if(!this.disabled){
5682             this.el.mask(this.msg, this.msgCls);
5683         }
5684     },
5685
5686     // private
5687     destroy : function(){
5688         if(this.store){
5689             this.store.un('beforeload', this.onBeforeLoad, this);
5690             this.store.un('load', this.onLoad, this);
5691             this.store.un('loadexception', this.onLoadException, this);
5692         }else{
5693             var um = this.el.getUpdateManager();
5694             um.un('beforeupdate', this.onBeforeLoad, this);
5695             um.un('update', this.onLoad, this);
5696             um.un('failure', this.onLoad, this);
5697         }
5698     }
5699 };/*
5700  * - LGPL
5701  *
5702  * table
5703  * 
5704  */
5705
5706 /**
5707  * @class Roo.bootstrap.Table
5708  * @extends Roo.bootstrap.Component
5709  * Bootstrap Table class
5710  * @cfg {String} cls table class
5711  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5712  * @cfg {String} bgcolor Specifies the background color for a table
5713  * @cfg {Number} border Specifies whether the table cells should have borders or not
5714  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5715  * @cfg {Number} cellspacing Specifies the space between cells
5716  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5717  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5718  * @cfg {String} sortable Specifies that the table should be sortable
5719  * @cfg {String} summary Specifies a summary of the content of a table
5720  * @cfg {Number} width Specifies the width of a table
5721  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5722  * 
5723  * @cfg {boolean} striped Should the rows be alternative striped
5724  * @cfg {boolean} bordered Add borders to the table
5725  * @cfg {boolean} hover Add hover highlighting
5726  * @cfg {boolean} condensed Format condensed
5727  * @cfg {boolean} responsive Format condensed
5728  * @cfg {Boolean} loadMask (true|false) default false
5729  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5730  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5731  * @cfg {Boolean} rowSelection (true|false) default false
5732  * @cfg {Boolean} cellSelection (true|false) default false
5733  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5734  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5735  
5736  * 
5737  * @constructor
5738  * Create a new Table
5739  * @param {Object} config The config object
5740  */
5741
5742 Roo.bootstrap.Table = function(config){
5743     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5744     
5745   
5746     
5747     // BC...
5748     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5749     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5750     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5751     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5752     
5753     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5754     if (this.sm) {
5755         this.sm.grid = this;
5756         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5757         this.sm = this.selModel;
5758         this.sm.xmodule = this.xmodule || false;
5759     }
5760     
5761     if (this.cm && typeof(this.cm.config) == 'undefined') {
5762         this.colModel = new Roo.grid.ColumnModel(this.cm);
5763         this.cm = this.colModel;
5764         this.cm.xmodule = this.xmodule || false;
5765     }
5766     if (this.store) {
5767         this.store= Roo.factory(this.store, Roo.data);
5768         this.ds = this.store;
5769         this.ds.xmodule = this.xmodule || false;
5770          
5771     }
5772     if (this.footer && this.store) {
5773         this.footer.dataSource = this.ds;
5774         this.footer = Roo.factory(this.footer);
5775     }
5776     
5777     /** @private */
5778     this.addEvents({
5779         /**
5780          * @event cellclick
5781          * Fires when a cell is clicked
5782          * @param {Roo.bootstrap.Table} this
5783          * @param {Roo.Element} el
5784          * @param {Number} rowIndex
5785          * @param {Number} columnIndex
5786          * @param {Roo.EventObject} e
5787          */
5788         "cellclick" : true,
5789         /**
5790          * @event celldblclick
5791          * Fires when a cell is double clicked
5792          * @param {Roo.bootstrap.Table} this
5793          * @param {Roo.Element} el
5794          * @param {Number} rowIndex
5795          * @param {Number} columnIndex
5796          * @param {Roo.EventObject} e
5797          */
5798         "celldblclick" : true,
5799         /**
5800          * @event rowclick
5801          * Fires when a row is clicked
5802          * @param {Roo.bootstrap.Table} this
5803          * @param {Roo.Element} el
5804          * @param {Number} rowIndex
5805          * @param {Roo.EventObject} e
5806          */
5807         "rowclick" : true,
5808         /**
5809          * @event rowdblclick
5810          * Fires when a row is double clicked
5811          * @param {Roo.bootstrap.Table} this
5812          * @param {Roo.Element} el
5813          * @param {Number} rowIndex
5814          * @param {Roo.EventObject} e
5815          */
5816         "rowdblclick" : true,
5817         /**
5818          * @event mouseover
5819          * Fires when a mouseover occur
5820          * @param {Roo.bootstrap.Table} this
5821          * @param {Roo.Element} el
5822          * @param {Number} rowIndex
5823          * @param {Number} columnIndex
5824          * @param {Roo.EventObject} e
5825          */
5826         "mouseover" : true,
5827         /**
5828          * @event mouseout
5829          * Fires when a mouseout occur
5830          * @param {Roo.bootstrap.Table} this
5831          * @param {Roo.Element} el
5832          * @param {Number} rowIndex
5833          * @param {Number} columnIndex
5834          * @param {Roo.EventObject} e
5835          */
5836         "mouseout" : true,
5837         /**
5838          * @event rowclass
5839          * Fires when a row is rendered, so you can change add a style to it.
5840          * @param {Roo.bootstrap.Table} this
5841          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5842          */
5843         'rowclass' : true,
5844           /**
5845          * @event rowsrendered
5846          * Fires when all the  rows have been rendered
5847          * @param {Roo.bootstrap.Table} this
5848          */
5849         'rowsrendered' : true,
5850         /**
5851          * @event contextmenu
5852          * The raw contextmenu event for the entire grid.
5853          * @param {Roo.EventObject} e
5854          */
5855         "contextmenu" : true,
5856         /**
5857          * @event rowcontextmenu
5858          * Fires when a row is right clicked
5859          * @param {Roo.bootstrap.Table} this
5860          * @param {Number} rowIndex
5861          * @param {Roo.EventObject} e
5862          */
5863         "rowcontextmenu" : true,
5864         /**
5865          * @event cellcontextmenu
5866          * Fires when a cell is right clicked
5867          * @param {Roo.bootstrap.Table} this
5868          * @param {Number} rowIndex
5869          * @param {Number} cellIndex
5870          * @param {Roo.EventObject} e
5871          */
5872          "cellcontextmenu" : true,
5873          /**
5874          * @event headercontextmenu
5875          * Fires when a header is right clicked
5876          * @param {Roo.bootstrap.Table} this
5877          * @param {Number} columnIndex
5878          * @param {Roo.EventObject} e
5879          */
5880         "headercontextmenu" : true
5881     });
5882 };
5883
5884 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5885     
5886     cls: false,
5887     align: false,
5888     bgcolor: false,
5889     border: false,
5890     cellpadding: false,
5891     cellspacing: false,
5892     frame: false,
5893     rules: false,
5894     sortable: false,
5895     summary: false,
5896     width: false,
5897     striped : false,
5898     scrollBody : false,
5899     bordered: false,
5900     hover:  false,
5901     condensed : false,
5902     responsive : false,
5903     sm : false,
5904     cm : false,
5905     store : false,
5906     loadMask : false,
5907     footerShow : true,
5908     headerShow : true,
5909   
5910     rowSelection : false,
5911     cellSelection : false,
5912     layout : false,
5913     
5914     // Roo.Element - the tbody
5915     mainBody: false,
5916     // Roo.Element - thead element
5917     mainHead: false,
5918     
5919     container: false, // used by gridpanel...
5920     
5921     getAutoCreate : function()
5922     {
5923         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5924         
5925         cfg = {
5926             tag: 'table',
5927             cls : 'table',
5928             cn : []
5929         };
5930         if (this.scrollBody) {
5931             cfg.cls += ' table-body-fixed';
5932         }    
5933         if (this.striped) {
5934             cfg.cls += ' table-striped';
5935         }
5936         
5937         if (this.hover) {
5938             cfg.cls += ' table-hover';
5939         }
5940         if (this.bordered) {
5941             cfg.cls += ' table-bordered';
5942         }
5943         if (this.condensed) {
5944             cfg.cls += ' table-condensed';
5945         }
5946         if (this.responsive) {
5947             cfg.cls += ' table-responsive';
5948         }
5949         
5950         if (this.cls) {
5951             cfg.cls+=  ' ' +this.cls;
5952         }
5953         
5954         // this lot should be simplifed...
5955         
5956         if (this.align) {
5957             cfg.align=this.align;
5958         }
5959         if (this.bgcolor) {
5960             cfg.bgcolor=this.bgcolor;
5961         }
5962         if (this.border) {
5963             cfg.border=this.border;
5964         }
5965         if (this.cellpadding) {
5966             cfg.cellpadding=this.cellpadding;
5967         }
5968         if (this.cellspacing) {
5969             cfg.cellspacing=this.cellspacing;
5970         }
5971         if (this.frame) {
5972             cfg.frame=this.frame;
5973         }
5974         if (this.rules) {
5975             cfg.rules=this.rules;
5976         }
5977         if (this.sortable) {
5978             cfg.sortable=this.sortable;
5979         }
5980         if (this.summary) {
5981             cfg.summary=this.summary;
5982         }
5983         if (this.width) {
5984             cfg.width=this.width;
5985         }
5986         if (this.layout) {
5987             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5988         }
5989         
5990         if(this.store || this.cm){
5991             if(this.headerShow){
5992                 cfg.cn.push(this.renderHeader());
5993             }
5994             
5995             cfg.cn.push(this.renderBody());
5996             
5997             if(this.footerShow){
5998                 cfg.cn.push(this.renderFooter());
5999             }
6000             // where does this come from?
6001             //cfg.cls+=  ' TableGrid';
6002         }
6003         
6004         return { cn : [ cfg ] };
6005     },
6006     
6007     initEvents : function()
6008     {   
6009         if(!this.store || !this.cm){
6010             return;
6011         }
6012         if (this.selModel) {
6013             this.selModel.initEvents();
6014         }
6015         
6016         
6017         //Roo.log('initEvents with ds!!!!');
6018         
6019         this.mainBody = this.el.select('tbody', true).first();
6020         this.mainHead = this.el.select('thead', true).first();
6021         
6022         
6023         
6024         
6025         var _this = this;
6026         
6027         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6028             e.on('click', _this.sort, _this);
6029         });
6030         
6031         this.mainBody.on("click", this.onClick, this);
6032         this.mainBody.on("dblclick", this.onDblClick, this);
6033         
6034         // why is this done????? = it breaks dialogs??
6035         //this.parent().el.setStyle('position', 'relative');
6036         
6037         
6038         if (this.footer) {
6039             this.footer.parentId = this.id;
6040             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6041         } 
6042         
6043         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6044         
6045         this.store.on('load', this.onLoad, this);
6046         this.store.on('beforeload', this.onBeforeLoad, this);
6047         this.store.on('update', this.onUpdate, this);
6048         this.store.on('add', this.onAdd, this);
6049         this.store.on("clear", this.clear, this);
6050         
6051         this.el.on("contextmenu", this.onContextMenu, this);
6052         
6053         this.mainBody.on('scroll', this.onBodyScroll, this);
6054         
6055         
6056     },
6057     
6058     onContextMenu : function(e, t)
6059     {
6060         this.processEvent("contextmenu", e);
6061     },
6062     
6063     processEvent : function(name, e)
6064     {
6065         if (name != 'touchstart' ) {
6066             this.fireEvent(name, e);    
6067         }
6068         
6069         var t = e.getTarget();
6070         
6071         var cell = Roo.get(t);
6072         
6073         if(!cell){
6074             return;
6075         }
6076         
6077         if(cell.findParent('tfoot', false, true)){
6078             return;
6079         }
6080         
6081         if(cell.findParent('thead', false, true)){
6082             
6083             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6084                 cell = Roo.get(t).findParent('th', false, true);
6085                 if (!cell) {
6086                     Roo.log("failed to find th in thead?");
6087                     Roo.log(e.getTarget());
6088                     return;
6089                 }
6090             }
6091             
6092             var cellIndex = cell.dom.cellIndex;
6093             
6094             var ename = name == 'touchstart' ? 'click' : name;
6095             this.fireEvent("header" + ename, this, cellIndex, e);
6096             
6097             return;
6098         }
6099         
6100         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6101             cell = Roo.get(t).findParent('td', false, true);
6102             if (!cell) {
6103                 Roo.log("failed to find th in tbody?");
6104                 Roo.log(e.getTarget());
6105                 return;
6106             }
6107         }
6108         
6109         var row = cell.findParent('tr', false, true);
6110         var cellIndex = cell.dom.cellIndex;
6111         var rowIndex = row.dom.rowIndex - 1;
6112         
6113         if(row !== false){
6114             
6115             this.fireEvent("row" + name, this, rowIndex, e);
6116             
6117             if(cell !== false){
6118             
6119                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6120             }
6121         }
6122         
6123     },
6124     
6125     onMouseover : function(e, el)
6126     {
6127         var cell = Roo.get(el);
6128         
6129         if(!cell){
6130             return;
6131         }
6132         
6133         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6134             cell = cell.findParent('td', false, true);
6135         }
6136         
6137         var row = cell.findParent('tr', false, true);
6138         var cellIndex = cell.dom.cellIndex;
6139         var rowIndex = row.dom.rowIndex - 1; // start from 0
6140         
6141         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6142         
6143     },
6144     
6145     onMouseout : function(e, el)
6146     {
6147         var cell = Roo.get(el);
6148         
6149         if(!cell){
6150             return;
6151         }
6152         
6153         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6154             cell = cell.findParent('td', false, true);
6155         }
6156         
6157         var row = cell.findParent('tr', false, true);
6158         var cellIndex = cell.dom.cellIndex;
6159         var rowIndex = row.dom.rowIndex - 1; // start from 0
6160         
6161         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6162         
6163     },
6164     
6165     onClick : function(e, el)
6166     {
6167         var cell = Roo.get(el);
6168         
6169         if(!cell || (!this.cellSelection && !this.rowSelection)){
6170             return;
6171         }
6172         
6173         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6174             cell = cell.findParent('td', false, true);
6175         }
6176         
6177         if(!cell || typeof(cell) == 'undefined'){
6178             return;
6179         }
6180         
6181         var row = cell.findParent('tr', false, true);
6182         
6183         if(!row || typeof(row) == 'undefined'){
6184             return;
6185         }
6186         
6187         var cellIndex = cell.dom.cellIndex;
6188         var rowIndex = this.getRowIndex(row);
6189         
6190         // why??? - should these not be based on SelectionModel?
6191         if(this.cellSelection){
6192             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6193         }
6194         
6195         if(this.rowSelection){
6196             this.fireEvent('rowclick', this, row, rowIndex, e);
6197         }
6198         
6199         
6200     },
6201         
6202     onDblClick : function(e,el)
6203     {
6204         var cell = Roo.get(el);
6205         
6206         if(!cell || (!this.cellSelection && !this.rowSelection)){
6207             return;
6208         }
6209         
6210         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6211             cell = cell.findParent('td', false, true);
6212         }
6213         
6214         if(!cell || typeof(cell) == 'undefined'){
6215             return;
6216         }
6217         
6218         var row = cell.findParent('tr', false, true);
6219         
6220         if(!row || typeof(row) == 'undefined'){
6221             return;
6222         }
6223         
6224         var cellIndex = cell.dom.cellIndex;
6225         var rowIndex = this.getRowIndex(row);
6226         
6227         if(this.cellSelection){
6228             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6229         }
6230         
6231         if(this.rowSelection){
6232             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6233         }
6234     },
6235     
6236     sort : function(e,el)
6237     {
6238         var col = Roo.get(el);
6239         
6240         if(!col.hasClass('sortable')){
6241             return;
6242         }
6243         
6244         var sort = col.attr('sort');
6245         var dir = 'ASC';
6246         
6247         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6248             dir = 'DESC';
6249         }
6250         
6251         this.store.sortInfo = {field : sort, direction : dir};
6252         
6253         if (this.footer) {
6254             Roo.log("calling footer first");
6255             this.footer.onClick('first');
6256         } else {
6257         
6258             this.store.load({ params : { start : 0 } });
6259         }
6260     },
6261     
6262     renderHeader : function()
6263     {
6264         var header = {
6265             tag: 'thead',
6266             cn : []
6267         };
6268         
6269         var cm = this.cm;
6270         this.totalWidth = 0;
6271         
6272         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6273             
6274             var config = cm.config[i];
6275             
6276             var c = {
6277                 tag: 'th',
6278                 style : '',
6279                 html: cm.getColumnHeader(i)
6280             };
6281             
6282             var hh = '';
6283             
6284             if(typeof(config.sortable) != 'undefined' && config.sortable){
6285                 c.cls = 'sortable';
6286                 c.html = '<i class="glyphicon"></i>' + c.html;
6287             }
6288             
6289             if(typeof(config.lgHeader) != 'undefined'){
6290                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6291             }
6292             
6293             if(typeof(config.mdHeader) != 'undefined'){
6294                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6295             }
6296             
6297             if(typeof(config.smHeader) != 'undefined'){
6298                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6299             }
6300             
6301             if(typeof(config.xsHeader) != 'undefined'){
6302                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6303             }
6304             
6305             if(hh.length){
6306                 c.html = hh;
6307             }
6308             
6309             if(typeof(config.tooltip) != 'undefined'){
6310                 c.tooltip = config.tooltip;
6311             }
6312             
6313             if(typeof(config.colspan) != 'undefined'){
6314                 c.colspan = config.colspan;
6315             }
6316             
6317             if(typeof(config.hidden) != 'undefined' && config.hidden){
6318                 c.style += ' display:none;';
6319             }
6320             
6321             if(typeof(config.dataIndex) != 'undefined'){
6322                 c.sort = config.dataIndex;
6323             }
6324             
6325            
6326             
6327             if(typeof(config.align) != 'undefined' && config.align.length){
6328                 c.style += ' text-align:' + config.align + ';';
6329             }
6330             
6331             if(typeof(config.width) != 'undefined'){
6332                 c.style += ' width:' + config.width + 'px;';
6333                 this.totalWidth += config.width;
6334             } else {
6335                 this.totalWidth += 100; // assume minimum of 100 per column?
6336             }
6337             
6338             if(typeof(config.cls) != 'undefined'){
6339                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6340             }
6341             
6342             ['xs','sm','md','lg'].map(function(size){
6343                 
6344                 if(typeof(config[size]) == 'undefined'){
6345                     return;
6346                 }
6347                 
6348                 if (!config[size]) { // 0 = hidden
6349                     c.cls += ' hidden-' + size;
6350                     return;
6351                 }
6352                 
6353                 c.cls += ' col-' + size + '-' + config[size];
6354
6355             });
6356             
6357             header.cn.push(c)
6358         }
6359         
6360         return header;
6361     },
6362     
6363     renderBody : function()
6364     {
6365         var body = {
6366             tag: 'tbody',
6367             cn : [
6368                 {
6369                     tag: 'tr',
6370                     cn : [
6371                         {
6372                             tag : 'td',
6373                             colspan :  this.cm.getColumnCount()
6374                         }
6375                     ]
6376                 }
6377             ]
6378         };
6379         
6380         return body;
6381     },
6382     
6383     renderFooter : function()
6384     {
6385         var footer = {
6386             tag: 'tfoot',
6387             cn : [
6388                 {
6389                     tag: 'tr',
6390                     cn : [
6391                         {
6392                             tag : 'td',
6393                             colspan :  this.cm.getColumnCount()
6394                         }
6395                     ]
6396                 }
6397             ]
6398         };
6399         
6400         return footer;
6401     },
6402     
6403     
6404     
6405     onLoad : function()
6406     {
6407 //        Roo.log('ds onload');
6408         this.clear();
6409         
6410         var _this = this;
6411         var cm = this.cm;
6412         var ds = this.store;
6413         
6414         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6415             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6416             if (_this.store.sortInfo) {
6417                     
6418                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6419                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6420                 }
6421                 
6422                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6423                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6424                 }
6425             }
6426         });
6427         
6428         var tbody =  this.mainBody;
6429               
6430         if(ds.getCount() > 0){
6431             ds.data.each(function(d,rowIndex){
6432                 var row =  this.renderRow(cm, ds, rowIndex);
6433                 
6434                 tbody.createChild(row);
6435                 
6436                 var _this = this;
6437                 
6438                 if(row.cellObjects.length){
6439                     Roo.each(row.cellObjects, function(r){
6440                         _this.renderCellObject(r);
6441                     })
6442                 }
6443                 
6444             }, this);
6445         }
6446         
6447         Roo.each(this.el.select('tbody td', true).elements, function(e){
6448             e.on('mouseover', _this.onMouseover, _this);
6449         });
6450         
6451         Roo.each(this.el.select('tbody td', true).elements, function(e){
6452             e.on('mouseout', _this.onMouseout, _this);
6453         });
6454         this.fireEvent('rowsrendered', this);
6455         //if(this.loadMask){
6456         //    this.maskEl.hide();
6457         //}
6458         
6459         this.autoSize();
6460     },
6461     
6462     
6463     onUpdate : function(ds,record)
6464     {
6465         this.refreshRow(record);
6466         this.autoSize();
6467     },
6468     
6469     onRemove : function(ds, record, index, isUpdate){
6470         if(isUpdate !== true){
6471             this.fireEvent("beforerowremoved", this, index, record);
6472         }
6473         var bt = this.mainBody.dom;
6474         
6475         var rows = this.el.select('tbody > tr', true).elements;
6476         
6477         if(typeof(rows[index]) != 'undefined'){
6478             bt.removeChild(rows[index].dom);
6479         }
6480         
6481 //        if(bt.rows[index]){
6482 //            bt.removeChild(bt.rows[index]);
6483 //        }
6484         
6485         if(isUpdate !== true){
6486             //this.stripeRows(index);
6487             //this.syncRowHeights(index, index);
6488             //this.layout();
6489             this.fireEvent("rowremoved", this, index, record);
6490         }
6491     },
6492     
6493     onAdd : function(ds, records, rowIndex)
6494     {
6495         //Roo.log('on Add called');
6496         // - note this does not handle multiple adding very well..
6497         var bt = this.mainBody.dom;
6498         for (var i =0 ; i < records.length;i++) {
6499             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6500             //Roo.log(records[i]);
6501             //Roo.log(this.store.getAt(rowIndex+i));
6502             this.insertRow(this.store, rowIndex + i, false);
6503             return;
6504         }
6505         
6506     },
6507     
6508     
6509     refreshRow : function(record){
6510         var ds = this.store, index;
6511         if(typeof record == 'number'){
6512             index = record;
6513             record = ds.getAt(index);
6514         }else{
6515             index = ds.indexOf(record);
6516         }
6517         this.insertRow(ds, index, true);
6518         this.autoSize();
6519         this.onRemove(ds, record, index+1, true);
6520         this.autoSize();
6521         //this.syncRowHeights(index, index);
6522         //this.layout();
6523         this.fireEvent("rowupdated", this, index, record);
6524     },
6525     
6526     insertRow : function(dm, rowIndex, isUpdate){
6527         
6528         if(!isUpdate){
6529             this.fireEvent("beforerowsinserted", this, rowIndex);
6530         }
6531             //var s = this.getScrollState();
6532         var row = this.renderRow(this.cm, this.store, rowIndex);
6533         // insert before rowIndex..
6534         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6535         
6536         var _this = this;
6537                 
6538         if(row.cellObjects.length){
6539             Roo.each(row.cellObjects, function(r){
6540                 _this.renderCellObject(r);
6541             })
6542         }
6543             
6544         if(!isUpdate){
6545             this.fireEvent("rowsinserted", this, rowIndex);
6546             //this.syncRowHeights(firstRow, lastRow);
6547             //this.stripeRows(firstRow);
6548             //this.layout();
6549         }
6550         
6551     },
6552     
6553     
6554     getRowDom : function(rowIndex)
6555     {
6556         var rows = this.el.select('tbody > tr', true).elements;
6557         
6558         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6559         
6560     },
6561     // returns the object tree for a tr..
6562   
6563     
6564     renderRow : function(cm, ds, rowIndex) 
6565     {
6566         
6567         var d = ds.getAt(rowIndex);
6568         
6569         var row = {
6570             tag : 'tr',
6571             cn : []
6572         };
6573             
6574         var cellObjects = [];
6575         
6576         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6577             var config = cm.config[i];
6578             
6579             var renderer = cm.getRenderer(i);
6580             var value = '';
6581             var id = false;
6582             
6583             if(typeof(renderer) !== 'undefined'){
6584                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6585             }
6586             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6587             // and are rendered into the cells after the row is rendered - using the id for the element.
6588             
6589             if(typeof(value) === 'object'){
6590                 id = Roo.id();
6591                 cellObjects.push({
6592                     container : id,
6593                     cfg : value 
6594                 })
6595             }
6596             
6597             var rowcfg = {
6598                 record: d,
6599                 rowIndex : rowIndex,
6600                 colIndex : i,
6601                 rowClass : ''
6602             };
6603
6604             this.fireEvent('rowclass', this, rowcfg);
6605             
6606             var td = {
6607                 tag: 'td',
6608                 cls : rowcfg.rowClass,
6609                 style: '',
6610                 html: (typeof(value) === 'object') ? '' : value
6611             };
6612             
6613             if (id) {
6614                 td.id = id;
6615             }
6616             
6617             if(typeof(config.colspan) != 'undefined'){
6618                 td.colspan = config.colspan;
6619             }
6620             
6621             if(typeof(config.hidden) != 'undefined' && config.hidden){
6622                 td.style += ' display:none;';
6623             }
6624             
6625             if(typeof(config.align) != 'undefined' && config.align.length){
6626                 td.style += ' text-align:' + config.align + ';';
6627             }
6628             
6629             if(typeof(config.width) != 'undefined'){
6630                 td.style += ' width:' +  config.width + 'px;';
6631             }
6632             
6633             if(typeof(config.cursor) != 'undefined'){
6634                 td.style += ' cursor:' +  config.cursor + ';';
6635             }
6636             
6637             if(typeof(config.cls) != 'undefined'){
6638                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6639             }
6640             
6641             ['xs','sm','md','lg'].map(function(size){
6642                 
6643                 if(typeof(config[size]) == 'undefined'){
6644                     return;
6645                 }
6646                 
6647                 if (!config[size]) { // 0 = hidden
6648                     td.cls += ' hidden-' + size;
6649                     return;
6650                 }
6651                 
6652                 td.cls += ' col-' + size + '-' + config[size];
6653
6654             });
6655              
6656             row.cn.push(td);
6657            
6658         }
6659         
6660         row.cellObjects = cellObjects;
6661         
6662         return row;
6663           
6664     },
6665     
6666     
6667     
6668     onBeforeLoad : function()
6669     {
6670         //Roo.log('ds onBeforeLoad');
6671         
6672         //this.clear();
6673         
6674         //if(this.loadMask){
6675         //    this.maskEl.show();
6676         //}
6677     },
6678      /**
6679      * Remove all rows
6680      */
6681     clear : function()
6682     {
6683         this.el.select('tbody', true).first().dom.innerHTML = '';
6684     },
6685     /**
6686      * Show or hide a row.
6687      * @param {Number} rowIndex to show or hide
6688      * @param {Boolean} state hide
6689      */
6690     setRowVisibility : function(rowIndex, state)
6691     {
6692         var bt = this.mainBody.dom;
6693         
6694         var rows = this.el.select('tbody > tr', true).elements;
6695         
6696         if(typeof(rows[rowIndex]) == 'undefined'){
6697             return;
6698         }
6699         rows[rowIndex].dom.style.display = state ? '' : 'none';
6700     },
6701     
6702     
6703     getSelectionModel : function(){
6704         if(!this.selModel){
6705             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6706         }
6707         return this.selModel;
6708     },
6709     /*
6710      * Render the Roo.bootstrap object from renderder
6711      */
6712     renderCellObject : function(r)
6713     {
6714         var _this = this;
6715         
6716         var t = r.cfg.render(r.container);
6717         
6718         if(r.cfg.cn){
6719             Roo.each(r.cfg.cn, function(c){
6720                 var child = {
6721                     container: t.getChildContainer(),
6722                     cfg: c
6723                 };
6724                 _this.renderCellObject(child);
6725             })
6726         }
6727     },
6728     
6729     getRowIndex : function(row)
6730     {
6731         var rowIndex = -1;
6732         
6733         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6734             if(el != row){
6735                 return;
6736             }
6737             
6738             rowIndex = index;
6739         });
6740         
6741         return rowIndex;
6742     },
6743      /**
6744      * Returns the grid's underlying element = used by panel.Grid
6745      * @return {Element} The element
6746      */
6747     getGridEl : function(){
6748         return this.el;
6749     },
6750      /**
6751      * Forces a resize - used by panel.Grid
6752      * @return {Element} The element
6753      */
6754     autoSize : function()
6755     {
6756         //var ctr = Roo.get(this.container.dom.parentElement);
6757         var ctr = Roo.get(this.el.dom);
6758         
6759         var thd = this.getGridEl().select('thead',true).first();
6760         var tbd = this.getGridEl().select('tbody', true).first();
6761         var tfd = this.getGridEl().select('tfoot', true).first();
6762         
6763         var cw = ctr.getWidth();
6764         
6765         if (tbd) {
6766             
6767             tbd.setSize(ctr.getWidth(),
6768                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6769             );
6770             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6771             cw -= barsize;
6772         }
6773         cw = Math.max(cw, this.totalWidth);
6774         this.getGridEl().select('tr',true).setWidth(cw);
6775         // resize 'expandable coloumn?
6776         
6777         return; // we doe not have a view in this design..
6778         
6779     },
6780     onBodyScroll: function()
6781     {
6782         
6783         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6784         this.mainHead.setStyle({
6785                     'position' : 'relative',
6786                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6787         });
6788         
6789         
6790     }
6791 });
6792
6793  
6794
6795  /*
6796  * - LGPL
6797  *
6798  * table cell
6799  * 
6800  */
6801
6802 /**
6803  * @class Roo.bootstrap.TableCell
6804  * @extends Roo.bootstrap.Component
6805  * Bootstrap TableCell class
6806  * @cfg {String} html cell contain text
6807  * @cfg {String} cls cell class
6808  * @cfg {String} tag cell tag (td|th) default td
6809  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6810  * @cfg {String} align Aligns the content in a cell
6811  * @cfg {String} axis Categorizes cells
6812  * @cfg {String} bgcolor Specifies the background color of a cell
6813  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6814  * @cfg {Number} colspan Specifies the number of columns a cell should span
6815  * @cfg {String} headers Specifies one or more header cells a cell is related to
6816  * @cfg {Number} height Sets the height of a cell
6817  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6818  * @cfg {Number} rowspan Sets the number of rows a cell should span
6819  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6820  * @cfg {String} valign Vertical aligns the content in a cell
6821  * @cfg {Number} width Specifies the width of a cell
6822  * 
6823  * @constructor
6824  * Create a new TableCell
6825  * @param {Object} config The config object
6826  */
6827
6828 Roo.bootstrap.TableCell = function(config){
6829     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6830 };
6831
6832 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6833     
6834     html: false,
6835     cls: false,
6836     tag: false,
6837     abbr: false,
6838     align: false,
6839     axis: false,
6840     bgcolor: false,
6841     charoff: false,
6842     colspan: false,
6843     headers: false,
6844     height: false,
6845     nowrap: false,
6846     rowspan: false,
6847     scope: false,
6848     valign: false,
6849     width: false,
6850     
6851     
6852     getAutoCreate : function(){
6853         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6854         
6855         cfg = {
6856             tag: 'td'
6857         };
6858         
6859         if(this.tag){
6860             cfg.tag = this.tag;
6861         }
6862         
6863         if (this.html) {
6864             cfg.html=this.html
6865         }
6866         if (this.cls) {
6867             cfg.cls=this.cls
6868         }
6869         if (this.abbr) {
6870             cfg.abbr=this.abbr
6871         }
6872         if (this.align) {
6873             cfg.align=this.align
6874         }
6875         if (this.axis) {
6876             cfg.axis=this.axis
6877         }
6878         if (this.bgcolor) {
6879             cfg.bgcolor=this.bgcolor
6880         }
6881         if (this.charoff) {
6882             cfg.charoff=this.charoff
6883         }
6884         if (this.colspan) {
6885             cfg.colspan=this.colspan
6886         }
6887         if (this.headers) {
6888             cfg.headers=this.headers
6889         }
6890         if (this.height) {
6891             cfg.height=this.height
6892         }
6893         if (this.nowrap) {
6894             cfg.nowrap=this.nowrap
6895         }
6896         if (this.rowspan) {
6897             cfg.rowspan=this.rowspan
6898         }
6899         if (this.scope) {
6900             cfg.scope=this.scope
6901         }
6902         if (this.valign) {
6903             cfg.valign=this.valign
6904         }
6905         if (this.width) {
6906             cfg.width=this.width
6907         }
6908         
6909         
6910         return cfg;
6911     }
6912    
6913 });
6914
6915  
6916
6917  /*
6918  * - LGPL
6919  *
6920  * table row
6921  * 
6922  */
6923
6924 /**
6925  * @class Roo.bootstrap.TableRow
6926  * @extends Roo.bootstrap.Component
6927  * Bootstrap TableRow class
6928  * @cfg {String} cls row class
6929  * @cfg {String} align Aligns the content in a table row
6930  * @cfg {String} bgcolor Specifies a background color for a table row
6931  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6932  * @cfg {String} valign Vertical aligns the content in a table row
6933  * 
6934  * @constructor
6935  * Create a new TableRow
6936  * @param {Object} config The config object
6937  */
6938
6939 Roo.bootstrap.TableRow = function(config){
6940     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6941 };
6942
6943 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6944     
6945     cls: false,
6946     align: false,
6947     bgcolor: false,
6948     charoff: false,
6949     valign: false,
6950     
6951     getAutoCreate : function(){
6952         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6953         
6954         cfg = {
6955             tag: 'tr'
6956         };
6957             
6958         if(this.cls){
6959             cfg.cls = this.cls;
6960         }
6961         if(this.align){
6962             cfg.align = this.align;
6963         }
6964         if(this.bgcolor){
6965             cfg.bgcolor = this.bgcolor;
6966         }
6967         if(this.charoff){
6968             cfg.charoff = this.charoff;
6969         }
6970         if(this.valign){
6971             cfg.valign = this.valign;
6972         }
6973         
6974         return cfg;
6975     }
6976    
6977 });
6978
6979  
6980
6981  /*
6982  * - LGPL
6983  *
6984  * table body
6985  * 
6986  */
6987
6988 /**
6989  * @class Roo.bootstrap.TableBody
6990  * @extends Roo.bootstrap.Component
6991  * Bootstrap TableBody class
6992  * @cfg {String} cls element class
6993  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6994  * @cfg {String} align Aligns the content inside the element
6995  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6996  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6997  * 
6998  * @constructor
6999  * Create a new TableBody
7000  * @param {Object} config The config object
7001  */
7002
7003 Roo.bootstrap.TableBody = function(config){
7004     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7005 };
7006
7007 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7008     
7009     cls: false,
7010     tag: false,
7011     align: false,
7012     charoff: false,
7013     valign: false,
7014     
7015     getAutoCreate : function(){
7016         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7017         
7018         cfg = {
7019             tag: 'tbody'
7020         };
7021             
7022         if (this.cls) {
7023             cfg.cls=this.cls
7024         }
7025         if(this.tag){
7026             cfg.tag = this.tag;
7027         }
7028         
7029         if(this.align){
7030             cfg.align = this.align;
7031         }
7032         if(this.charoff){
7033             cfg.charoff = this.charoff;
7034         }
7035         if(this.valign){
7036             cfg.valign = this.valign;
7037         }
7038         
7039         return cfg;
7040     }
7041     
7042     
7043 //    initEvents : function()
7044 //    {
7045 //        
7046 //        if(!this.store){
7047 //            return;
7048 //        }
7049 //        
7050 //        this.store = Roo.factory(this.store, Roo.data);
7051 //        this.store.on('load', this.onLoad, this);
7052 //        
7053 //        this.store.load();
7054 //        
7055 //    },
7056 //    
7057 //    onLoad: function () 
7058 //    {   
7059 //        this.fireEvent('load', this);
7060 //    }
7061 //    
7062 //   
7063 });
7064
7065  
7066
7067  /*
7068  * Based on:
7069  * Ext JS Library 1.1.1
7070  * Copyright(c) 2006-2007, Ext JS, LLC.
7071  *
7072  * Originally Released Under LGPL - original licence link has changed is not relivant.
7073  *
7074  * Fork - LGPL
7075  * <script type="text/javascript">
7076  */
7077
7078 // as we use this in bootstrap.
7079 Roo.namespace('Roo.form');
7080  /**
7081  * @class Roo.form.Action
7082  * Internal Class used to handle form actions
7083  * @constructor
7084  * @param {Roo.form.BasicForm} el The form element or its id
7085  * @param {Object} config Configuration options
7086  */
7087
7088  
7089  
7090 // define the action interface
7091 Roo.form.Action = function(form, options){
7092     this.form = form;
7093     this.options = options || {};
7094 };
7095 /**
7096  * Client Validation Failed
7097  * @const 
7098  */
7099 Roo.form.Action.CLIENT_INVALID = 'client';
7100 /**
7101  * Server Validation Failed
7102  * @const 
7103  */
7104 Roo.form.Action.SERVER_INVALID = 'server';
7105  /**
7106  * Connect to Server Failed
7107  * @const 
7108  */
7109 Roo.form.Action.CONNECT_FAILURE = 'connect';
7110 /**
7111  * Reading Data from Server Failed
7112  * @const 
7113  */
7114 Roo.form.Action.LOAD_FAILURE = 'load';
7115
7116 Roo.form.Action.prototype = {
7117     type : 'default',
7118     failureType : undefined,
7119     response : undefined,
7120     result : undefined,
7121
7122     // interface method
7123     run : function(options){
7124
7125     },
7126
7127     // interface method
7128     success : function(response){
7129
7130     },
7131
7132     // interface method
7133     handleResponse : function(response){
7134
7135     },
7136
7137     // default connection failure
7138     failure : function(response){
7139         
7140         this.response = response;
7141         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7142         this.form.afterAction(this, false);
7143     },
7144
7145     processResponse : function(response){
7146         this.response = response;
7147         if(!response.responseText){
7148             return true;
7149         }
7150         this.result = this.handleResponse(response);
7151         return this.result;
7152     },
7153
7154     // utility functions used internally
7155     getUrl : function(appendParams){
7156         var url = this.options.url || this.form.url || this.form.el.dom.action;
7157         if(appendParams){
7158             var p = this.getParams();
7159             if(p){
7160                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7161             }
7162         }
7163         return url;
7164     },
7165
7166     getMethod : function(){
7167         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7168     },
7169
7170     getParams : function(){
7171         var bp = this.form.baseParams;
7172         var p = this.options.params;
7173         if(p){
7174             if(typeof p == "object"){
7175                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7176             }else if(typeof p == 'string' && bp){
7177                 p += '&' + Roo.urlEncode(bp);
7178             }
7179         }else if(bp){
7180             p = Roo.urlEncode(bp);
7181         }
7182         return p;
7183     },
7184
7185     createCallback : function(){
7186         return {
7187             success: this.success,
7188             failure: this.failure,
7189             scope: this,
7190             timeout: (this.form.timeout*1000),
7191             upload: this.form.fileUpload ? this.success : undefined
7192         };
7193     }
7194 };
7195
7196 Roo.form.Action.Submit = function(form, options){
7197     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7198 };
7199
7200 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7201     type : 'submit',
7202
7203     haveProgress : false,
7204     uploadComplete : false,
7205     
7206     // uploadProgress indicator.
7207     uploadProgress : function()
7208     {
7209         if (!this.form.progressUrl) {
7210             return;
7211         }
7212         
7213         if (!this.haveProgress) {
7214             Roo.MessageBox.progress("Uploading", "Uploading");
7215         }
7216         if (this.uploadComplete) {
7217            Roo.MessageBox.hide();
7218            return;
7219         }
7220         
7221         this.haveProgress = true;
7222    
7223         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7224         
7225         var c = new Roo.data.Connection();
7226         c.request({
7227             url : this.form.progressUrl,
7228             params: {
7229                 id : uid
7230             },
7231             method: 'GET',
7232             success : function(req){
7233                //console.log(data);
7234                 var rdata = false;
7235                 var edata;
7236                 try  {
7237                    rdata = Roo.decode(req.responseText)
7238                 } catch (e) {
7239                     Roo.log("Invalid data from server..");
7240                     Roo.log(edata);
7241                     return;
7242                 }
7243                 if (!rdata || !rdata.success) {
7244                     Roo.log(rdata);
7245                     Roo.MessageBox.alert(Roo.encode(rdata));
7246                     return;
7247                 }
7248                 var data = rdata.data;
7249                 
7250                 if (this.uploadComplete) {
7251                    Roo.MessageBox.hide();
7252                    return;
7253                 }
7254                    
7255                 if (data){
7256                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7257                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7258                     );
7259                 }
7260                 this.uploadProgress.defer(2000,this);
7261             },
7262        
7263             failure: function(data) {
7264                 Roo.log('progress url failed ');
7265                 Roo.log(data);
7266             },
7267             scope : this
7268         });
7269            
7270     },
7271     
7272     
7273     run : function()
7274     {
7275         // run get Values on the form, so it syncs any secondary forms.
7276         this.form.getValues();
7277         
7278         var o = this.options;
7279         var method = this.getMethod();
7280         var isPost = method == 'POST';
7281         if(o.clientValidation === false || this.form.isValid()){
7282             
7283             if (this.form.progressUrl) {
7284                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7285                     (new Date() * 1) + '' + Math.random());
7286                     
7287             } 
7288             
7289             
7290             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7291                 form:this.form.el.dom,
7292                 url:this.getUrl(!isPost),
7293                 method: method,
7294                 params:isPost ? this.getParams() : null,
7295                 isUpload: this.form.fileUpload
7296             }));
7297             
7298             this.uploadProgress();
7299
7300         }else if (o.clientValidation !== false){ // client validation failed
7301             this.failureType = Roo.form.Action.CLIENT_INVALID;
7302             this.form.afterAction(this, false);
7303         }
7304     },
7305
7306     success : function(response)
7307     {
7308         this.uploadComplete= true;
7309         if (this.haveProgress) {
7310             Roo.MessageBox.hide();
7311         }
7312         
7313         
7314         var result = this.processResponse(response);
7315         if(result === true || result.success){
7316             this.form.afterAction(this, true);
7317             return;
7318         }
7319         if(result.errors){
7320             this.form.markInvalid(result.errors);
7321             this.failureType = Roo.form.Action.SERVER_INVALID;
7322         }
7323         this.form.afterAction(this, false);
7324     },
7325     failure : function(response)
7326     {
7327         this.uploadComplete= true;
7328         if (this.haveProgress) {
7329             Roo.MessageBox.hide();
7330         }
7331         
7332         this.response = response;
7333         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7334         this.form.afterAction(this, false);
7335     },
7336     
7337     handleResponse : function(response){
7338         if(this.form.errorReader){
7339             var rs = this.form.errorReader.read(response);
7340             var errors = [];
7341             if(rs.records){
7342                 for(var i = 0, len = rs.records.length; i < len; i++) {
7343                     var r = rs.records[i];
7344                     errors[i] = r.data;
7345                 }
7346             }
7347             if(errors.length < 1){
7348                 errors = null;
7349             }
7350             return {
7351                 success : rs.success,
7352                 errors : errors
7353             };
7354         }
7355         var ret = false;
7356         try {
7357             ret = Roo.decode(response.responseText);
7358         } catch (e) {
7359             ret = {
7360                 success: false,
7361                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7362                 errors : []
7363             };
7364         }
7365         return ret;
7366         
7367     }
7368 });
7369
7370
7371 Roo.form.Action.Load = function(form, options){
7372     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7373     this.reader = this.form.reader;
7374 };
7375
7376 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7377     type : 'load',
7378
7379     run : function(){
7380         
7381         Roo.Ajax.request(Roo.apply(
7382                 this.createCallback(), {
7383                     method:this.getMethod(),
7384                     url:this.getUrl(false),
7385                     params:this.getParams()
7386         }));
7387     },
7388
7389     success : function(response){
7390         
7391         var result = this.processResponse(response);
7392         if(result === true || !result.success || !result.data){
7393             this.failureType = Roo.form.Action.LOAD_FAILURE;
7394             this.form.afterAction(this, false);
7395             return;
7396         }
7397         this.form.clearInvalid();
7398         this.form.setValues(result.data);
7399         this.form.afterAction(this, true);
7400     },
7401
7402     handleResponse : function(response){
7403         if(this.form.reader){
7404             var rs = this.form.reader.read(response);
7405             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7406             return {
7407                 success : rs.success,
7408                 data : data
7409             };
7410         }
7411         return Roo.decode(response.responseText);
7412     }
7413 });
7414
7415 Roo.form.Action.ACTION_TYPES = {
7416     'load' : Roo.form.Action.Load,
7417     'submit' : Roo.form.Action.Submit
7418 };/*
7419  * - LGPL
7420  *
7421  * form
7422  *
7423  */
7424
7425 /**
7426  * @class Roo.bootstrap.Form
7427  * @extends Roo.bootstrap.Component
7428  * Bootstrap Form class
7429  * @cfg {String} method  GET | POST (default POST)
7430  * @cfg {String} labelAlign top | left (default top)
7431  * @cfg {String} align left  | right - for navbars
7432  * @cfg {Boolean} loadMask load mask when submit (default true)
7433
7434  *
7435  * @constructor
7436  * Create a new Form
7437  * @param {Object} config The config object
7438  */
7439
7440
7441 Roo.bootstrap.Form = function(config){
7442     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7443     this.addEvents({
7444         /**
7445          * @event clientvalidation
7446          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7447          * @param {Form} this
7448          * @param {Boolean} valid true if the form has passed client-side validation
7449          */
7450         clientvalidation: true,
7451         /**
7452          * @event beforeaction
7453          * Fires before any action is performed. Return false to cancel the action.
7454          * @param {Form} this
7455          * @param {Action} action The action to be performed
7456          */
7457         beforeaction: true,
7458         /**
7459          * @event actionfailed
7460          * Fires when an action fails.
7461          * @param {Form} this
7462          * @param {Action} action The action that failed
7463          */
7464         actionfailed : true,
7465         /**
7466          * @event actioncomplete
7467          * Fires when an action is completed.
7468          * @param {Form} this
7469          * @param {Action} action The action that completed
7470          */
7471         actioncomplete : true
7472     });
7473
7474 };
7475
7476 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7477
7478      /**
7479      * @cfg {String} method
7480      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7481      */
7482     method : 'POST',
7483     /**
7484      * @cfg {String} url
7485      * The URL to use for form actions if one isn't supplied in the action options.
7486      */
7487     /**
7488      * @cfg {Boolean} fileUpload
7489      * Set to true if this form is a file upload.
7490      */
7491
7492     /**
7493      * @cfg {Object} baseParams
7494      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7495      */
7496
7497     /**
7498      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7499      */
7500     timeout: 30,
7501     /**
7502      * @cfg {Sting} align (left|right) for navbar forms
7503      */
7504     align : 'left',
7505
7506     // private
7507     activeAction : null,
7508
7509     /**
7510      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7511      * element by passing it or its id or mask the form itself by passing in true.
7512      * @type Mixed
7513      */
7514     waitMsgTarget : false,
7515
7516     loadMask : true,
7517
7518     getAutoCreate : function(){
7519
7520         var cfg = {
7521             tag: 'form',
7522             method : this.method || 'POST',
7523             id : this.id || Roo.id(),
7524             cls : ''
7525         };
7526         if (this.parent().xtype.match(/^Nav/)) {
7527             cfg.cls = 'navbar-form navbar-' + this.align;
7528
7529         }
7530
7531         if (this.labelAlign == 'left' ) {
7532             cfg.cls += ' form-horizontal';
7533         }
7534
7535
7536         return cfg;
7537     },
7538     initEvents : function()
7539     {
7540         this.el.on('submit', this.onSubmit, this);
7541         // this was added as random key presses on the form where triggering form submit.
7542         this.el.on('keypress', function(e) {
7543             if (e.getCharCode() != 13) {
7544                 return true;
7545             }
7546             // we might need to allow it for textareas.. and some other items.
7547             // check e.getTarget().
7548
7549             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7550                 return true;
7551             }
7552
7553             Roo.log("keypress blocked");
7554
7555             e.preventDefault();
7556             return false;
7557         });
7558
7559     },
7560     // private
7561     onSubmit : function(e){
7562         e.stopEvent();
7563     },
7564
7565      /**
7566      * Returns true if client-side validation on the form is successful.
7567      * @return Boolean
7568      */
7569     isValid : function(){
7570         var items = this.getItems();
7571         var valid = true;
7572         items.each(function(f){
7573            if(!f.validate()){
7574                valid = false;
7575
7576            }
7577         });
7578         return valid;
7579     },
7580     /**
7581      * Returns true if any fields in this form have changed since their original load.
7582      * @return Boolean
7583      */
7584     isDirty : function(){
7585         var dirty = false;
7586         var items = this.getItems();
7587         items.each(function(f){
7588            if(f.isDirty()){
7589                dirty = true;
7590                return false;
7591            }
7592            return true;
7593         });
7594         return dirty;
7595     },
7596      /**
7597      * Performs a predefined action (submit or load) or custom actions you define on this form.
7598      * @param {String} actionName The name of the action type
7599      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7600      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7601      * accept other config options):
7602      * <pre>
7603 Property          Type             Description
7604 ----------------  ---------------  ----------------------------------------------------------------------------------
7605 url               String           The url for the action (defaults to the form's url)
7606 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7607 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7608 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7609                                    validate the form on the client (defaults to false)
7610      * </pre>
7611      * @return {BasicForm} this
7612      */
7613     doAction : function(action, options){
7614         if(typeof action == 'string'){
7615             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7616         }
7617         if(this.fireEvent('beforeaction', this, action) !== false){
7618             this.beforeAction(action);
7619             action.run.defer(100, action);
7620         }
7621         return this;
7622     },
7623
7624     // private
7625     beforeAction : function(action){
7626         var o = action.options;
7627
7628         if(this.loadMask){
7629             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7630         }
7631         // not really supported yet.. ??
7632
7633         //if(this.waitMsgTarget === true){
7634         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7635         //}else if(this.waitMsgTarget){
7636         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7637         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7638         //}else {
7639         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7640        // }
7641
7642     },
7643
7644     // private
7645     afterAction : function(action, success){
7646         this.activeAction = null;
7647         var o = action.options;
7648
7649         //if(this.waitMsgTarget === true){
7650             this.el.unmask();
7651         //}else if(this.waitMsgTarget){
7652         //    this.waitMsgTarget.unmask();
7653         //}else{
7654         //    Roo.MessageBox.updateProgress(1);
7655         //    Roo.MessageBox.hide();
7656        // }
7657         //
7658         if(success){
7659             if(o.reset){
7660                 this.reset();
7661             }
7662             Roo.callback(o.success, o.scope, [this, action]);
7663             this.fireEvent('actioncomplete', this, action);
7664
7665         }else{
7666
7667             // failure condition..
7668             // we have a scenario where updates need confirming.
7669             // eg. if a locking scenario exists..
7670             // we look for { errors : { needs_confirm : true }} in the response.
7671             if (
7672                 (typeof(action.result) != 'undefined')  &&
7673                 (typeof(action.result.errors) != 'undefined')  &&
7674                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7675            ){
7676                 var _t = this;
7677                 Roo.log("not supported yet");
7678                  /*
7679
7680                 Roo.MessageBox.confirm(
7681                     "Change requires confirmation",
7682                     action.result.errorMsg,
7683                     function(r) {
7684                         if (r != 'yes') {
7685                             return;
7686                         }
7687                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7688                     }
7689
7690                 );
7691                 */
7692
7693
7694                 return;
7695             }
7696
7697             Roo.callback(o.failure, o.scope, [this, action]);
7698             // show an error message if no failed handler is set..
7699             if (!this.hasListener('actionfailed')) {
7700                 Roo.log("need to add dialog support");
7701                 /*
7702                 Roo.MessageBox.alert("Error",
7703                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7704                         action.result.errorMsg :
7705                         "Saving Failed, please check your entries or try again"
7706                 );
7707                 */
7708             }
7709
7710             this.fireEvent('actionfailed', this, action);
7711         }
7712
7713     },
7714     /**
7715      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7716      * @param {String} id The value to search for
7717      * @return Field
7718      */
7719     findField : function(id){
7720         var items = this.getItems();
7721         var field = items.get(id);
7722         if(!field){
7723              items.each(function(f){
7724                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7725                     field = f;
7726                     return false;
7727                 }
7728                 return true;
7729             });
7730         }
7731         return field || null;
7732     },
7733      /**
7734      * Mark fields in this form invalid in bulk.
7735      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7736      * @return {BasicForm} this
7737      */
7738     markInvalid : function(errors){
7739         if(errors instanceof Array){
7740             for(var i = 0, len = errors.length; i < len; i++){
7741                 var fieldError = errors[i];
7742                 var f = this.findField(fieldError.id);
7743                 if(f){
7744                     f.markInvalid(fieldError.msg);
7745                 }
7746             }
7747         }else{
7748             var field, id;
7749             for(id in errors){
7750                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7751                     field.markInvalid(errors[id]);
7752                 }
7753             }
7754         }
7755         //Roo.each(this.childForms || [], function (f) {
7756         //    f.markInvalid(errors);
7757         //});
7758
7759         return this;
7760     },
7761
7762     /**
7763      * Set values for fields in this form in bulk.
7764      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7765      * @return {BasicForm} this
7766      */
7767     setValues : function(values){
7768         if(values instanceof Array){ // array of objects
7769             for(var i = 0, len = values.length; i < len; i++){
7770                 var v = values[i];
7771                 var f = this.findField(v.id);
7772                 if(f){
7773                     f.setValue(v.value);
7774                     if(this.trackResetOnLoad){
7775                         f.originalValue = f.getValue();
7776                     }
7777                 }
7778             }
7779         }else{ // object hash
7780             var field, id;
7781             for(id in values){
7782                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7783
7784                     if (field.setFromData &&
7785                         field.valueField &&
7786                         field.displayField &&
7787                         // combos' with local stores can
7788                         // be queried via setValue()
7789                         // to set their value..
7790                         (field.store && !field.store.isLocal)
7791                         ) {
7792                         // it's a combo
7793                         var sd = { };
7794                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7795                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7796                         field.setFromData(sd);
7797
7798                     } else {
7799                         field.setValue(values[id]);
7800                     }
7801
7802
7803                     if(this.trackResetOnLoad){
7804                         field.originalValue = field.getValue();
7805                     }
7806                 }
7807             }
7808         }
7809
7810         //Roo.each(this.childForms || [], function (f) {
7811         //    f.setValues(values);
7812         //});
7813
7814         return this;
7815     },
7816
7817     /**
7818      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7819      * they are returned as an array.
7820      * @param {Boolean} asString
7821      * @return {Object}
7822      */
7823     getValues : function(asString){
7824         //if (this.childForms) {
7825             // copy values from the child forms
7826         //    Roo.each(this.childForms, function (f) {
7827         //        this.setValues(f.getValues());
7828         //    }, this);
7829         //}
7830
7831
7832
7833         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7834         if(asString === true){
7835             return fs;
7836         }
7837         return Roo.urlDecode(fs);
7838     },
7839
7840     /**
7841      * Returns the fields in this form as an object with key/value pairs.
7842      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7843      * @return {Object}
7844      */
7845     getFieldValues : function(with_hidden)
7846     {
7847         var items = this.getItems();
7848         var ret = {};
7849         items.each(function(f){
7850             if (!f.getName()) {
7851                 return;
7852             }
7853             var v = f.getValue();
7854             if (f.inputType =='radio') {
7855                 if (typeof(ret[f.getName()]) == 'undefined') {
7856                     ret[f.getName()] = ''; // empty..
7857                 }
7858
7859                 if (!f.el.dom.checked) {
7860                     return;
7861
7862                 }
7863                 v = f.el.dom.value;
7864
7865             }
7866
7867             // not sure if this supported any more..
7868             if ((typeof(v) == 'object') && f.getRawValue) {
7869                 v = f.getRawValue() ; // dates..
7870             }
7871             // combo boxes where name != hiddenName...
7872             if (f.name != f.getName()) {
7873                 ret[f.name] = f.getRawValue();
7874             }
7875             ret[f.getName()] = v;
7876         });
7877
7878         return ret;
7879     },
7880
7881     /**
7882      * Clears all invalid messages in this form.
7883      * @return {BasicForm} this
7884      */
7885     clearInvalid : function(){
7886         var items = this.getItems();
7887
7888         items.each(function(f){
7889            f.clearInvalid();
7890         });
7891
7892
7893
7894         return this;
7895     },
7896
7897     /**
7898      * Resets this form.
7899      * @return {BasicForm} this
7900      */
7901     reset : function(){
7902         var items = this.getItems();
7903         items.each(function(f){
7904             f.reset();
7905         });
7906
7907         Roo.each(this.childForms || [], function (f) {
7908             f.reset();
7909         });
7910
7911
7912         return this;
7913     },
7914     getItems : function()
7915     {
7916         var r=new Roo.util.MixedCollection(false, function(o){
7917             return o.id || (o.id = Roo.id());
7918         });
7919         var iter = function(el) {
7920             if (el.inputEl) {
7921                 r.add(el);
7922             }
7923             if (!el.items) {
7924                 return;
7925             }
7926             Roo.each(el.items,function(e) {
7927                 iter(e);
7928             });
7929
7930
7931         };
7932
7933         iter(this);
7934         return r;
7935
7936
7937
7938
7939     }
7940
7941 });
7942 /*
7943  * Based on:
7944  * Ext JS Library 1.1.1
7945  * Copyright(c) 2006-2007, Ext JS, LLC.
7946  *
7947  * Originally Released Under LGPL - original licence link has changed is not relivant.
7948  *
7949  * Fork - LGPL
7950  * <script type="text/javascript">
7951  */
7952 /**
7953  * @class Roo.form.VTypes
7954  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7955  * @singleton
7956  */
7957 Roo.form.VTypes = function(){
7958     // closure these in so they are only created once.
7959     var alpha = /^[a-zA-Z_]+$/;
7960     var alphanum = /^[a-zA-Z0-9_]+$/;
7961     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7962     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7963
7964     // All these messages and functions are configurable
7965     return {
7966         /**
7967          * The function used to validate email addresses
7968          * @param {String} value The email address
7969          */
7970         'email' : function(v){
7971             return email.test(v);
7972         },
7973         /**
7974          * The error text to display when the email validation function returns false
7975          * @type String
7976          */
7977         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7978         /**
7979          * The keystroke filter mask to be applied on email input
7980          * @type RegExp
7981          */
7982         'emailMask' : /[a-z0-9_\.\-@]/i,
7983
7984         /**
7985          * The function used to validate URLs
7986          * @param {String} value The URL
7987          */
7988         'url' : function(v){
7989             return url.test(v);
7990         },
7991         /**
7992          * The error text to display when the url validation function returns false
7993          * @type String
7994          */
7995         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7996         
7997         /**
7998          * The function used to validate alpha values
7999          * @param {String} value The value
8000          */
8001         'alpha' : function(v){
8002             return alpha.test(v);
8003         },
8004         /**
8005          * The error text to display when the alpha validation function returns false
8006          * @type String
8007          */
8008         'alphaText' : 'This field should only contain letters and _',
8009         /**
8010          * The keystroke filter mask to be applied on alpha input
8011          * @type RegExp
8012          */
8013         'alphaMask' : /[a-z_]/i,
8014
8015         /**
8016          * The function used to validate alphanumeric values
8017          * @param {String} value The value
8018          */
8019         'alphanum' : function(v){
8020             return alphanum.test(v);
8021         },
8022         /**
8023          * The error text to display when the alphanumeric validation function returns false
8024          * @type String
8025          */
8026         'alphanumText' : 'This field should only contain letters, numbers and _',
8027         /**
8028          * The keystroke filter mask to be applied on alphanumeric input
8029          * @type RegExp
8030          */
8031         'alphanumMask' : /[a-z0-9_]/i
8032     };
8033 }();/*
8034  * - LGPL
8035  *
8036  * Input
8037  * 
8038  */
8039
8040 /**
8041  * @class Roo.bootstrap.Input
8042  * @extends Roo.bootstrap.Component
8043  * Bootstrap Input class
8044  * @cfg {Boolean} disabled is it disabled
8045  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8046  * @cfg {String} name name of the input
8047  * @cfg {string} fieldLabel - the label associated
8048  * @cfg {string} placeholder - placeholder to put in text.
8049  * @cfg {string}  before - input group add on before
8050  * @cfg {string} after - input group add on after
8051  * @cfg {string} size - (lg|sm) or leave empty..
8052  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8053  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8054  * @cfg {Number} md colspan out of 12 for computer-sized screens
8055  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8056  * @cfg {string} value default value of the input
8057  * @cfg {Number} labelWidth set the width of label (0-12)
8058  * @cfg {String} labelAlign (top|left)
8059  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8060  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8061  * @cfg {String} indicatorpos (left|right) default left
8062
8063  * @cfg {String} align (left|center|right) Default left
8064  * @cfg {Boolean} forceFeedback (true|false) Default false
8065  * 
8066  * 
8067  * 
8068  * 
8069  * @constructor
8070  * Create a new Input
8071  * @param {Object} config The config object
8072  */
8073
8074 Roo.bootstrap.Input = function(config){
8075     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8076    
8077         this.addEvents({
8078             /**
8079              * @event focus
8080              * Fires when this field receives input focus.
8081              * @param {Roo.form.Field} this
8082              */
8083             focus : true,
8084             /**
8085              * @event blur
8086              * Fires when this field loses input focus.
8087              * @param {Roo.form.Field} this
8088              */
8089             blur : true,
8090             /**
8091              * @event specialkey
8092              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8093              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8094              * @param {Roo.form.Field} this
8095              * @param {Roo.EventObject} e The event object
8096              */
8097             specialkey : true,
8098             /**
8099              * @event change
8100              * Fires just before the field blurs if the field value has changed.
8101              * @param {Roo.form.Field} this
8102              * @param {Mixed} newValue The new value
8103              * @param {Mixed} oldValue The original value
8104              */
8105             change : true,
8106             /**
8107              * @event invalid
8108              * Fires after the field has been marked as invalid.
8109              * @param {Roo.form.Field} this
8110              * @param {String} msg The validation message
8111              */
8112             invalid : true,
8113             /**
8114              * @event valid
8115              * Fires after the field has been validated with no errors.
8116              * @param {Roo.form.Field} this
8117              */
8118             valid : true,
8119              /**
8120              * @event keyup
8121              * Fires after the key up
8122              * @param {Roo.form.Field} this
8123              * @param {Roo.EventObject}  e The event Object
8124              */
8125             keyup : true
8126         });
8127 };
8128
8129 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8130      /**
8131      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8132       automatic validation (defaults to "keyup").
8133      */
8134     validationEvent : "keyup",
8135      /**
8136      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8137      */
8138     validateOnBlur : true,
8139     /**
8140      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8141      */
8142     validationDelay : 250,
8143      /**
8144      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8145      */
8146     focusClass : "x-form-focus",  // not needed???
8147     
8148        
8149     /**
8150      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8151      */
8152     invalidClass : "has-warning",
8153     
8154     /**
8155      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8156      */
8157     validClass : "has-success",
8158     
8159     /**
8160      * @cfg {Boolean} hasFeedback (true|false) default true
8161      */
8162     hasFeedback : true,
8163     
8164     /**
8165      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8166      */
8167     invalidFeedbackClass : "glyphicon-warning-sign",
8168     
8169     /**
8170      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8171      */
8172     validFeedbackClass : "glyphicon-ok",
8173     
8174     /**
8175      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8176      */
8177     selectOnFocus : false,
8178     
8179      /**
8180      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8181      */
8182     maskRe : null,
8183        /**
8184      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8185      */
8186     vtype : null,
8187     
8188       /**
8189      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8190      */
8191     disableKeyFilter : false,
8192     
8193        /**
8194      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8195      */
8196     disabled : false,
8197      /**
8198      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8199      */
8200     allowBlank : true,
8201     /**
8202      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8203      */
8204     blankText : "This field is required",
8205     
8206      /**
8207      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8208      */
8209     minLength : 0,
8210     /**
8211      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8212      */
8213     maxLength : Number.MAX_VALUE,
8214     /**
8215      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8216      */
8217     minLengthText : "The minimum length for this field is {0}",
8218     /**
8219      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8220      */
8221     maxLengthText : "The maximum length for this field is {0}",
8222   
8223     
8224     /**
8225      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8226      * If available, this function will be called only after the basic validators all return true, and will be passed the
8227      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8228      */
8229     validator : null,
8230     /**
8231      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8232      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8233      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8234      */
8235     regex : null,
8236     /**
8237      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8238      */
8239     regexText : "",
8240     
8241     autocomplete: false,
8242     
8243     
8244     fieldLabel : '',
8245     inputType : 'text',
8246     
8247     name : false,
8248     placeholder: false,
8249     before : false,
8250     after : false,
8251     size : false,
8252     hasFocus : false,
8253     preventMark: false,
8254     isFormField : true,
8255     value : '',
8256     labelWidth : 2,
8257     labelAlign : false,
8258     readOnly : false,
8259     align : false,
8260     formatedValue : false,
8261     forceFeedback : false,
8262     
8263     indicatorpos : 'left',
8264     
8265     parentLabelAlign : function()
8266     {
8267         var parent = this;
8268         while (parent.parent()) {
8269             parent = parent.parent();
8270             if (typeof(parent.labelAlign) !='undefined') {
8271                 return parent.labelAlign;
8272             }
8273         }
8274         return 'left';
8275         
8276     },
8277     
8278     getAutoCreate : function()
8279     {
8280         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8281         
8282         var id = Roo.id();
8283         
8284         var cfg = {};
8285         
8286         if(this.inputType != 'hidden'){
8287             cfg.cls = 'form-group' //input-group
8288         }
8289         
8290         var input =  {
8291             tag: 'input',
8292             id : id,
8293             type : this.inputType,
8294             value : this.value,
8295             cls : 'form-control',
8296             placeholder : this.placeholder || '',
8297             autocomplete : this.autocomplete || 'new-password'
8298         };
8299         
8300         if(this.align){
8301             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8302         }
8303         
8304         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8305             input.maxLength = this.maxLength;
8306         }
8307         
8308         if (this.disabled) {
8309             input.disabled=true;
8310         }
8311         
8312         if (this.readOnly) {
8313             input.readonly=true;
8314         }
8315         
8316         if (this.name) {
8317             input.name = this.name;
8318         }
8319         
8320         if (this.size) {
8321             input.cls += ' input-' + this.size;
8322         }
8323         
8324         var settings=this;
8325         ['xs','sm','md','lg'].map(function(size){
8326             if (settings[size]) {
8327                 cfg.cls += ' col-' + size + '-' + settings[size];
8328             }
8329         });
8330         
8331         var inputblock = input;
8332         
8333         var feedback = {
8334             tag: 'span',
8335             cls: 'glyphicon form-control-feedback'
8336         };
8337             
8338         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8339             
8340             inputblock = {
8341                 cls : 'has-feedback',
8342                 cn :  [
8343                     input,
8344                     feedback
8345                 ] 
8346             };  
8347         }
8348         
8349         if (this.before || this.after) {
8350             
8351             inputblock = {
8352                 cls : 'input-group',
8353                 cn :  [] 
8354             };
8355             
8356             if (this.before && typeof(this.before) == 'string') {
8357                 
8358                 inputblock.cn.push({
8359                     tag :'span',
8360                     cls : 'roo-input-before input-group-addon',
8361                     html : this.before
8362                 });
8363             }
8364             if (this.before && typeof(this.before) == 'object') {
8365                 this.before = Roo.factory(this.before);
8366                 
8367                 inputblock.cn.push({
8368                     tag :'span',
8369                     cls : 'roo-input-before input-group-' +
8370                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8371                 });
8372             }
8373             
8374             inputblock.cn.push(input);
8375             
8376             if (this.after && typeof(this.after) == 'string') {
8377                 inputblock.cn.push({
8378                     tag :'span',
8379                     cls : 'roo-input-after input-group-addon',
8380                     html : this.after
8381                 });
8382             }
8383             if (this.after && typeof(this.after) == 'object') {
8384                 this.after = Roo.factory(this.after);
8385                 
8386                 inputblock.cn.push({
8387                     tag :'span',
8388                     cls : 'roo-input-after input-group-' +
8389                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8390                 });
8391             }
8392             
8393             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8394                 inputblock.cls += ' has-feedback';
8395                 inputblock.cn.push(feedback);
8396             }
8397         };
8398         
8399         if (align ==='left' && this.fieldLabel.length) {
8400             
8401             cfg.cn = [
8402                 {
8403                     tag : 'i',
8404                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8405                     tooltip : 'This field is required'
8406                 },
8407                 {
8408                     tag: 'label',
8409                     'for' :  id,
8410                     cls : 'control-label col-sm-' + this.labelWidth,
8411                     html : this.fieldLabel
8412
8413                 },
8414                 {
8415                     cls : "col-sm-" + (12 - this.labelWidth), 
8416                     cn: [
8417                         inputblock
8418                     ]
8419                 }
8420
8421             ];
8422             
8423             if(this.indicatorpos == 'right'){
8424                 cfg.cn = [
8425                     {
8426                         tag: 'label',
8427                         'for' :  id,
8428                         cls : 'control-label col-sm-' + this.labelWidth,
8429                         html : this.fieldLabel
8430
8431                     },
8432                     {
8433                         tag : 'i',
8434                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8435                         tooltip : 'This field is required'
8436                     },
8437                     {
8438                         cls : "col-sm-" + (12 - this.labelWidth), 
8439                         cn: [
8440                             inputblock
8441                         ]
8442                     }
8443
8444                 ];
8445             }
8446             
8447         } else if ( this.fieldLabel.length) {
8448                 
8449             cfg.cn = [
8450                 {
8451                     tag : 'i',
8452                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8453                     tooltip : 'This field is required'
8454                 },
8455                 {
8456                     tag: 'label',
8457                    //cls : 'input-group-addon',
8458                     html : this.fieldLabel
8459
8460                 },
8461
8462                inputblock
8463
8464            ];
8465            
8466            if(this.indicatorpos == 'right'){
8467                 
8468                 cfg.cn = [
8469                     {
8470                         tag: 'label',
8471                        //cls : 'input-group-addon',
8472                         html : this.fieldLabel
8473
8474                     },
8475                     {
8476                         tag : 'i',
8477                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8478                         tooltip : 'This field is required'
8479                     },
8480
8481                    inputblock
8482
8483                ];
8484
8485             }
8486
8487         } else {
8488             
8489             cfg.cn = [
8490
8491                     inputblock
8492
8493             ];
8494                 
8495                 
8496         };
8497         
8498         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8499            cfg.cls += ' navbar-form';
8500         }
8501         
8502         if (this.parentType === 'NavGroup') {
8503            cfg.cls += ' navbar-form';
8504            cfg.tag = 'li';
8505         }
8506         
8507         return cfg;
8508         
8509     },
8510     /**
8511      * return the real input element.
8512      */
8513     inputEl: function ()
8514     {
8515         return this.el.select('input.form-control',true).first();
8516     },
8517     
8518     tooltipEl : function()
8519     {
8520         return this.inputEl();
8521     },
8522     
8523     indicatorEl : function()
8524     {
8525         var indicator = this.el.select('i.roo-required-indicator',true).first();
8526         
8527         if(!indicator){
8528             return false;
8529         }
8530         
8531         return indicator;
8532         
8533     },
8534     
8535     setDisabled : function(v)
8536     {
8537         var i  = this.inputEl().dom;
8538         if (!v) {
8539             i.removeAttribute('disabled');
8540             return;
8541             
8542         }
8543         i.setAttribute('disabled','true');
8544     },
8545     initEvents : function()
8546     {
8547           
8548         this.inputEl().on("keydown" , this.fireKey,  this);
8549         this.inputEl().on("focus", this.onFocus,  this);
8550         this.inputEl().on("blur", this.onBlur,  this);
8551         
8552         this.inputEl().relayEvent('keyup', this);
8553         
8554         this.indicator = this.indicatorEl();
8555         
8556         if(this.indicator){
8557             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8558             this.indicator.hide();
8559         }
8560  
8561         // reference to original value for reset
8562         this.originalValue = this.getValue();
8563         //Roo.form.TextField.superclass.initEvents.call(this);
8564         if(this.validationEvent == 'keyup'){
8565             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8566             this.inputEl().on('keyup', this.filterValidation, this);
8567         }
8568         else if(this.validationEvent !== false){
8569             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8570         }
8571         
8572         if(this.selectOnFocus){
8573             this.on("focus", this.preFocus, this);
8574             
8575         }
8576         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8577             this.inputEl().on("keypress", this.filterKeys, this);
8578         } else {
8579             this.inputEl().relayEvent('keypress', this);
8580         }
8581        /* if(this.grow){
8582             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8583             this.el.on("click", this.autoSize,  this);
8584         }
8585         */
8586         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8587             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8588         }
8589         
8590         if (typeof(this.before) == 'object') {
8591             this.before.render(this.el.select('.roo-input-before',true).first());
8592         }
8593         if (typeof(this.after) == 'object') {
8594             this.after.render(this.el.select('.roo-input-after',true).first());
8595         }
8596         
8597         
8598     },
8599     filterValidation : function(e){
8600         if(!e.isNavKeyPress()){
8601             this.validationTask.delay(this.validationDelay);
8602         }
8603     },
8604      /**
8605      * Validates the field value
8606      * @return {Boolean} True if the value is valid, else false
8607      */
8608     validate : function(){
8609         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8610         if(this.disabled || this.validateValue(this.getRawValue())){
8611             this.markValid();
8612             return true;
8613         }
8614         
8615         this.markInvalid();
8616         return false;
8617     },
8618     
8619     
8620     /**
8621      * Validates a value according to the field's validation rules and marks the field as invalid
8622      * if the validation fails
8623      * @param {Mixed} value The value to validate
8624      * @return {Boolean} True if the value is valid, else false
8625      */
8626     validateValue : function(value){
8627         if(value.length < 1)  { // if it's blank
8628             if(this.allowBlank){
8629                 return true;
8630             }
8631             return false;
8632         }
8633         
8634         if(value.length < this.minLength){
8635             return false;
8636         }
8637         if(value.length > this.maxLength){
8638             return false;
8639         }
8640         if(this.vtype){
8641             var vt = Roo.form.VTypes;
8642             if(!vt[this.vtype](value, this)){
8643                 return false;
8644             }
8645         }
8646         if(typeof this.validator == "function"){
8647             var msg = this.validator(value);
8648             if(msg !== true){
8649                 return false;
8650             }
8651         }
8652         
8653         if(this.regex && !this.regex.test(value)){
8654             return false;
8655         }
8656         
8657         return true;
8658     },
8659
8660     
8661     
8662      // private
8663     fireKey : function(e){
8664         //Roo.log('field ' + e.getKey());
8665         if(e.isNavKeyPress()){
8666             this.fireEvent("specialkey", this, e);
8667         }
8668     },
8669     focus : function (selectText){
8670         if(this.rendered){
8671             this.inputEl().focus();
8672             if(selectText === true){
8673                 this.inputEl().dom.select();
8674             }
8675         }
8676         return this;
8677     } ,
8678     
8679     onFocus : function(){
8680         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8681            // this.el.addClass(this.focusClass);
8682         }
8683         if(!this.hasFocus){
8684             this.hasFocus = true;
8685             this.startValue = this.getValue();
8686             this.fireEvent("focus", this);
8687         }
8688     },
8689     
8690     beforeBlur : Roo.emptyFn,
8691
8692     
8693     // private
8694     onBlur : function(){
8695         this.beforeBlur();
8696         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8697             //this.el.removeClass(this.focusClass);
8698         }
8699         this.hasFocus = false;
8700         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8701             this.validate();
8702         }
8703         var v = this.getValue();
8704         if(String(v) !== String(this.startValue)){
8705             this.fireEvent('change', this, v, this.startValue);
8706         }
8707         this.fireEvent("blur", this);
8708     },
8709     
8710     /**
8711      * Resets the current field value to the originally loaded value and clears any validation messages
8712      */
8713     reset : function(){
8714         this.setValue(this.originalValue);
8715         this.validate();
8716     },
8717      /**
8718      * Returns the name of the field
8719      * @return {Mixed} name The name field
8720      */
8721     getName: function(){
8722         return this.name;
8723     },
8724      /**
8725      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8726      * @return {Mixed} value The field value
8727      */
8728     getValue : function(){
8729         
8730         var v = this.inputEl().getValue();
8731         
8732         return v;
8733     },
8734     /**
8735      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8736      * @return {Mixed} value The field value
8737      */
8738     getRawValue : function(){
8739         var v = this.inputEl().getValue();
8740         
8741         return v;
8742     },
8743     
8744     /**
8745      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8746      * @param {Mixed} value The value to set
8747      */
8748     setRawValue : function(v){
8749         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8750     },
8751     
8752     selectText : function(start, end){
8753         var v = this.getRawValue();
8754         if(v.length > 0){
8755             start = start === undefined ? 0 : start;
8756             end = end === undefined ? v.length : end;
8757             var d = this.inputEl().dom;
8758             if(d.setSelectionRange){
8759                 d.setSelectionRange(start, end);
8760             }else if(d.createTextRange){
8761                 var range = d.createTextRange();
8762                 range.moveStart("character", start);
8763                 range.moveEnd("character", v.length-end);
8764                 range.select();
8765             }
8766         }
8767     },
8768     
8769     /**
8770      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8771      * @param {Mixed} value The value to set
8772      */
8773     setValue : function(v){
8774         this.value = v;
8775         if(this.rendered){
8776             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8777             this.validate();
8778         }
8779     },
8780     
8781     /*
8782     processValue : function(value){
8783         if(this.stripCharsRe){
8784             var newValue = value.replace(this.stripCharsRe, '');
8785             if(newValue !== value){
8786                 this.setRawValue(newValue);
8787                 return newValue;
8788             }
8789         }
8790         return value;
8791     },
8792   */
8793     preFocus : function(){
8794         
8795         if(this.selectOnFocus){
8796             this.inputEl().dom.select();
8797         }
8798     },
8799     filterKeys : function(e){
8800         var k = e.getKey();
8801         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8802             return;
8803         }
8804         var c = e.getCharCode(), cc = String.fromCharCode(c);
8805         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8806             return;
8807         }
8808         if(!this.maskRe.test(cc)){
8809             e.stopEvent();
8810         }
8811     },
8812      /**
8813      * Clear any invalid styles/messages for this field
8814      */
8815     clearInvalid : function(){
8816         
8817         if(!this.el || this.preventMark){ // not rendered
8818             return;
8819         }
8820         
8821         if(this.indicator){
8822             this.indicator.hide();
8823         }
8824         
8825         this.el.removeClass(this.invalidClass);
8826         
8827         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8828             
8829             var feedback = this.el.select('.form-control-feedback', true).first();
8830             
8831             if(feedback){
8832                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8833             }
8834             
8835         }
8836         
8837         this.fireEvent('valid', this);
8838     },
8839     
8840      /**
8841      * Mark this field as valid
8842      */
8843     markValid : function()
8844     {
8845         if(!this.el  || this.preventMark){ // not rendered
8846             return;
8847         }
8848         
8849         this.el.removeClass([this.invalidClass, this.validClass]);
8850         
8851         var feedback = this.el.select('.form-control-feedback', true).first();
8852             
8853         if(feedback){
8854             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8855         }
8856
8857         if(this.disabled){
8858             return;
8859         }
8860         
8861         if(this.allowBlank && !this.getRawValue().length){
8862             return;
8863         }
8864         
8865         if(this.indicator){
8866             this.indicator.hide();
8867         }
8868         
8869         this.el.addClass(this.validClass);
8870         
8871         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8872             
8873             var feedback = this.el.select('.form-control-feedback', true).first();
8874             
8875             if(feedback){
8876                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8877                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8878             }
8879             
8880         }
8881         
8882         this.fireEvent('valid', this);
8883     },
8884     
8885      /**
8886      * Mark this field as invalid
8887      * @param {String} msg The validation message
8888      */
8889     markInvalid : function(msg)
8890     {
8891         if(!this.el  || this.preventMark){ // not rendered
8892             return;
8893         }
8894         
8895         this.el.removeClass([this.invalidClass, this.validClass]);
8896         
8897         var feedback = this.el.select('.form-control-feedback', true).first();
8898             
8899         if(feedback){
8900             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8901         }
8902
8903         if(this.disabled){
8904             return;
8905         }
8906         
8907         if(this.allowBlank && !this.getRawValue().length){
8908             return;
8909         }
8910         
8911         if(this.indicator){
8912             this.indicator.show();
8913         }
8914         
8915         this.el.addClass(this.invalidClass);
8916         
8917         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8918             
8919             var feedback = this.el.select('.form-control-feedback', true).first();
8920             
8921             if(feedback){
8922                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8923                 
8924                 if(this.getValue().length || this.forceFeedback){
8925                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8926                 }
8927                 
8928             }
8929             
8930         }
8931         
8932         this.fireEvent('invalid', this, msg);
8933     },
8934     // private
8935     SafariOnKeyDown : function(event)
8936     {
8937         // this is a workaround for a password hang bug on chrome/ webkit.
8938         if (this.inputEl().dom.type != 'password') {
8939             return;
8940         }
8941         
8942         var isSelectAll = false;
8943         
8944         if(this.inputEl().dom.selectionEnd > 0){
8945             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8946         }
8947         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8948             event.preventDefault();
8949             this.setValue('');
8950             return;
8951         }
8952         
8953         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8954             
8955             event.preventDefault();
8956             // this is very hacky as keydown always get's upper case.
8957             //
8958             var cc = String.fromCharCode(event.getCharCode());
8959             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8960             
8961         }
8962     },
8963     adjustWidth : function(tag, w){
8964         tag = tag.toLowerCase();
8965         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8966             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8967                 if(tag == 'input'){
8968                     return w + 2;
8969                 }
8970                 if(tag == 'textarea'){
8971                     return w-2;
8972                 }
8973             }else if(Roo.isOpera){
8974                 if(tag == 'input'){
8975                     return w + 2;
8976                 }
8977                 if(tag == 'textarea'){
8978                     return w-2;
8979                 }
8980             }
8981         }
8982         return w;
8983     }
8984     
8985 });
8986
8987  
8988 /*
8989  * - LGPL
8990  *
8991  * Input
8992  * 
8993  */
8994
8995 /**
8996  * @class Roo.bootstrap.TextArea
8997  * @extends Roo.bootstrap.Input
8998  * Bootstrap TextArea class
8999  * @cfg {Number} cols Specifies the visible width of a text area
9000  * @cfg {Number} rows Specifies the visible number of lines in a text area
9001  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9002  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9003  * @cfg {string} html text
9004  * 
9005  * @constructor
9006  * Create a new TextArea
9007  * @param {Object} config The config object
9008  */
9009
9010 Roo.bootstrap.TextArea = function(config){
9011     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9012    
9013 };
9014
9015 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9016      
9017     cols : false,
9018     rows : 5,
9019     readOnly : false,
9020     warp : 'soft',
9021     resize : false,
9022     value: false,
9023     html: false,
9024     
9025     getAutoCreate : function(){
9026         
9027         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9028         
9029         var id = Roo.id();
9030         
9031         var cfg = {};
9032         
9033         var input =  {
9034             tag: 'textarea',
9035             id : id,
9036             warp : this.warp,
9037             rows : this.rows,
9038             value : this.value || '',
9039             html: this.html || '',
9040             cls : 'form-control',
9041             placeholder : this.placeholder || '' 
9042             
9043         };
9044         
9045         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9046             input.maxLength = this.maxLength;
9047         }
9048         
9049         if(this.resize){
9050             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9051         }
9052         
9053         if(this.cols){
9054             input.cols = this.cols;
9055         }
9056         
9057         if (this.readOnly) {
9058             input.readonly = true;
9059         }
9060         
9061         if (this.name) {
9062             input.name = this.name;
9063         }
9064         
9065         if (this.size) {
9066             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9067         }
9068         
9069         var settings=this;
9070         ['xs','sm','md','lg'].map(function(size){
9071             if (settings[size]) {
9072                 cfg.cls += ' col-' + size + '-' + settings[size];
9073             }
9074         });
9075         
9076         var inputblock = input;
9077         
9078         if(this.hasFeedback && !this.allowBlank){
9079             
9080             var feedback = {
9081                 tag: 'span',
9082                 cls: 'glyphicon form-control-feedback'
9083             };
9084
9085             inputblock = {
9086                 cls : 'has-feedback',
9087                 cn :  [
9088                     input,
9089                     feedback
9090                 ] 
9091             };  
9092         }
9093         
9094         
9095         if (this.before || this.after) {
9096             
9097             inputblock = {
9098                 cls : 'input-group',
9099                 cn :  [] 
9100             };
9101             if (this.before) {
9102                 inputblock.cn.push({
9103                     tag :'span',
9104                     cls : 'input-group-addon',
9105                     html : this.before
9106                 });
9107             }
9108             
9109             inputblock.cn.push(input);
9110             
9111             if(this.hasFeedback && !this.allowBlank){
9112                 inputblock.cls += ' has-feedback';
9113                 inputblock.cn.push(feedback);
9114             }
9115             
9116             if (this.after) {
9117                 inputblock.cn.push({
9118                     tag :'span',
9119                     cls : 'input-group-addon',
9120                     html : this.after
9121                 });
9122             }
9123             
9124         }
9125         
9126         if (align ==='left' && this.fieldLabel.length) {
9127 //                Roo.log("left and has label");
9128                 cfg.cn = [
9129                     
9130                     {
9131                         tag: 'label',
9132                         'for' :  id,
9133                         cls : 'control-label col-sm-' + this.labelWidth,
9134                         html : this.fieldLabel
9135                         
9136                     },
9137                     {
9138                         cls : "col-sm-" + (12 - this.labelWidth), 
9139                         cn: [
9140                             inputblock
9141                         ]
9142                     }
9143                     
9144                 ];
9145         } else if ( this.fieldLabel.length) {
9146 //                Roo.log(" label");
9147                  cfg.cn = [
9148                    
9149                     {
9150                         tag: 'label',
9151                         //cls : 'input-group-addon',
9152                         html : this.fieldLabel
9153                         
9154                     },
9155                     
9156                     inputblock
9157                     
9158                 ];
9159
9160         } else {
9161             
9162 //                   Roo.log(" no label && no align");
9163                 cfg.cn = [
9164                     
9165                         inputblock
9166                     
9167                 ];
9168                 
9169                 
9170         }
9171         
9172         if (this.disabled) {
9173             input.disabled=true;
9174         }
9175         
9176         return cfg;
9177         
9178     },
9179     /**
9180      * return the real textarea element.
9181      */
9182     inputEl: function ()
9183     {
9184         return this.el.select('textarea.form-control',true).first();
9185     },
9186     
9187     /**
9188      * Clear any invalid styles/messages for this field
9189      */
9190     clearInvalid : function()
9191     {
9192         
9193         if(!this.el || this.preventMark){ // not rendered
9194             return;
9195         }
9196         
9197         var label = this.el.select('label', true).first();
9198         var icon = this.el.select('i.fa-star', true).first();
9199         
9200         if(label && icon){
9201             icon.remove();
9202         }
9203         
9204         this.el.removeClass(this.invalidClass);
9205         
9206         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9207             
9208             var feedback = this.el.select('.form-control-feedback', true).first();
9209             
9210             if(feedback){
9211                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9212             }
9213             
9214         }
9215         
9216         this.fireEvent('valid', this);
9217     },
9218     
9219      /**
9220      * Mark this field as valid
9221      */
9222     markValid : function()
9223     {
9224         if(!this.el  || this.preventMark){ // not rendered
9225             return;
9226         }
9227         
9228         this.el.removeClass([this.invalidClass, this.validClass]);
9229         
9230         var feedback = this.el.select('.form-control-feedback', true).first();
9231             
9232         if(feedback){
9233             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9234         }
9235
9236         if(this.disabled || this.allowBlank){
9237             return;
9238         }
9239         
9240         var label = this.el.select('label', true).first();
9241         var icon = this.el.select('i.fa-star', true).first();
9242         
9243         if(label && icon){
9244             icon.remove();
9245         }
9246         
9247         this.el.addClass(this.validClass);
9248         
9249         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9250             
9251             var feedback = this.el.select('.form-control-feedback', true).first();
9252             
9253             if(feedback){
9254                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9255                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9256             }
9257             
9258         }
9259         
9260         this.fireEvent('valid', this);
9261     },
9262     
9263      /**
9264      * Mark this field as invalid
9265      * @param {String} msg The validation message
9266      */
9267     markInvalid : function(msg)
9268     {
9269         if(!this.el  || this.preventMark){ // not rendered
9270             return;
9271         }
9272         
9273         this.el.removeClass([this.invalidClass, this.validClass]);
9274         
9275         var feedback = this.el.select('.form-control-feedback', true).first();
9276             
9277         if(feedback){
9278             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9279         }
9280
9281         if(this.disabled || this.allowBlank){
9282             return;
9283         }
9284         
9285         var label = this.el.select('label', true).first();
9286         var icon = this.el.select('i.fa-star', true).first();
9287         
9288         if(!this.getValue().length && label && !icon){
9289             this.el.createChild({
9290                 tag : 'i',
9291                 cls : 'text-danger fa fa-lg fa-star',
9292                 tooltip : 'This field is required',
9293                 style : 'margin-right:5px;'
9294             }, label, true);
9295         }
9296
9297         this.el.addClass(this.invalidClass);
9298         
9299         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9300             
9301             var feedback = this.el.select('.form-control-feedback', true).first();
9302             
9303             if(feedback){
9304                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9305                 
9306                 if(this.getValue().length || this.forceFeedback){
9307                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9308                 }
9309                 
9310             }
9311             
9312         }
9313         
9314         this.fireEvent('invalid', this, msg);
9315     }
9316 });
9317
9318  
9319 /*
9320  * - LGPL
9321  *
9322  * trigger field - base class for combo..
9323  * 
9324  */
9325  
9326 /**
9327  * @class Roo.bootstrap.TriggerField
9328  * @extends Roo.bootstrap.Input
9329  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9330  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9331  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9332  * for which you can provide a custom implementation.  For example:
9333  * <pre><code>
9334 var trigger = new Roo.bootstrap.TriggerField();
9335 trigger.onTriggerClick = myTriggerFn;
9336 trigger.applyTo('my-field');
9337 </code></pre>
9338  *
9339  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9340  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9341  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9342  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9343  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9344
9345  * @constructor
9346  * Create a new TriggerField.
9347  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9348  * to the base TextField)
9349  */
9350 Roo.bootstrap.TriggerField = function(config){
9351     this.mimicing = false;
9352     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9353 };
9354
9355 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9356     /**
9357      * @cfg {String} triggerClass A CSS class to apply to the trigger
9358      */
9359      /**
9360      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9361      */
9362     hideTrigger:false,
9363
9364     /**
9365      * @cfg {Boolean} removable (true|false) special filter default false
9366      */
9367     removable : false,
9368     
9369     /** @cfg {Boolean} grow @hide */
9370     /** @cfg {Number} growMin @hide */
9371     /** @cfg {Number} growMax @hide */
9372
9373     /**
9374      * @hide 
9375      * @method
9376      */
9377     autoSize: Roo.emptyFn,
9378     // private
9379     monitorTab : true,
9380     // private
9381     deferHeight : true,
9382
9383     
9384     actionMode : 'wrap',
9385     
9386     caret : false,
9387     
9388     
9389     getAutoCreate : function(){
9390        
9391         var align = this.labelAlign || this.parentLabelAlign();
9392         
9393         var id = Roo.id();
9394         
9395         var cfg = {
9396             cls: 'form-group' //input-group
9397         };
9398         
9399         
9400         var input =  {
9401             tag: 'input',
9402             id : id,
9403             type : this.inputType,
9404             cls : 'form-control',
9405             autocomplete: 'new-password',
9406             placeholder : this.placeholder || '' 
9407             
9408         };
9409         if (this.name) {
9410             input.name = this.name;
9411         }
9412         if (this.size) {
9413             input.cls += ' input-' + this.size;
9414         }
9415         
9416         if (this.disabled) {
9417             input.disabled=true;
9418         }
9419         
9420         var inputblock = input;
9421         
9422         if(this.hasFeedback && !this.allowBlank){
9423             
9424             var feedback = {
9425                 tag: 'span',
9426                 cls: 'glyphicon form-control-feedback'
9427             };
9428             
9429             if(this.removable && !this.editable && !this.tickable){
9430                 inputblock = {
9431                     cls : 'has-feedback',
9432                     cn :  [
9433                         inputblock,
9434                         {
9435                             tag: 'button',
9436                             html : 'x',
9437                             cls : 'roo-combo-removable-btn close'
9438                         },
9439                         feedback
9440                     ] 
9441                 };
9442             } else {
9443                 inputblock = {
9444                     cls : 'has-feedback',
9445                     cn :  [
9446                         inputblock,
9447                         feedback
9448                     ] 
9449                 };
9450             }
9451
9452         } else {
9453             if(this.removable && !this.editable && !this.tickable){
9454                 inputblock = {
9455                     cls : 'roo-removable',
9456                     cn :  [
9457                         inputblock,
9458                         {
9459                             tag: 'button',
9460                             html : 'x',
9461                             cls : 'roo-combo-removable-btn close'
9462                         }
9463                     ] 
9464                 };
9465             }
9466         }
9467         
9468         if (this.before || this.after) {
9469             
9470             inputblock = {
9471                 cls : 'input-group',
9472                 cn :  [] 
9473             };
9474             if (this.before) {
9475                 inputblock.cn.push({
9476                     tag :'span',
9477                     cls : 'input-group-addon',
9478                     html : this.before
9479                 });
9480             }
9481             
9482             inputblock.cn.push(input);
9483             
9484             if(this.hasFeedback && !this.allowBlank){
9485                 inputblock.cls += ' has-feedback';
9486                 inputblock.cn.push(feedback);
9487             }
9488             
9489             if (this.after) {
9490                 inputblock.cn.push({
9491                     tag :'span',
9492                     cls : 'input-group-addon',
9493                     html : this.after
9494                 });
9495             }
9496             
9497         };
9498         
9499         var box = {
9500             tag: 'div',
9501             cn: [
9502                 {
9503                     tag: 'input',
9504                     type : 'hidden',
9505                     cls: 'form-hidden-field'
9506                 },
9507                 inputblock
9508             ]
9509             
9510         };
9511         
9512         if(this.multiple){
9513             box = {
9514                 tag: 'div',
9515                 cn: [
9516                     {
9517                         tag: 'input',
9518                         type : 'hidden',
9519                         cls: 'form-hidden-field'
9520                     },
9521                     {
9522                         tag: 'ul',
9523                         cls: 'roo-select2-choices',
9524                         cn:[
9525                             {
9526                                 tag: 'li',
9527                                 cls: 'roo-select2-search-field',
9528                                 cn: [
9529
9530                                     inputblock
9531                                 ]
9532                             }
9533                         ]
9534                     }
9535                 ]
9536             }
9537         };
9538         
9539         var combobox = {
9540             cls: 'roo-select2-container input-group',
9541             cn: [
9542                 box
9543 //                {
9544 //                    tag: 'ul',
9545 //                    cls: 'typeahead typeahead-long dropdown-menu',
9546 //                    style: 'display:none'
9547 //                }
9548             ]
9549         };
9550         
9551         if(!this.multiple && this.showToggleBtn){
9552             
9553             var caret = {
9554                         tag: 'span',
9555                         cls: 'caret'
9556              };
9557             if (this.caret != false) {
9558                 caret = {
9559                      tag: 'i',
9560                      cls: 'fa fa-' + this.caret
9561                 };
9562                 
9563             }
9564             
9565             combobox.cn.push({
9566                 tag :'span',
9567                 cls : 'input-group-addon btn dropdown-toggle',
9568                 cn : [
9569                     caret,
9570                     {
9571                         tag: 'span',
9572                         cls: 'combobox-clear',
9573                         cn  : [
9574                             {
9575                                 tag : 'i',
9576                                 cls: 'icon-remove'
9577                             }
9578                         ]
9579                     }
9580                 ]
9581
9582             })
9583         }
9584         
9585         if(this.multiple){
9586             combobox.cls += ' roo-select2-container-multi';
9587         }
9588         
9589         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9590             
9591 //                Roo.log("left and has label");
9592             cfg.cn = [
9593                 {
9594                     tag : 'i',
9595                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9596                     tooltip : 'This field is required'
9597                 },
9598                 {
9599                     tag: 'label',
9600                     'for' :  id,
9601                     cls : 'control-label col-sm-' + this.labelWidth,
9602                     html : this.fieldLabel
9603
9604                 },
9605                 {
9606                     cls : "col-sm-" + (12 - this.labelWidth), 
9607                     cn: [
9608                         combobox
9609                     ]
9610                 }
9611
9612             ];
9613             
9614             if(this.indicatorpos == 'right'){
9615                 cfg.cn = [
9616                     {
9617                         tag: 'label',
9618                         'for' :  id,
9619                         cls : 'control-label col-sm-' + this.labelWidth,
9620                         html : this.fieldLabel
9621
9622                     },
9623                     {
9624                         tag : 'i',
9625                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9626                         tooltip : 'This field is required'
9627                     },
9628                     {
9629                         cls : "col-sm-" + (12 - this.labelWidth), 
9630                         cn: [
9631                             combobox
9632                         ]
9633                     }
9634
9635                 ];
9636             }
9637             
9638         } else if ( this.fieldLabel.length) {
9639 //                Roo.log(" label");
9640             cfg.cn = [
9641                 {
9642                    tag : 'i',
9643                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9644                    tooltip : 'This field is required'
9645                },
9646                {
9647                    tag: 'label',
9648                    //cls : 'input-group-addon',
9649                    html : this.fieldLabel
9650
9651                },
9652
9653                combobox
9654
9655             ];
9656             
9657             if(this.indicatorpos == 'right'){
9658                 
9659                 cfg.cn = [
9660                     {
9661                        tag: 'label',
9662                        //cls : 'input-group-addon',
9663                        html : this.fieldLabel
9664
9665                     },
9666                     {
9667                        tag : 'i',
9668                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9669                        tooltip : 'This field is required'
9670                     },
9671                     
9672                     combobox
9673
9674                 ];
9675
9676             }
9677
9678         } else {
9679             
9680 //                Roo.log(" no label && no align");
9681                 cfg = combobox
9682                      
9683                 
9684         }
9685          
9686         var settings=this;
9687         ['xs','sm','md','lg'].map(function(size){
9688             if (settings[size]) {
9689                 cfg.cls += ' col-' + size + '-' + settings[size];
9690             }
9691         });
9692         
9693         return cfg;
9694         
9695     },
9696     
9697     
9698     
9699     // private
9700     onResize : function(w, h){
9701 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9702 //        if(typeof w == 'number'){
9703 //            var x = w - this.trigger.getWidth();
9704 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9705 //            this.trigger.setStyle('left', x+'px');
9706 //        }
9707     },
9708
9709     // private
9710     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9711
9712     // private
9713     getResizeEl : function(){
9714         return this.inputEl();
9715     },
9716
9717     // private
9718     getPositionEl : function(){
9719         return this.inputEl();
9720     },
9721
9722     // private
9723     alignErrorIcon : function(){
9724         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9725     },
9726
9727     // private
9728     initEvents : function(){
9729         
9730         this.createList();
9731         
9732         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9733         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9734         if(!this.multiple && this.showToggleBtn){
9735             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9736             if(this.hideTrigger){
9737                 this.trigger.setDisplayed(false);
9738             }
9739             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9740         }
9741         
9742         if(this.multiple){
9743             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9744         }
9745         
9746         if(this.removable && !this.editable && !this.tickable){
9747             var close = this.closeTriggerEl();
9748             
9749             if(close){
9750                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9751                 close.on('click', this.removeBtnClick, this, close);
9752             }
9753         }
9754         
9755         //this.trigger.addClassOnOver('x-form-trigger-over');
9756         //this.trigger.addClassOnClick('x-form-trigger-click');
9757         
9758         //if(!this.width){
9759         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9760         //}
9761     },
9762     
9763     closeTriggerEl : function()
9764     {
9765         var close = this.el.select('.roo-combo-removable-btn', true).first();
9766         return close ? close : false;
9767     },
9768     
9769     removeBtnClick : function(e, h, el)
9770     {
9771         e.preventDefault();
9772         
9773         if(this.fireEvent("remove", this) !== false){
9774             this.reset();
9775             this.fireEvent("afterremove", this)
9776         }
9777     },
9778     
9779     createList : function()
9780     {
9781         this.list = Roo.get(document.body).createChild({
9782             tag: 'ul',
9783             cls: 'typeahead typeahead-long dropdown-menu',
9784             style: 'display:none'
9785         });
9786         
9787         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9788         
9789     },
9790
9791     // private
9792     initTrigger : function(){
9793        
9794     },
9795
9796     // private
9797     onDestroy : function(){
9798         if(this.trigger){
9799             this.trigger.removeAllListeners();
9800           //  this.trigger.remove();
9801         }
9802         //if(this.wrap){
9803         //    this.wrap.remove();
9804         //}
9805         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9806     },
9807
9808     // private
9809     onFocus : function(){
9810         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9811         /*
9812         if(!this.mimicing){
9813             this.wrap.addClass('x-trigger-wrap-focus');
9814             this.mimicing = true;
9815             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9816             if(this.monitorTab){
9817                 this.el.on("keydown", this.checkTab, this);
9818             }
9819         }
9820         */
9821     },
9822
9823     // private
9824     checkTab : function(e){
9825         if(e.getKey() == e.TAB){
9826             this.triggerBlur();
9827         }
9828     },
9829
9830     // private
9831     onBlur : function(){
9832         // do nothing
9833     },
9834
9835     // private
9836     mimicBlur : function(e, t){
9837         /*
9838         if(!this.wrap.contains(t) && this.validateBlur()){
9839             this.triggerBlur();
9840         }
9841         */
9842     },
9843
9844     // private
9845     triggerBlur : function(){
9846         this.mimicing = false;
9847         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9848         if(this.monitorTab){
9849             this.el.un("keydown", this.checkTab, this);
9850         }
9851         //this.wrap.removeClass('x-trigger-wrap-focus');
9852         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9853     },
9854
9855     // private
9856     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9857     validateBlur : function(e, t){
9858         return true;
9859     },
9860
9861     // private
9862     onDisable : function(){
9863         this.inputEl().dom.disabled = true;
9864         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9865         //if(this.wrap){
9866         //    this.wrap.addClass('x-item-disabled');
9867         //}
9868     },
9869
9870     // private
9871     onEnable : function(){
9872         this.inputEl().dom.disabled = false;
9873         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9874         //if(this.wrap){
9875         //    this.el.removeClass('x-item-disabled');
9876         //}
9877     },
9878
9879     // private
9880     onShow : function(){
9881         var ae = this.getActionEl();
9882         
9883         if(ae){
9884             ae.dom.style.display = '';
9885             ae.dom.style.visibility = 'visible';
9886         }
9887     },
9888
9889     // private
9890     
9891     onHide : function(){
9892         var ae = this.getActionEl();
9893         ae.dom.style.display = 'none';
9894     },
9895
9896     /**
9897      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9898      * by an implementing function.
9899      * @method
9900      * @param {EventObject} e
9901      */
9902     onTriggerClick : Roo.emptyFn
9903 });
9904  /*
9905  * Based on:
9906  * Ext JS Library 1.1.1
9907  * Copyright(c) 2006-2007, Ext JS, LLC.
9908  *
9909  * Originally Released Under LGPL - original licence link has changed is not relivant.
9910  *
9911  * Fork - LGPL
9912  * <script type="text/javascript">
9913  */
9914
9915
9916 /**
9917  * @class Roo.data.SortTypes
9918  * @singleton
9919  * Defines the default sorting (casting?) comparison functions used when sorting data.
9920  */
9921 Roo.data.SortTypes = {
9922     /**
9923      * Default sort that does nothing
9924      * @param {Mixed} s The value being converted
9925      * @return {Mixed} The comparison value
9926      */
9927     none : function(s){
9928         return s;
9929     },
9930     
9931     /**
9932      * The regular expression used to strip tags
9933      * @type {RegExp}
9934      * @property
9935      */
9936     stripTagsRE : /<\/?[^>]+>/gi,
9937     
9938     /**
9939      * Strips all HTML tags to sort on text only
9940      * @param {Mixed} s The value being converted
9941      * @return {String} The comparison value
9942      */
9943     asText : function(s){
9944         return String(s).replace(this.stripTagsRE, "");
9945     },
9946     
9947     /**
9948      * Strips all HTML tags to sort on text only - Case insensitive
9949      * @param {Mixed} s The value being converted
9950      * @return {String} The comparison value
9951      */
9952     asUCText : function(s){
9953         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9954     },
9955     
9956     /**
9957      * Case insensitive string
9958      * @param {Mixed} s The value being converted
9959      * @return {String} The comparison value
9960      */
9961     asUCString : function(s) {
9962         return String(s).toUpperCase();
9963     },
9964     
9965     /**
9966      * Date sorting
9967      * @param {Mixed} s The value being converted
9968      * @return {Number} The comparison value
9969      */
9970     asDate : function(s) {
9971         if(!s){
9972             return 0;
9973         }
9974         if(s instanceof Date){
9975             return s.getTime();
9976         }
9977         return Date.parse(String(s));
9978     },
9979     
9980     /**
9981      * Float sorting
9982      * @param {Mixed} s The value being converted
9983      * @return {Float} The comparison value
9984      */
9985     asFloat : function(s) {
9986         var val = parseFloat(String(s).replace(/,/g, ""));
9987         if(isNaN(val)) {
9988             val = 0;
9989         }
9990         return val;
9991     },
9992     
9993     /**
9994      * Integer sorting
9995      * @param {Mixed} s The value being converted
9996      * @return {Number} The comparison value
9997      */
9998     asInt : function(s) {
9999         var val = parseInt(String(s).replace(/,/g, ""));
10000         if(isNaN(val)) {
10001             val = 0;
10002         }
10003         return val;
10004     }
10005 };/*
10006  * Based on:
10007  * Ext JS Library 1.1.1
10008  * Copyright(c) 2006-2007, Ext JS, LLC.
10009  *
10010  * Originally Released Under LGPL - original licence link has changed is not relivant.
10011  *
10012  * Fork - LGPL
10013  * <script type="text/javascript">
10014  */
10015
10016 /**
10017 * @class Roo.data.Record
10018  * Instances of this class encapsulate both record <em>definition</em> information, and record
10019  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10020  * to access Records cached in an {@link Roo.data.Store} object.<br>
10021  * <p>
10022  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10023  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10024  * objects.<br>
10025  * <p>
10026  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10027  * @constructor
10028  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10029  * {@link #create}. The parameters are the same.
10030  * @param {Array} data An associative Array of data values keyed by the field name.
10031  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10032  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10033  * not specified an integer id is generated.
10034  */
10035 Roo.data.Record = function(data, id){
10036     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10037     this.data = data;
10038 };
10039
10040 /**
10041  * Generate a constructor for a specific record layout.
10042  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10043  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10044  * Each field definition object may contain the following properties: <ul>
10045  * <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,
10046  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10047  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10048  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10049  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10050  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10051  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10052  * this may be omitted.</p></li>
10053  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10054  * <ul><li>auto (Default, implies no conversion)</li>
10055  * <li>string</li>
10056  * <li>int</li>
10057  * <li>float</li>
10058  * <li>boolean</li>
10059  * <li>date</li></ul></p></li>
10060  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10061  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10062  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10063  * by the Reader into an object that will be stored in the Record. It is passed the
10064  * following parameters:<ul>
10065  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10066  * </ul></p></li>
10067  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10068  * </ul>
10069  * <br>usage:<br><pre><code>
10070 var TopicRecord = Roo.data.Record.create(
10071     {name: 'title', mapping: 'topic_title'},
10072     {name: 'author', mapping: 'username'},
10073     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10074     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10075     {name: 'lastPoster', mapping: 'user2'},
10076     {name: 'excerpt', mapping: 'post_text'}
10077 );
10078
10079 var myNewRecord = new TopicRecord({
10080     title: 'Do my job please',
10081     author: 'noobie',
10082     totalPosts: 1,
10083     lastPost: new Date(),
10084     lastPoster: 'Animal',
10085     excerpt: 'No way dude!'
10086 });
10087 myStore.add(myNewRecord);
10088 </code></pre>
10089  * @method create
10090  * @static
10091  */
10092 Roo.data.Record.create = function(o){
10093     var f = function(){
10094         f.superclass.constructor.apply(this, arguments);
10095     };
10096     Roo.extend(f, Roo.data.Record);
10097     var p = f.prototype;
10098     p.fields = new Roo.util.MixedCollection(false, function(field){
10099         return field.name;
10100     });
10101     for(var i = 0, len = o.length; i < len; i++){
10102         p.fields.add(new Roo.data.Field(o[i]));
10103     }
10104     f.getField = function(name){
10105         return p.fields.get(name);  
10106     };
10107     return f;
10108 };
10109
10110 Roo.data.Record.AUTO_ID = 1000;
10111 Roo.data.Record.EDIT = 'edit';
10112 Roo.data.Record.REJECT = 'reject';
10113 Roo.data.Record.COMMIT = 'commit';
10114
10115 Roo.data.Record.prototype = {
10116     /**
10117      * Readonly flag - true if this record has been modified.
10118      * @type Boolean
10119      */
10120     dirty : false,
10121     editing : false,
10122     error: null,
10123     modified: null,
10124
10125     // private
10126     join : function(store){
10127         this.store = store;
10128     },
10129
10130     /**
10131      * Set the named field to the specified value.
10132      * @param {String} name The name of the field to set.
10133      * @param {Object} value The value to set the field to.
10134      */
10135     set : function(name, value){
10136         if(this.data[name] == value){
10137             return;
10138         }
10139         this.dirty = true;
10140         if(!this.modified){
10141             this.modified = {};
10142         }
10143         if(typeof this.modified[name] == 'undefined'){
10144             this.modified[name] = this.data[name];
10145         }
10146         this.data[name] = value;
10147         if(!this.editing && this.store){
10148             this.store.afterEdit(this);
10149         }       
10150     },
10151
10152     /**
10153      * Get the value of the named field.
10154      * @param {String} name The name of the field to get the value of.
10155      * @return {Object} The value of the field.
10156      */
10157     get : function(name){
10158         return this.data[name]; 
10159     },
10160
10161     // private
10162     beginEdit : function(){
10163         this.editing = true;
10164         this.modified = {}; 
10165     },
10166
10167     // private
10168     cancelEdit : function(){
10169         this.editing = false;
10170         delete this.modified;
10171     },
10172
10173     // private
10174     endEdit : function(){
10175         this.editing = false;
10176         if(this.dirty && this.store){
10177             this.store.afterEdit(this);
10178         }
10179     },
10180
10181     /**
10182      * Usually called by the {@link Roo.data.Store} which owns the Record.
10183      * Rejects all changes made to the Record since either creation, or the last commit operation.
10184      * Modified fields are reverted to their original values.
10185      * <p>
10186      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10187      * of reject operations.
10188      */
10189     reject : function(){
10190         var m = this.modified;
10191         for(var n in m){
10192             if(typeof m[n] != "function"){
10193                 this.data[n] = m[n];
10194             }
10195         }
10196         this.dirty = false;
10197         delete this.modified;
10198         this.editing = false;
10199         if(this.store){
10200             this.store.afterReject(this);
10201         }
10202     },
10203
10204     /**
10205      * Usually called by the {@link Roo.data.Store} which owns the Record.
10206      * Commits all changes made to the Record since either creation, or the last commit operation.
10207      * <p>
10208      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10209      * of commit operations.
10210      */
10211     commit : function(){
10212         this.dirty = false;
10213         delete this.modified;
10214         this.editing = false;
10215         if(this.store){
10216             this.store.afterCommit(this);
10217         }
10218     },
10219
10220     // private
10221     hasError : function(){
10222         return this.error != null;
10223     },
10224
10225     // private
10226     clearError : function(){
10227         this.error = null;
10228     },
10229
10230     /**
10231      * Creates a copy of this record.
10232      * @param {String} id (optional) A new record id if you don't want to use this record's id
10233      * @return {Record}
10234      */
10235     copy : function(newId) {
10236         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10237     }
10238 };/*
10239  * Based on:
10240  * Ext JS Library 1.1.1
10241  * Copyright(c) 2006-2007, Ext JS, LLC.
10242  *
10243  * Originally Released Under LGPL - original licence link has changed is not relivant.
10244  *
10245  * Fork - LGPL
10246  * <script type="text/javascript">
10247  */
10248
10249
10250
10251 /**
10252  * @class Roo.data.Store
10253  * @extends Roo.util.Observable
10254  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10255  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10256  * <p>
10257  * 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
10258  * has no knowledge of the format of the data returned by the Proxy.<br>
10259  * <p>
10260  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10261  * instances from the data object. These records are cached and made available through accessor functions.
10262  * @constructor
10263  * Creates a new Store.
10264  * @param {Object} config A config object containing the objects needed for the Store to access data,
10265  * and read the data into Records.
10266  */
10267 Roo.data.Store = function(config){
10268     this.data = new Roo.util.MixedCollection(false);
10269     this.data.getKey = function(o){
10270         return o.id;
10271     };
10272     this.baseParams = {};
10273     // private
10274     this.paramNames = {
10275         "start" : "start",
10276         "limit" : "limit",
10277         "sort" : "sort",
10278         "dir" : "dir",
10279         "multisort" : "_multisort"
10280     };
10281
10282     if(config && config.data){
10283         this.inlineData = config.data;
10284         delete config.data;
10285     }
10286
10287     Roo.apply(this, config);
10288     
10289     if(this.reader){ // reader passed
10290         this.reader = Roo.factory(this.reader, Roo.data);
10291         this.reader.xmodule = this.xmodule || false;
10292         if(!this.recordType){
10293             this.recordType = this.reader.recordType;
10294         }
10295         if(this.reader.onMetaChange){
10296             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10297         }
10298     }
10299
10300     if(this.recordType){
10301         this.fields = this.recordType.prototype.fields;
10302     }
10303     this.modified = [];
10304
10305     this.addEvents({
10306         /**
10307          * @event datachanged
10308          * Fires when the data cache has changed, and a widget which is using this Store
10309          * as a Record cache should refresh its view.
10310          * @param {Store} this
10311          */
10312         datachanged : true,
10313         /**
10314          * @event metachange
10315          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10316          * @param {Store} this
10317          * @param {Object} meta The JSON metadata
10318          */
10319         metachange : true,
10320         /**
10321          * @event add
10322          * Fires when Records have been added to the Store
10323          * @param {Store} this
10324          * @param {Roo.data.Record[]} records The array of Records added
10325          * @param {Number} index The index at which the record(s) were added
10326          */
10327         add : true,
10328         /**
10329          * @event remove
10330          * Fires when a Record has been removed from the Store
10331          * @param {Store} this
10332          * @param {Roo.data.Record} record The Record that was removed
10333          * @param {Number} index The index at which the record was removed
10334          */
10335         remove : true,
10336         /**
10337          * @event update
10338          * Fires when a Record has been updated
10339          * @param {Store} this
10340          * @param {Roo.data.Record} record The Record that was updated
10341          * @param {String} operation The update operation being performed.  Value may be one of:
10342          * <pre><code>
10343  Roo.data.Record.EDIT
10344  Roo.data.Record.REJECT
10345  Roo.data.Record.COMMIT
10346          * </code></pre>
10347          */
10348         update : true,
10349         /**
10350          * @event clear
10351          * Fires when the data cache has been cleared.
10352          * @param {Store} this
10353          */
10354         clear : true,
10355         /**
10356          * @event beforeload
10357          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10358          * the load action will be canceled.
10359          * @param {Store} this
10360          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10361          */
10362         beforeload : true,
10363         /**
10364          * @event beforeloadadd
10365          * Fires after a new set of Records has been loaded.
10366          * @param {Store} this
10367          * @param {Roo.data.Record[]} records The Records that were loaded
10368          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10369          */
10370         beforeloadadd : true,
10371         /**
10372          * @event load
10373          * Fires after a new set of Records has been loaded, before they are added to the store.
10374          * @param {Store} this
10375          * @param {Roo.data.Record[]} records The Records that were loaded
10376          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10377          * @params {Object} return from reader
10378          */
10379         load : true,
10380         /**
10381          * @event loadexception
10382          * Fires if an exception occurs in the Proxy during loading.
10383          * Called with the signature of the Proxy's "loadexception" event.
10384          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10385          * 
10386          * @param {Proxy} 
10387          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10388          * @param {Object} load options 
10389          * @param {Object} jsonData from your request (normally this contains the Exception)
10390          */
10391         loadexception : true
10392     });
10393     
10394     if(this.proxy){
10395         this.proxy = Roo.factory(this.proxy, Roo.data);
10396         this.proxy.xmodule = this.xmodule || false;
10397         this.relayEvents(this.proxy,  ["loadexception"]);
10398     }
10399     this.sortToggle = {};
10400     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10401
10402     Roo.data.Store.superclass.constructor.call(this);
10403
10404     if(this.inlineData){
10405         this.loadData(this.inlineData);
10406         delete this.inlineData;
10407     }
10408 };
10409
10410 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10411      /**
10412     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10413     * without a remote query - used by combo/forms at present.
10414     */
10415     
10416     /**
10417     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10418     */
10419     /**
10420     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10421     */
10422     /**
10423     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10424     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10425     */
10426     /**
10427     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10428     * on any HTTP request
10429     */
10430     /**
10431     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10432     */
10433     /**
10434     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10435     */
10436     multiSort: false,
10437     /**
10438     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10439     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10440     */
10441     remoteSort : false,
10442
10443     /**
10444     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10445      * loaded or when a record is removed. (defaults to false).
10446     */
10447     pruneModifiedRecords : false,
10448
10449     // private
10450     lastOptions : null,
10451
10452     /**
10453      * Add Records to the Store and fires the add event.
10454      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10455      */
10456     add : function(records){
10457         records = [].concat(records);
10458         for(var i = 0, len = records.length; i < len; i++){
10459             records[i].join(this);
10460         }
10461         var index = this.data.length;
10462         this.data.addAll(records);
10463         this.fireEvent("add", this, records, index);
10464     },
10465
10466     /**
10467      * Remove a Record from the Store and fires the remove event.
10468      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10469      */
10470     remove : function(record){
10471         var index = this.data.indexOf(record);
10472         this.data.removeAt(index);
10473         if(this.pruneModifiedRecords){
10474             this.modified.remove(record);
10475         }
10476         this.fireEvent("remove", this, record, index);
10477     },
10478
10479     /**
10480      * Remove all Records from the Store and fires the clear event.
10481      */
10482     removeAll : function(){
10483         this.data.clear();
10484         if(this.pruneModifiedRecords){
10485             this.modified = [];
10486         }
10487         this.fireEvent("clear", this);
10488     },
10489
10490     /**
10491      * Inserts Records to the Store at the given index and fires the add event.
10492      * @param {Number} index The start index at which to insert the passed Records.
10493      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10494      */
10495     insert : function(index, records){
10496         records = [].concat(records);
10497         for(var i = 0, len = records.length; i < len; i++){
10498             this.data.insert(index, records[i]);
10499             records[i].join(this);
10500         }
10501         this.fireEvent("add", this, records, index);
10502     },
10503
10504     /**
10505      * Get the index within the cache of the passed Record.
10506      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10507      * @return {Number} The index of the passed Record. Returns -1 if not found.
10508      */
10509     indexOf : function(record){
10510         return this.data.indexOf(record);
10511     },
10512
10513     /**
10514      * Get the index within the cache of the Record with the passed id.
10515      * @param {String} id The id of the Record to find.
10516      * @return {Number} The index of the Record. Returns -1 if not found.
10517      */
10518     indexOfId : function(id){
10519         return this.data.indexOfKey(id);
10520     },
10521
10522     /**
10523      * Get the Record with the specified id.
10524      * @param {String} id The id of the Record to find.
10525      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10526      */
10527     getById : function(id){
10528         return this.data.key(id);
10529     },
10530
10531     /**
10532      * Get the Record at the specified index.
10533      * @param {Number} index The index of the Record to find.
10534      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10535      */
10536     getAt : function(index){
10537         return this.data.itemAt(index);
10538     },
10539
10540     /**
10541      * Returns a range of Records between specified indices.
10542      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10543      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10544      * @return {Roo.data.Record[]} An array of Records
10545      */
10546     getRange : function(start, end){
10547         return this.data.getRange(start, end);
10548     },
10549
10550     // private
10551     storeOptions : function(o){
10552         o = Roo.apply({}, o);
10553         delete o.callback;
10554         delete o.scope;
10555         this.lastOptions = o;
10556     },
10557
10558     /**
10559      * Loads the Record cache from the configured Proxy using the configured Reader.
10560      * <p>
10561      * If using remote paging, then the first load call must specify the <em>start</em>
10562      * and <em>limit</em> properties in the options.params property to establish the initial
10563      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10564      * <p>
10565      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10566      * and this call will return before the new data has been loaded. Perform any post-processing
10567      * in a callback function, or in a "load" event handler.</strong>
10568      * <p>
10569      * @param {Object} options An object containing properties which control loading options:<ul>
10570      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10571      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10572      * passed the following arguments:<ul>
10573      * <li>r : Roo.data.Record[]</li>
10574      * <li>options: Options object from the load call</li>
10575      * <li>success: Boolean success indicator</li></ul></li>
10576      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10577      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10578      * </ul>
10579      */
10580     load : function(options){
10581         options = options || {};
10582         if(this.fireEvent("beforeload", this, options) !== false){
10583             this.storeOptions(options);
10584             var p = Roo.apply(options.params || {}, this.baseParams);
10585             // if meta was not loaded from remote source.. try requesting it.
10586             if (!this.reader.metaFromRemote) {
10587                 p._requestMeta = 1;
10588             }
10589             if(this.sortInfo && this.remoteSort){
10590                 var pn = this.paramNames;
10591                 p[pn["sort"]] = this.sortInfo.field;
10592                 p[pn["dir"]] = this.sortInfo.direction;
10593             }
10594             if (this.multiSort) {
10595                 var pn = this.paramNames;
10596                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10597             }
10598             
10599             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10600         }
10601     },
10602
10603     /**
10604      * Reloads the Record cache from the configured Proxy using the configured Reader and
10605      * the options from the last load operation performed.
10606      * @param {Object} options (optional) An object containing properties which may override the options
10607      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10608      * the most recently used options are reused).
10609      */
10610     reload : function(options){
10611         this.load(Roo.applyIf(options||{}, this.lastOptions));
10612     },
10613
10614     // private
10615     // Called as a callback by the Reader during a load operation.
10616     loadRecords : function(o, options, success){
10617         if(!o || success === false){
10618             if(success !== false){
10619                 this.fireEvent("load", this, [], options, o);
10620             }
10621             if(options.callback){
10622                 options.callback.call(options.scope || this, [], options, false);
10623             }
10624             return;
10625         }
10626         // if data returned failure - throw an exception.
10627         if (o.success === false) {
10628             // show a message if no listener is registered.
10629             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10630                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10631             }
10632             // loadmask wil be hooked into this..
10633             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10634             return;
10635         }
10636         var r = o.records, t = o.totalRecords || r.length;
10637         
10638         this.fireEvent("beforeloadadd", this, r, options, o);
10639         
10640         if(!options || options.add !== true){
10641             if(this.pruneModifiedRecords){
10642                 this.modified = [];
10643             }
10644             for(var i = 0, len = r.length; i < len; i++){
10645                 r[i].join(this);
10646             }
10647             if(this.snapshot){
10648                 this.data = this.snapshot;
10649                 delete this.snapshot;
10650             }
10651             this.data.clear();
10652             this.data.addAll(r);
10653             this.totalLength = t;
10654             this.applySort();
10655             this.fireEvent("datachanged", this);
10656         }else{
10657             this.totalLength = Math.max(t, this.data.length+r.length);
10658             this.add(r);
10659         }
10660         this.fireEvent("load", this, r, options, o);
10661         if(options.callback){
10662             options.callback.call(options.scope || this, r, options, true);
10663         }
10664     },
10665
10666
10667     /**
10668      * Loads data from a passed data block. A Reader which understands the format of the data
10669      * must have been configured in the constructor.
10670      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10671      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10672      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10673      */
10674     loadData : function(o, append){
10675         var r = this.reader.readRecords(o);
10676         this.loadRecords(r, {add: append}, true);
10677     },
10678
10679     /**
10680      * Gets the number of cached records.
10681      * <p>
10682      * <em>If using paging, this may not be the total size of the dataset. If the data object
10683      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10684      * the data set size</em>
10685      */
10686     getCount : function(){
10687         return this.data.length || 0;
10688     },
10689
10690     /**
10691      * Gets the total number of records in the dataset as returned by the server.
10692      * <p>
10693      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10694      * the dataset size</em>
10695      */
10696     getTotalCount : function(){
10697         return this.totalLength || 0;
10698     },
10699
10700     /**
10701      * Returns the sort state of the Store as an object with two properties:
10702      * <pre><code>
10703  field {String} The name of the field by which the Records are sorted
10704  direction {String} The sort order, "ASC" or "DESC"
10705      * </code></pre>
10706      */
10707     getSortState : function(){
10708         return this.sortInfo;
10709     },
10710
10711     // private
10712     applySort : function(){
10713         if(this.sortInfo && !this.remoteSort){
10714             var s = this.sortInfo, f = s.field;
10715             var st = this.fields.get(f).sortType;
10716             var fn = function(r1, r2){
10717                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10718                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10719             };
10720             this.data.sort(s.direction, fn);
10721             if(this.snapshot && this.snapshot != this.data){
10722                 this.snapshot.sort(s.direction, fn);
10723             }
10724         }
10725     },
10726
10727     /**
10728      * Sets the default sort column and order to be used by the next load operation.
10729      * @param {String} fieldName The name of the field to sort by.
10730      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10731      */
10732     setDefaultSort : function(field, dir){
10733         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10734     },
10735
10736     /**
10737      * Sort the Records.
10738      * If remote sorting is used, the sort is performed on the server, and the cache is
10739      * reloaded. If local sorting is used, the cache is sorted internally.
10740      * @param {String} fieldName The name of the field to sort by.
10741      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10742      */
10743     sort : function(fieldName, dir){
10744         var f = this.fields.get(fieldName);
10745         if(!dir){
10746             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10747             
10748             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10749                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10750             }else{
10751                 dir = f.sortDir;
10752             }
10753         }
10754         this.sortToggle[f.name] = dir;
10755         this.sortInfo = {field: f.name, direction: dir};
10756         if(!this.remoteSort){
10757             this.applySort();
10758             this.fireEvent("datachanged", this);
10759         }else{
10760             this.load(this.lastOptions);
10761         }
10762     },
10763
10764     /**
10765      * Calls the specified function for each of the Records in the cache.
10766      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10767      * Returning <em>false</em> aborts and exits the iteration.
10768      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10769      */
10770     each : function(fn, scope){
10771         this.data.each(fn, scope);
10772     },
10773
10774     /**
10775      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10776      * (e.g., during paging).
10777      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10778      */
10779     getModifiedRecords : function(){
10780         return this.modified;
10781     },
10782
10783     // private
10784     createFilterFn : function(property, value, anyMatch){
10785         if(!value.exec){ // not a regex
10786             value = String(value);
10787             if(value.length == 0){
10788                 return false;
10789             }
10790             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10791         }
10792         return function(r){
10793             return value.test(r.data[property]);
10794         };
10795     },
10796
10797     /**
10798      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10799      * @param {String} property A field on your records
10800      * @param {Number} start The record index to start at (defaults to 0)
10801      * @param {Number} end The last record index to include (defaults to length - 1)
10802      * @return {Number} The sum
10803      */
10804     sum : function(property, start, end){
10805         var rs = this.data.items, v = 0;
10806         start = start || 0;
10807         end = (end || end === 0) ? end : rs.length-1;
10808
10809         for(var i = start; i <= end; i++){
10810             v += (rs[i].data[property] || 0);
10811         }
10812         return v;
10813     },
10814
10815     /**
10816      * Filter the records by a specified property.
10817      * @param {String} field A field on your records
10818      * @param {String/RegExp} value Either a string that the field
10819      * should start with or a RegExp to test against the field
10820      * @param {Boolean} anyMatch True to match any part not just the beginning
10821      */
10822     filter : function(property, value, anyMatch){
10823         var fn = this.createFilterFn(property, value, anyMatch);
10824         return fn ? this.filterBy(fn) : this.clearFilter();
10825     },
10826
10827     /**
10828      * Filter by a function. The specified function will be called with each
10829      * record in this data source. If the function returns true the record is included,
10830      * otherwise it is filtered.
10831      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10832      * @param {Object} scope (optional) The scope of the function (defaults to this)
10833      */
10834     filterBy : function(fn, scope){
10835         this.snapshot = this.snapshot || this.data;
10836         this.data = this.queryBy(fn, scope||this);
10837         this.fireEvent("datachanged", this);
10838     },
10839
10840     /**
10841      * Query the records by a specified property.
10842      * @param {String} field A field on your records
10843      * @param {String/RegExp} value Either a string that the field
10844      * should start with or a RegExp to test against the field
10845      * @param {Boolean} anyMatch True to match any part not just the beginning
10846      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10847      */
10848     query : function(property, value, anyMatch){
10849         var fn = this.createFilterFn(property, value, anyMatch);
10850         return fn ? this.queryBy(fn) : this.data.clone();
10851     },
10852
10853     /**
10854      * Query by a function. The specified function will be called with each
10855      * record in this data source. If the function returns true the record is included
10856      * in the results.
10857      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10858      * @param {Object} scope (optional) The scope of the function (defaults to this)
10859       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10860      **/
10861     queryBy : function(fn, scope){
10862         var data = this.snapshot || this.data;
10863         return data.filterBy(fn, scope||this);
10864     },
10865
10866     /**
10867      * Collects unique values for a particular dataIndex from this store.
10868      * @param {String} dataIndex The property to collect
10869      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10870      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10871      * @return {Array} An array of the unique values
10872      **/
10873     collect : function(dataIndex, allowNull, bypassFilter){
10874         var d = (bypassFilter === true && this.snapshot) ?
10875                 this.snapshot.items : this.data.items;
10876         var v, sv, r = [], l = {};
10877         for(var i = 0, len = d.length; i < len; i++){
10878             v = d[i].data[dataIndex];
10879             sv = String(v);
10880             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10881                 l[sv] = true;
10882                 r[r.length] = v;
10883             }
10884         }
10885         return r;
10886     },
10887
10888     /**
10889      * Revert to a view of the Record cache with no filtering applied.
10890      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10891      */
10892     clearFilter : function(suppressEvent){
10893         if(this.snapshot && this.snapshot != this.data){
10894             this.data = this.snapshot;
10895             delete this.snapshot;
10896             if(suppressEvent !== true){
10897                 this.fireEvent("datachanged", this);
10898             }
10899         }
10900     },
10901
10902     // private
10903     afterEdit : function(record){
10904         if(this.modified.indexOf(record) == -1){
10905             this.modified.push(record);
10906         }
10907         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10908     },
10909     
10910     // private
10911     afterReject : function(record){
10912         this.modified.remove(record);
10913         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10914     },
10915
10916     // private
10917     afterCommit : function(record){
10918         this.modified.remove(record);
10919         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10920     },
10921
10922     /**
10923      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10924      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10925      */
10926     commitChanges : function(){
10927         var m = this.modified.slice(0);
10928         this.modified = [];
10929         for(var i = 0, len = m.length; i < len; i++){
10930             m[i].commit();
10931         }
10932     },
10933
10934     /**
10935      * Cancel outstanding changes on all changed records.
10936      */
10937     rejectChanges : function(){
10938         var m = this.modified.slice(0);
10939         this.modified = [];
10940         for(var i = 0, len = m.length; i < len; i++){
10941             m[i].reject();
10942         }
10943     },
10944
10945     onMetaChange : function(meta, rtype, o){
10946         this.recordType = rtype;
10947         this.fields = rtype.prototype.fields;
10948         delete this.snapshot;
10949         this.sortInfo = meta.sortInfo || this.sortInfo;
10950         this.modified = [];
10951         this.fireEvent('metachange', this, this.reader.meta);
10952     },
10953     
10954     moveIndex : function(data, type)
10955     {
10956         var index = this.indexOf(data);
10957         
10958         var newIndex = index + type;
10959         
10960         this.remove(data);
10961         
10962         this.insert(newIndex, data);
10963         
10964     }
10965 });/*
10966  * Based on:
10967  * Ext JS Library 1.1.1
10968  * Copyright(c) 2006-2007, Ext JS, LLC.
10969  *
10970  * Originally Released Under LGPL - original licence link has changed is not relivant.
10971  *
10972  * Fork - LGPL
10973  * <script type="text/javascript">
10974  */
10975
10976 /**
10977  * @class Roo.data.SimpleStore
10978  * @extends Roo.data.Store
10979  * Small helper class to make creating Stores from Array data easier.
10980  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10981  * @cfg {Array} fields An array of field definition objects, or field name strings.
10982  * @cfg {Array} data The multi-dimensional array of data
10983  * @constructor
10984  * @param {Object} config
10985  */
10986 Roo.data.SimpleStore = function(config){
10987     Roo.data.SimpleStore.superclass.constructor.call(this, {
10988         isLocal : true,
10989         reader: new Roo.data.ArrayReader({
10990                 id: config.id
10991             },
10992             Roo.data.Record.create(config.fields)
10993         ),
10994         proxy : new Roo.data.MemoryProxy(config.data)
10995     });
10996     this.load();
10997 };
10998 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10999  * Based on:
11000  * Ext JS Library 1.1.1
11001  * Copyright(c) 2006-2007, Ext JS, LLC.
11002  *
11003  * Originally Released Under LGPL - original licence link has changed is not relivant.
11004  *
11005  * Fork - LGPL
11006  * <script type="text/javascript">
11007  */
11008
11009 /**
11010 /**
11011  * @extends Roo.data.Store
11012  * @class Roo.data.JsonStore
11013  * Small helper class to make creating Stores for JSON data easier. <br/>
11014 <pre><code>
11015 var store = new Roo.data.JsonStore({
11016     url: 'get-images.php',
11017     root: 'images',
11018     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11019 });
11020 </code></pre>
11021  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11022  * JsonReader and HttpProxy (unless inline data is provided).</b>
11023  * @cfg {Array} fields An array of field definition objects, or field name strings.
11024  * @constructor
11025  * @param {Object} config
11026  */
11027 Roo.data.JsonStore = function(c){
11028     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11029         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11030         reader: new Roo.data.JsonReader(c, c.fields)
11031     }));
11032 };
11033 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11034  * Based on:
11035  * Ext JS Library 1.1.1
11036  * Copyright(c) 2006-2007, Ext JS, LLC.
11037  *
11038  * Originally Released Under LGPL - original licence link has changed is not relivant.
11039  *
11040  * Fork - LGPL
11041  * <script type="text/javascript">
11042  */
11043
11044  
11045 Roo.data.Field = function(config){
11046     if(typeof config == "string"){
11047         config = {name: config};
11048     }
11049     Roo.apply(this, config);
11050     
11051     if(!this.type){
11052         this.type = "auto";
11053     }
11054     
11055     var st = Roo.data.SortTypes;
11056     // named sortTypes are supported, here we look them up
11057     if(typeof this.sortType == "string"){
11058         this.sortType = st[this.sortType];
11059     }
11060     
11061     // set default sortType for strings and dates
11062     if(!this.sortType){
11063         switch(this.type){
11064             case "string":
11065                 this.sortType = st.asUCString;
11066                 break;
11067             case "date":
11068                 this.sortType = st.asDate;
11069                 break;
11070             default:
11071                 this.sortType = st.none;
11072         }
11073     }
11074
11075     // define once
11076     var stripRe = /[\$,%]/g;
11077
11078     // prebuilt conversion function for this field, instead of
11079     // switching every time we're reading a value
11080     if(!this.convert){
11081         var cv, dateFormat = this.dateFormat;
11082         switch(this.type){
11083             case "":
11084             case "auto":
11085             case undefined:
11086                 cv = function(v){ return v; };
11087                 break;
11088             case "string":
11089                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11090                 break;
11091             case "int":
11092                 cv = function(v){
11093                     return v !== undefined && v !== null && v !== '' ?
11094                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11095                     };
11096                 break;
11097             case "float":
11098                 cv = function(v){
11099                     return v !== undefined && v !== null && v !== '' ?
11100                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11101                     };
11102                 break;
11103             case "bool":
11104             case "boolean":
11105                 cv = function(v){ return v === true || v === "true" || v == 1; };
11106                 break;
11107             case "date":
11108                 cv = function(v){
11109                     if(!v){
11110                         return '';
11111                     }
11112                     if(v instanceof Date){
11113                         return v;
11114                     }
11115                     if(dateFormat){
11116                         if(dateFormat == "timestamp"){
11117                             return new Date(v*1000);
11118                         }
11119                         return Date.parseDate(v, dateFormat);
11120                     }
11121                     var parsed = Date.parse(v);
11122                     return parsed ? new Date(parsed) : null;
11123                 };
11124              break;
11125             
11126         }
11127         this.convert = cv;
11128     }
11129 };
11130
11131 Roo.data.Field.prototype = {
11132     dateFormat: null,
11133     defaultValue: "",
11134     mapping: null,
11135     sortType : null,
11136     sortDir : "ASC"
11137 };/*
11138  * Based on:
11139  * Ext JS Library 1.1.1
11140  * Copyright(c) 2006-2007, Ext JS, LLC.
11141  *
11142  * Originally Released Under LGPL - original licence link has changed is not relivant.
11143  *
11144  * Fork - LGPL
11145  * <script type="text/javascript">
11146  */
11147  
11148 // Base class for reading structured data from a data source.  This class is intended to be
11149 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11150
11151 /**
11152  * @class Roo.data.DataReader
11153  * Base class for reading structured data from a data source.  This class is intended to be
11154  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11155  */
11156
11157 Roo.data.DataReader = function(meta, recordType){
11158     
11159     this.meta = meta;
11160     
11161     this.recordType = recordType instanceof Array ? 
11162         Roo.data.Record.create(recordType) : recordType;
11163 };
11164
11165 Roo.data.DataReader.prototype = {
11166      /**
11167      * Create an empty record
11168      * @param {Object} data (optional) - overlay some values
11169      * @return {Roo.data.Record} record created.
11170      */
11171     newRow :  function(d) {
11172         var da =  {};
11173         this.recordType.prototype.fields.each(function(c) {
11174             switch( c.type) {
11175                 case 'int' : da[c.name] = 0; break;
11176                 case 'date' : da[c.name] = new Date(); break;
11177                 case 'float' : da[c.name] = 0.0; break;
11178                 case 'boolean' : da[c.name] = false; break;
11179                 default : da[c.name] = ""; break;
11180             }
11181             
11182         });
11183         return new this.recordType(Roo.apply(da, d));
11184     }
11185     
11186 };/*
11187  * Based on:
11188  * Ext JS Library 1.1.1
11189  * Copyright(c) 2006-2007, Ext JS, LLC.
11190  *
11191  * Originally Released Under LGPL - original licence link has changed is not relivant.
11192  *
11193  * Fork - LGPL
11194  * <script type="text/javascript">
11195  */
11196
11197 /**
11198  * @class Roo.data.DataProxy
11199  * @extends Roo.data.Observable
11200  * This class is an abstract base class for implementations which provide retrieval of
11201  * unformatted data objects.<br>
11202  * <p>
11203  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11204  * (of the appropriate type which knows how to parse the data object) to provide a block of
11205  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11206  * <p>
11207  * Custom implementations must implement the load method as described in
11208  * {@link Roo.data.HttpProxy#load}.
11209  */
11210 Roo.data.DataProxy = function(){
11211     this.addEvents({
11212         /**
11213          * @event beforeload
11214          * Fires before a network request is made to retrieve a data object.
11215          * @param {Object} This DataProxy object.
11216          * @param {Object} params The params parameter to the load function.
11217          */
11218         beforeload : true,
11219         /**
11220          * @event load
11221          * Fires before the load method's callback is called.
11222          * @param {Object} This DataProxy object.
11223          * @param {Object} o The data object.
11224          * @param {Object} arg The callback argument object passed to the load function.
11225          */
11226         load : true,
11227         /**
11228          * @event loadexception
11229          * Fires if an Exception occurs during data retrieval.
11230          * @param {Object} This DataProxy object.
11231          * @param {Object} o The data object.
11232          * @param {Object} arg The callback argument object passed to the load function.
11233          * @param {Object} e The Exception.
11234          */
11235         loadexception : true
11236     });
11237     Roo.data.DataProxy.superclass.constructor.call(this);
11238 };
11239
11240 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11241
11242     /**
11243      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11244      */
11245 /*
11246  * Based on:
11247  * Ext JS Library 1.1.1
11248  * Copyright(c) 2006-2007, Ext JS, LLC.
11249  *
11250  * Originally Released Under LGPL - original licence link has changed is not relivant.
11251  *
11252  * Fork - LGPL
11253  * <script type="text/javascript">
11254  */
11255 /**
11256  * @class Roo.data.MemoryProxy
11257  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11258  * to the Reader when its load method is called.
11259  * @constructor
11260  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11261  */
11262 Roo.data.MemoryProxy = function(data){
11263     if (data.data) {
11264         data = data.data;
11265     }
11266     Roo.data.MemoryProxy.superclass.constructor.call(this);
11267     this.data = data;
11268 };
11269
11270 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11271     
11272     /**
11273      * Load data from the requested source (in this case an in-memory
11274      * data object passed to the constructor), read the data object into
11275      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11276      * process that block using the passed callback.
11277      * @param {Object} params This parameter is not used by the MemoryProxy class.
11278      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11279      * object into a block of Roo.data.Records.
11280      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11281      * The function must be passed <ul>
11282      * <li>The Record block object</li>
11283      * <li>The "arg" argument from the load function</li>
11284      * <li>A boolean success indicator</li>
11285      * </ul>
11286      * @param {Object} scope The scope in which to call the callback
11287      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11288      */
11289     load : function(params, reader, callback, scope, arg){
11290         params = params || {};
11291         var result;
11292         try {
11293             result = reader.readRecords(this.data);
11294         }catch(e){
11295             this.fireEvent("loadexception", this, arg, null, e);
11296             callback.call(scope, null, arg, false);
11297             return;
11298         }
11299         callback.call(scope, result, arg, true);
11300     },
11301     
11302     // private
11303     update : function(params, records){
11304         
11305     }
11306 });/*
11307  * Based on:
11308  * Ext JS Library 1.1.1
11309  * Copyright(c) 2006-2007, Ext JS, LLC.
11310  *
11311  * Originally Released Under LGPL - original licence link has changed is not relivant.
11312  *
11313  * Fork - LGPL
11314  * <script type="text/javascript">
11315  */
11316 /**
11317  * @class Roo.data.HttpProxy
11318  * @extends Roo.data.DataProxy
11319  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11320  * configured to reference a certain URL.<br><br>
11321  * <p>
11322  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11323  * from which the running page was served.<br><br>
11324  * <p>
11325  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11326  * <p>
11327  * Be aware that to enable the browser to parse an XML document, the server must set
11328  * the Content-Type header in the HTTP response to "text/xml".
11329  * @constructor
11330  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11331  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11332  * will be used to make the request.
11333  */
11334 Roo.data.HttpProxy = function(conn){
11335     Roo.data.HttpProxy.superclass.constructor.call(this);
11336     // is conn a conn config or a real conn?
11337     this.conn = conn;
11338     this.useAjax = !conn || !conn.events;
11339   
11340 };
11341
11342 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11343     // thse are take from connection...
11344     
11345     /**
11346      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11347      */
11348     /**
11349      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11350      * extra parameters to each request made by this object. (defaults to undefined)
11351      */
11352     /**
11353      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11354      *  to each request made by this object. (defaults to undefined)
11355      */
11356     /**
11357      * @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)
11358      */
11359     /**
11360      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11361      */
11362      /**
11363      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11364      * @type Boolean
11365      */
11366   
11367
11368     /**
11369      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11370      * @type Boolean
11371      */
11372     /**
11373      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11374      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11375      * a finer-grained basis than the DataProxy events.
11376      */
11377     getConnection : function(){
11378         return this.useAjax ? Roo.Ajax : this.conn;
11379     },
11380
11381     /**
11382      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11383      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11384      * process that block using the passed callback.
11385      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11386      * for the request to the remote server.
11387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11388      * object into a block of Roo.data.Records.
11389      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11390      * The function must be passed <ul>
11391      * <li>The Record block object</li>
11392      * <li>The "arg" argument from the load function</li>
11393      * <li>A boolean success indicator</li>
11394      * </ul>
11395      * @param {Object} scope The scope in which to call the callback
11396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11397      */
11398     load : function(params, reader, callback, scope, arg){
11399         if(this.fireEvent("beforeload", this, params) !== false){
11400             var  o = {
11401                 params : params || {},
11402                 request: {
11403                     callback : callback,
11404                     scope : scope,
11405                     arg : arg
11406                 },
11407                 reader: reader,
11408                 callback : this.loadResponse,
11409                 scope: this
11410             };
11411             if(this.useAjax){
11412                 Roo.applyIf(o, this.conn);
11413                 if(this.activeRequest){
11414                     Roo.Ajax.abort(this.activeRequest);
11415                 }
11416                 this.activeRequest = Roo.Ajax.request(o);
11417             }else{
11418                 this.conn.request(o);
11419             }
11420         }else{
11421             callback.call(scope||this, null, arg, false);
11422         }
11423     },
11424
11425     // private
11426     loadResponse : function(o, success, response){
11427         delete this.activeRequest;
11428         if(!success){
11429             this.fireEvent("loadexception", this, o, response);
11430             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11431             return;
11432         }
11433         var result;
11434         try {
11435             result = o.reader.read(response);
11436         }catch(e){
11437             this.fireEvent("loadexception", this, o, response, e);
11438             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11439             return;
11440         }
11441         
11442         this.fireEvent("load", this, o, o.request.arg);
11443         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11444     },
11445
11446     // private
11447     update : function(dataSet){
11448
11449     },
11450
11451     // private
11452     updateResponse : function(dataSet){
11453
11454     }
11455 });/*
11456  * Based on:
11457  * Ext JS Library 1.1.1
11458  * Copyright(c) 2006-2007, Ext JS, LLC.
11459  *
11460  * Originally Released Under LGPL - original licence link has changed is not relivant.
11461  *
11462  * Fork - LGPL
11463  * <script type="text/javascript">
11464  */
11465
11466 /**
11467  * @class Roo.data.ScriptTagProxy
11468  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11469  * other than the originating domain of the running page.<br><br>
11470  * <p>
11471  * <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
11472  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11473  * <p>
11474  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11475  * source code that is used as the source inside a &lt;script> tag.<br><br>
11476  * <p>
11477  * In order for the browser to process the returned data, the server must wrap the data object
11478  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11479  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11480  * depending on whether the callback name was passed:
11481  * <p>
11482  * <pre><code>
11483 boolean scriptTag = false;
11484 String cb = request.getParameter("callback");
11485 if (cb != null) {
11486     scriptTag = true;
11487     response.setContentType("text/javascript");
11488 } else {
11489     response.setContentType("application/x-json");
11490 }
11491 Writer out = response.getWriter();
11492 if (scriptTag) {
11493     out.write(cb + "(");
11494 }
11495 out.print(dataBlock.toJsonString());
11496 if (scriptTag) {
11497     out.write(");");
11498 }
11499 </pre></code>
11500  *
11501  * @constructor
11502  * @param {Object} config A configuration object.
11503  */
11504 Roo.data.ScriptTagProxy = function(config){
11505     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11506     Roo.apply(this, config);
11507     this.head = document.getElementsByTagName("head")[0];
11508 };
11509
11510 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11511
11512 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11513     /**
11514      * @cfg {String} url The URL from which to request the data object.
11515      */
11516     /**
11517      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11518      */
11519     timeout : 30000,
11520     /**
11521      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11522      * the server the name of the callback function set up by the load call to process the returned data object.
11523      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11524      * javascript output which calls this named function passing the data object as its only parameter.
11525      */
11526     callbackParam : "callback",
11527     /**
11528      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11529      * name to the request.
11530      */
11531     nocache : true,
11532
11533     /**
11534      * Load data from the configured URL, read the data object into
11535      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11536      * process that block using the passed callback.
11537      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11538      * for the request to the remote server.
11539      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11540      * object into a block of Roo.data.Records.
11541      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11542      * The function must be passed <ul>
11543      * <li>The Record block object</li>
11544      * <li>The "arg" argument from the load function</li>
11545      * <li>A boolean success indicator</li>
11546      * </ul>
11547      * @param {Object} scope The scope in which to call the callback
11548      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11549      */
11550     load : function(params, reader, callback, scope, arg){
11551         if(this.fireEvent("beforeload", this, params) !== false){
11552
11553             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11554
11555             var url = this.url;
11556             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11557             if(this.nocache){
11558                 url += "&_dc=" + (new Date().getTime());
11559             }
11560             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11561             var trans = {
11562                 id : transId,
11563                 cb : "stcCallback"+transId,
11564                 scriptId : "stcScript"+transId,
11565                 params : params,
11566                 arg : arg,
11567                 url : url,
11568                 callback : callback,
11569                 scope : scope,
11570                 reader : reader
11571             };
11572             var conn = this;
11573
11574             window[trans.cb] = function(o){
11575                 conn.handleResponse(o, trans);
11576             };
11577
11578             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11579
11580             if(this.autoAbort !== false){
11581                 this.abort();
11582             }
11583
11584             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11585
11586             var script = document.createElement("script");
11587             script.setAttribute("src", url);
11588             script.setAttribute("type", "text/javascript");
11589             script.setAttribute("id", trans.scriptId);
11590             this.head.appendChild(script);
11591
11592             this.trans = trans;
11593         }else{
11594             callback.call(scope||this, null, arg, false);
11595         }
11596     },
11597
11598     // private
11599     isLoading : function(){
11600         return this.trans ? true : false;
11601     },
11602
11603     /**
11604      * Abort the current server request.
11605      */
11606     abort : function(){
11607         if(this.isLoading()){
11608             this.destroyTrans(this.trans);
11609         }
11610     },
11611
11612     // private
11613     destroyTrans : function(trans, isLoaded){
11614         this.head.removeChild(document.getElementById(trans.scriptId));
11615         clearTimeout(trans.timeoutId);
11616         if(isLoaded){
11617             window[trans.cb] = undefined;
11618             try{
11619                 delete window[trans.cb];
11620             }catch(e){}
11621         }else{
11622             // if hasn't been loaded, wait for load to remove it to prevent script error
11623             window[trans.cb] = function(){
11624                 window[trans.cb] = undefined;
11625                 try{
11626                     delete window[trans.cb];
11627                 }catch(e){}
11628             };
11629         }
11630     },
11631
11632     // private
11633     handleResponse : function(o, trans){
11634         this.trans = false;
11635         this.destroyTrans(trans, true);
11636         var result;
11637         try {
11638             result = trans.reader.readRecords(o);
11639         }catch(e){
11640             this.fireEvent("loadexception", this, o, trans.arg, e);
11641             trans.callback.call(trans.scope||window, null, trans.arg, false);
11642             return;
11643         }
11644         this.fireEvent("load", this, o, trans.arg);
11645         trans.callback.call(trans.scope||window, result, trans.arg, true);
11646     },
11647
11648     // private
11649     handleFailure : function(trans){
11650         this.trans = false;
11651         this.destroyTrans(trans, false);
11652         this.fireEvent("loadexception", this, null, trans.arg);
11653         trans.callback.call(trans.scope||window, null, trans.arg, false);
11654     }
11655 });/*
11656  * Based on:
11657  * Ext JS Library 1.1.1
11658  * Copyright(c) 2006-2007, Ext JS, LLC.
11659  *
11660  * Originally Released Under LGPL - original licence link has changed is not relivant.
11661  *
11662  * Fork - LGPL
11663  * <script type="text/javascript">
11664  */
11665
11666 /**
11667  * @class Roo.data.JsonReader
11668  * @extends Roo.data.DataReader
11669  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11670  * based on mappings in a provided Roo.data.Record constructor.
11671  * 
11672  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11673  * in the reply previously. 
11674  * 
11675  * <p>
11676  * Example code:
11677  * <pre><code>
11678 var RecordDef = Roo.data.Record.create([
11679     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11680     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11681 ]);
11682 var myReader = new Roo.data.JsonReader({
11683     totalProperty: "results",    // The property which contains the total dataset size (optional)
11684     root: "rows",                // The property which contains an Array of row objects
11685     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11686 }, RecordDef);
11687 </code></pre>
11688  * <p>
11689  * This would consume a JSON file like this:
11690  * <pre><code>
11691 { 'results': 2, 'rows': [
11692     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11693     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11694 }
11695 </code></pre>
11696  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11697  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11698  * paged from the remote server.
11699  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11700  * @cfg {String} root name of the property which contains the Array of row objects.
11701  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11702  * @cfg {Array} fields Array of field definition objects
11703  * @constructor
11704  * Create a new JsonReader
11705  * @param {Object} meta Metadata configuration options
11706  * @param {Object} recordType Either an Array of field definition objects,
11707  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11708  */
11709 Roo.data.JsonReader = function(meta, recordType){
11710     
11711     meta = meta || {};
11712     // set some defaults:
11713     Roo.applyIf(meta, {
11714         totalProperty: 'total',
11715         successProperty : 'success',
11716         root : 'data',
11717         id : 'id'
11718     });
11719     
11720     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11721 };
11722 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11723     
11724     /**
11725      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11726      * Used by Store query builder to append _requestMeta to params.
11727      * 
11728      */
11729     metaFromRemote : false,
11730     /**
11731      * This method is only used by a DataProxy which has retrieved data from a remote server.
11732      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11733      * @return {Object} data A data block which is used by an Roo.data.Store object as
11734      * a cache of Roo.data.Records.
11735      */
11736     read : function(response){
11737         var json = response.responseText;
11738        
11739         var o = /* eval:var:o */ eval("("+json+")");
11740         if(!o) {
11741             throw {message: "JsonReader.read: Json object not found"};
11742         }
11743         
11744         if(o.metaData){
11745             
11746             delete this.ef;
11747             this.metaFromRemote = true;
11748             this.meta = o.metaData;
11749             this.recordType = Roo.data.Record.create(o.metaData.fields);
11750             this.onMetaChange(this.meta, this.recordType, o);
11751         }
11752         return this.readRecords(o);
11753     },
11754
11755     // private function a store will implement
11756     onMetaChange : function(meta, recordType, o){
11757
11758     },
11759
11760     /**
11761          * @ignore
11762          */
11763     simpleAccess: function(obj, subsc) {
11764         return obj[subsc];
11765     },
11766
11767         /**
11768          * @ignore
11769          */
11770     getJsonAccessor: function(){
11771         var re = /[\[\.]/;
11772         return function(expr) {
11773             try {
11774                 return(re.test(expr))
11775                     ? new Function("obj", "return obj." + expr)
11776                     : function(obj){
11777                         return obj[expr];
11778                     };
11779             } catch(e){}
11780             return Roo.emptyFn;
11781         };
11782     }(),
11783
11784     /**
11785      * Create a data block containing Roo.data.Records from an XML document.
11786      * @param {Object} o An object which contains an Array of row objects in the property specified
11787      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11788      * which contains the total size of the dataset.
11789      * @return {Object} data A data block which is used by an Roo.data.Store object as
11790      * a cache of Roo.data.Records.
11791      */
11792     readRecords : function(o){
11793         /**
11794          * After any data loads, the raw JSON data is available for further custom processing.
11795          * @type Object
11796          */
11797         this.o = o;
11798         var s = this.meta, Record = this.recordType,
11799             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11800
11801 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11802         if (!this.ef) {
11803             if(s.totalProperty) {
11804                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11805                 }
11806                 if(s.successProperty) {
11807                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11808                 }
11809                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11810                 if (s.id) {
11811                         var g = this.getJsonAccessor(s.id);
11812                         this.getId = function(rec) {
11813                                 var r = g(rec);  
11814                                 return (r === undefined || r === "") ? null : r;
11815                         };
11816                 } else {
11817                         this.getId = function(){return null;};
11818                 }
11819             this.ef = [];
11820             for(var jj = 0; jj < fl; jj++){
11821                 f = fi[jj];
11822                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11823                 this.ef[jj] = this.getJsonAccessor(map);
11824             }
11825         }
11826
11827         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11828         if(s.totalProperty){
11829             var vt = parseInt(this.getTotal(o), 10);
11830             if(!isNaN(vt)){
11831                 totalRecords = vt;
11832             }
11833         }
11834         if(s.successProperty){
11835             var vs = this.getSuccess(o);
11836             if(vs === false || vs === 'false'){
11837                 success = false;
11838             }
11839         }
11840         var records = [];
11841         for(var i = 0; i < c; i++){
11842                 var n = root[i];
11843             var values = {};
11844             var id = this.getId(n);
11845             for(var j = 0; j < fl; j++){
11846                 f = fi[j];
11847             var v = this.ef[j](n);
11848             if (!f.convert) {
11849                 Roo.log('missing convert for ' + f.name);
11850                 Roo.log(f);
11851                 continue;
11852             }
11853             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11854             }
11855             var record = new Record(values, id);
11856             record.json = n;
11857             records[i] = record;
11858         }
11859         return {
11860             raw : o,
11861             success : success,
11862             records : records,
11863             totalRecords : totalRecords
11864         };
11865     }
11866 });/*
11867  * Based on:
11868  * Ext JS Library 1.1.1
11869  * Copyright(c) 2006-2007, Ext JS, LLC.
11870  *
11871  * Originally Released Under LGPL - original licence link has changed is not relivant.
11872  *
11873  * Fork - LGPL
11874  * <script type="text/javascript">
11875  */
11876
11877 /**
11878  * @class Roo.data.ArrayReader
11879  * @extends Roo.data.DataReader
11880  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11881  * Each element of that Array represents a row of data fields. The
11882  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11883  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11884  * <p>
11885  * Example code:.
11886  * <pre><code>
11887 var RecordDef = Roo.data.Record.create([
11888     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11889     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11890 ]);
11891 var myReader = new Roo.data.ArrayReader({
11892     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11893 }, RecordDef);
11894 </code></pre>
11895  * <p>
11896  * This would consume an Array like this:
11897  * <pre><code>
11898 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11899   </code></pre>
11900  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11901  * @constructor
11902  * Create a new JsonReader
11903  * @param {Object} meta Metadata configuration options.
11904  * @param {Object} recordType Either an Array of field definition objects
11905  * as specified to {@link Roo.data.Record#create},
11906  * or an {@link Roo.data.Record} object
11907  * created using {@link Roo.data.Record#create}.
11908  */
11909 Roo.data.ArrayReader = function(meta, recordType){
11910     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11911 };
11912
11913 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11914     /**
11915      * Create a data block containing Roo.data.Records from an XML document.
11916      * @param {Object} o An Array of row objects which represents the dataset.
11917      * @return {Object} data A data block which is used by an Roo.data.Store object as
11918      * a cache of Roo.data.Records.
11919      */
11920     readRecords : function(o){
11921         var sid = this.meta ? this.meta.id : null;
11922         var recordType = this.recordType, fields = recordType.prototype.fields;
11923         var records = [];
11924         var root = o;
11925             for(var i = 0; i < root.length; i++){
11926                     var n = root[i];
11927                 var values = {};
11928                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11929                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11930                 var f = fields.items[j];
11931                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11932                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11933                 v = f.convert(v);
11934                 values[f.name] = v;
11935             }
11936                 var record = new recordType(values, id);
11937                 record.json = n;
11938                 records[records.length] = record;
11939             }
11940             return {
11941                 records : records,
11942                 totalRecords : records.length
11943             };
11944     }
11945 });/*
11946  * - LGPL
11947  * * 
11948  */
11949
11950 /**
11951  * @class Roo.bootstrap.ComboBox
11952  * @extends Roo.bootstrap.TriggerField
11953  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11954  * @cfg {Boolean} append (true|false) default false
11955  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11956  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11957  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11958  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11959  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11960  * @cfg {Boolean} animate default true
11961  * @cfg {Boolean} emptyResultText only for touch device
11962  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11963  * @constructor
11964  * Create a new ComboBox.
11965  * @param {Object} config Configuration options
11966  */
11967 Roo.bootstrap.ComboBox = function(config){
11968     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11969     this.addEvents({
11970         /**
11971          * @event expand
11972          * Fires when the dropdown list is expanded
11973              * @param {Roo.bootstrap.ComboBox} combo This combo box
11974              */
11975         'expand' : true,
11976         /**
11977          * @event collapse
11978          * Fires when the dropdown list is collapsed
11979              * @param {Roo.bootstrap.ComboBox} combo This combo box
11980              */
11981         'collapse' : true,
11982         /**
11983          * @event beforeselect
11984          * Fires before a list item is selected. Return false to cancel the selection.
11985              * @param {Roo.bootstrap.ComboBox} combo This combo box
11986              * @param {Roo.data.Record} record The data record returned from the underlying store
11987              * @param {Number} index The index of the selected item in the dropdown list
11988              */
11989         'beforeselect' : true,
11990         /**
11991          * @event select
11992          * Fires when a list item is selected
11993              * @param {Roo.bootstrap.ComboBox} combo This combo box
11994              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11995              * @param {Number} index The index of the selected item in the dropdown list
11996              */
11997         'select' : true,
11998         /**
11999          * @event beforequery
12000          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12001          * The event object passed has these properties:
12002              * @param {Roo.bootstrap.ComboBox} combo This combo box
12003              * @param {String} query The query
12004              * @param {Boolean} forceAll true to force "all" query
12005              * @param {Boolean} cancel true to cancel the query
12006              * @param {Object} e The query event object
12007              */
12008         'beforequery': true,
12009          /**
12010          * @event add
12011          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12012              * @param {Roo.bootstrap.ComboBox} combo This combo box
12013              */
12014         'add' : true,
12015         /**
12016          * @event edit
12017          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12018              * @param {Roo.bootstrap.ComboBox} combo This combo box
12019              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12020              */
12021         'edit' : true,
12022         /**
12023          * @event remove
12024          * Fires when the remove value from the combobox array
12025              * @param {Roo.bootstrap.ComboBox} combo This combo box
12026              */
12027         'remove' : true,
12028         /**
12029          * @event afterremove
12030          * Fires when the remove value from the combobox array
12031              * @param {Roo.bootstrap.ComboBox} combo This combo box
12032              */
12033         'afterremove' : true,
12034         /**
12035          * @event specialfilter
12036          * Fires when specialfilter
12037             * @param {Roo.bootstrap.ComboBox} combo This combo box
12038             */
12039         'specialfilter' : true,
12040         /**
12041          * @event tick
12042          * Fires when tick the element
12043             * @param {Roo.bootstrap.ComboBox} combo This combo box
12044             */
12045         'tick' : true,
12046         /**
12047          * @event touchviewdisplay
12048          * Fires when touch view require special display (default is using displayField)
12049             * @param {Roo.bootstrap.ComboBox} combo This combo box
12050             * @param {Object} cfg set html .
12051             */
12052         'touchviewdisplay' : true
12053         
12054     });
12055     
12056     this.item = [];
12057     this.tickItems = [];
12058     
12059     this.selectedIndex = -1;
12060     if(this.mode == 'local'){
12061         if(config.queryDelay === undefined){
12062             this.queryDelay = 10;
12063         }
12064         if(config.minChars === undefined){
12065             this.minChars = 0;
12066         }
12067     }
12068 };
12069
12070 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12071      
12072     /**
12073      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12074      * rendering into an Roo.Editor, defaults to false)
12075      */
12076     /**
12077      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12078      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12079      */
12080     /**
12081      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12082      */
12083     /**
12084      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12085      * the dropdown list (defaults to undefined, with no header element)
12086      */
12087
12088      /**
12089      * @cfg {String/Roo.Template} tpl The template to use to render the output
12090      */
12091      
12092      /**
12093      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12094      */
12095     listWidth: undefined,
12096     /**
12097      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12098      * mode = 'remote' or 'text' if mode = 'local')
12099      */
12100     displayField: undefined,
12101     
12102     /**
12103      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12104      * mode = 'remote' or 'value' if mode = 'local'). 
12105      * Note: use of a valueField requires the user make a selection
12106      * in order for a value to be mapped.
12107      */
12108     valueField: undefined,
12109     /**
12110      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12111      */
12112     modalTitle : '',
12113     
12114     /**
12115      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12116      * field's data value (defaults to the underlying DOM element's name)
12117      */
12118     hiddenName: undefined,
12119     /**
12120      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12121      */
12122     listClass: '',
12123     /**
12124      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12125      */
12126     selectedClass: 'active',
12127     
12128     /**
12129      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12130      */
12131     shadow:'sides',
12132     /**
12133      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12134      * anchor positions (defaults to 'tl-bl')
12135      */
12136     listAlign: 'tl-bl?',
12137     /**
12138      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12139      */
12140     maxHeight: 300,
12141     /**
12142      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12143      * query specified by the allQuery config option (defaults to 'query')
12144      */
12145     triggerAction: 'query',
12146     /**
12147      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12148      * (defaults to 4, does not apply if editable = false)
12149      */
12150     minChars : 4,
12151     /**
12152      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12153      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12154      */
12155     typeAhead: false,
12156     /**
12157      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12158      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12159      */
12160     queryDelay: 500,
12161     /**
12162      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12163      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12164      */
12165     pageSize: 0,
12166     /**
12167      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12168      * when editable = true (defaults to false)
12169      */
12170     selectOnFocus:false,
12171     /**
12172      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12173      */
12174     queryParam: 'query',
12175     /**
12176      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12177      * when mode = 'remote' (defaults to 'Loading...')
12178      */
12179     loadingText: 'Loading...',
12180     /**
12181      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12182      */
12183     resizable: false,
12184     /**
12185      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12186      */
12187     handleHeight : 8,
12188     /**
12189      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12190      * traditional select (defaults to true)
12191      */
12192     editable: true,
12193     /**
12194      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12195      */
12196     allQuery: '',
12197     /**
12198      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12199      */
12200     mode: 'remote',
12201     /**
12202      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12203      * listWidth has a higher value)
12204      */
12205     minListWidth : 70,
12206     /**
12207      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12208      * allow the user to set arbitrary text into the field (defaults to false)
12209      */
12210     forceSelection:false,
12211     /**
12212      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12213      * if typeAhead = true (defaults to 250)
12214      */
12215     typeAheadDelay : 250,
12216     /**
12217      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12218      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12219      */
12220     valueNotFoundText : undefined,
12221     /**
12222      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12223      */
12224     blockFocus : false,
12225     
12226     /**
12227      * @cfg {Boolean} disableClear Disable showing of clear button.
12228      */
12229     disableClear : false,
12230     /**
12231      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12232      */
12233     alwaysQuery : false,
12234     
12235     /**
12236      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12237      */
12238     multiple : false,
12239     
12240     /**
12241      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12242      */
12243     invalidClass : "has-warning",
12244     
12245     /**
12246      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12247      */
12248     validClass : "has-success",
12249     
12250     /**
12251      * @cfg {Boolean} specialFilter (true|false) special filter default false
12252      */
12253     specialFilter : false,
12254     
12255     /**
12256      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12257      */
12258     mobileTouchView : true,
12259     
12260     /**
12261      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12262      */
12263     useNativeIOS : false,
12264     
12265     ios_options : false,
12266     
12267     //private
12268     addicon : false,
12269     editicon: false,
12270     
12271     page: 0,
12272     hasQuery: false,
12273     append: false,
12274     loadNext: false,
12275     autoFocus : true,
12276     tickable : false,
12277     btnPosition : 'right',
12278     triggerList : true,
12279     showToggleBtn : true,
12280     animate : true,
12281     emptyResultText: 'Empty',
12282     triggerText : 'Select',
12283     
12284     // element that contains real text value.. (when hidden is used..)
12285     
12286     getAutoCreate : function()
12287     {
12288         var cfg = false;
12289         
12290         /*
12291          * Render classic select for iso
12292          */
12293         
12294         if(Roo.isIOS && this.useNativeIOS){
12295             cfg = this.getAutoCreateNativeIOS();
12296             return cfg;
12297         }
12298         
12299         /*
12300          * Touch Devices
12301          */
12302         
12303         if(Roo.isTouch && this.mobileTouchView){
12304             cfg = this.getAutoCreateTouchView();
12305             return cfg;;
12306         }
12307         
12308         /*
12309          *  Normal ComboBox
12310          */
12311         if(!this.tickable){
12312             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12313             return cfg;
12314         }
12315         
12316         /*
12317          *  ComboBox with tickable selections
12318          */
12319              
12320         var align = this.labelAlign || this.parentLabelAlign();
12321         
12322         cfg = {
12323             cls : 'form-group roo-combobox-tickable' //input-group
12324         };
12325         
12326         var buttons = {
12327             tag : 'div',
12328             cls : 'tickable-buttons',
12329             cn : [
12330                 {
12331                     tag : 'button',
12332                     type : 'button',
12333                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12334                     html : this.triggerText
12335                 },
12336                 {
12337                     tag : 'button',
12338                     type : 'button',
12339                     name : 'ok',
12340                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12341                     html : 'Done'
12342                 },
12343                 {
12344                     tag : 'button',
12345                     type : 'button',
12346                     name : 'cancel',
12347                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12348                     html : 'Cancel'
12349                 }
12350             ]
12351         };
12352         
12353         if(this.editable){
12354             buttons.cn.unshift({
12355                 tag: 'input',
12356                 cls: 'roo-select2-search-field-input'
12357             });
12358         }
12359         
12360         var _this = this;
12361         
12362         Roo.each(buttons.cn, function(c){
12363             if (_this.size) {
12364                 c.cls += ' btn-' + _this.size;
12365             }
12366
12367             if (_this.disabled) {
12368                 c.disabled = true;
12369             }
12370         });
12371         
12372         var box = {
12373             tag: 'div',
12374             cn: [
12375                 {
12376                     tag: 'input',
12377                     type : 'hidden',
12378                     cls: 'form-hidden-field'
12379                 },
12380                 {
12381                     tag: 'ul',
12382                     cls: 'roo-select2-choices',
12383                     cn:[
12384                         {
12385                             tag: 'li',
12386                             cls: 'roo-select2-search-field',
12387                             cn: [
12388
12389                                 buttons
12390                             ]
12391                         }
12392                     ]
12393                 }
12394             ]
12395         };
12396         
12397         var combobox = {
12398             cls: 'roo-select2-container input-group roo-select2-container-multi',
12399             cn: [
12400                 box
12401 //                {
12402 //                    tag: 'ul',
12403 //                    cls: 'typeahead typeahead-long dropdown-menu',
12404 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12405 //                }
12406             ]
12407         };
12408         
12409         if(this.hasFeedback && !this.allowBlank){
12410             
12411             var feedback = {
12412                 tag: 'span',
12413                 cls: 'glyphicon form-control-feedback'
12414             };
12415
12416             combobox.cn.push(feedback);
12417         }
12418         
12419         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12420             
12421 //                Roo.log("left and has label");
12422             cfg.cn = [
12423                 {
12424                     tag : 'i',
12425                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12426                     tooltip : 'This field is required'
12427                 },
12428                 {
12429                     tag: 'label',
12430                     'for' :  id,
12431                     cls : 'control-label col-sm-' + this.labelWidth,
12432                     html : this.fieldLabel
12433
12434                 },
12435                 {
12436                     cls : "col-sm-" + (12 - this.labelWidth), 
12437                     cn: [
12438                         combobox
12439                     ]
12440                 }
12441
12442             ];
12443
12444             if(this.indicatorpos == 'right'){
12445                 
12446                 cfg.cn = [
12447                     {
12448                         tag: 'label',
12449                         'for' :  id,
12450                         cls : 'control-label col-sm-' + this.labelWidth,
12451                         html : this.fieldLabel
12452
12453                     },
12454                     {
12455                         tag : 'i',
12456                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12457                         tooltip : 'This field is required'
12458                     },
12459                     {
12460                         cls : "col-sm-" + (12 - this.labelWidth), 
12461                         cn: [
12462                             combobox
12463                         ]
12464                     }
12465
12466                 ];
12467             
12468             }
12469                 
12470                 
12471         } else if ( this.fieldLabel.length) {
12472 //                Roo.log(" 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                         //cls : 'input-group-addon',
12482                         html : this.fieldLabel
12483                         
12484                     },
12485                     
12486                     combobox
12487                     
12488                 ];
12489                 
12490                 if(this.indicatorpos == 'right'){
12491                     
12492                     cfg.cn = [
12493                         {
12494                             tag: 'label',
12495                             //cls : 'input-group-addon',
12496                             html : this.fieldLabel
12497
12498                         },
12499                         
12500                         {
12501                             tag : 'i',
12502                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12503                             tooltip : 'This field is required'
12504                         },
12505                         
12506                         combobox
12507
12508                     ];
12509                 
12510                 }
12511
12512         } else {
12513             
12514 //                Roo.log(" no label && no align");
12515                 cfg = combobox
12516                      
12517                 
12518         }
12519          
12520         var settings=this;
12521         ['xs','sm','md','lg'].map(function(size){
12522             if (settings[size]) {
12523                 cfg.cls += ' col-' + size + '-' + settings[size];
12524             }
12525         });
12526         
12527         return cfg;
12528         
12529     },
12530     
12531     _initEventsCalled : false,
12532     
12533     // private
12534     initEvents: function()
12535     {   
12536         if (this._initEventsCalled) { // as we call render... prevent looping...
12537             return;
12538         }
12539         this._initEventsCalled = true;
12540         
12541         if (!this.store) {
12542             throw "can not find store for combo";
12543         }
12544         
12545         this.store = Roo.factory(this.store, Roo.data);
12546         
12547         // if we are building from html. then this element is so complex, that we can not really
12548         // use the rendered HTML.
12549         // so we have to trash and replace the previous code.
12550         if (Roo.XComponent.build_from_html) {
12551             
12552             // remove this element....
12553             var e = this.el.dom, k=0;
12554             while (e ) { e = e.previousSibling;  ++k;}
12555
12556             this.el.remove();
12557             
12558             this.el=false;
12559             this.rendered = false;
12560             
12561             this.render(this.parent().getChildContainer(true), k);
12562             
12563             
12564             
12565         }
12566         
12567         if(Roo.isIOS && this.useNativeIOS){
12568             this.initIOSView();
12569             return;
12570         }
12571         
12572         /*
12573          * Touch Devices
12574          */
12575         
12576         if(Roo.isTouch && this.mobileTouchView){
12577             this.initTouchView();
12578             return;
12579         }
12580         
12581         if(this.tickable){
12582             this.initTickableEvents();
12583             return;
12584         }
12585         
12586         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12587         
12588         if(this.hiddenName){
12589             
12590             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12591             
12592             this.hiddenField.dom.value =
12593                 this.hiddenValue !== undefined ? this.hiddenValue :
12594                 this.value !== undefined ? this.value : '';
12595
12596             // prevent input submission
12597             this.el.dom.removeAttribute('name');
12598             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12599              
12600              
12601         }
12602         //if(Roo.isGecko){
12603         //    this.el.dom.setAttribute('autocomplete', 'off');
12604         //}
12605         
12606         var cls = 'x-combo-list';
12607         
12608         //this.list = new Roo.Layer({
12609         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12610         //});
12611         
12612         var _this = this;
12613         
12614         (function(){
12615             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12616             _this.list.setWidth(lw);
12617         }).defer(100);
12618         
12619         this.list.on('mouseover', this.onViewOver, this);
12620         this.list.on('mousemove', this.onViewMove, this);
12621         
12622         this.list.on('scroll', this.onViewScroll, this);
12623         
12624         /*
12625         this.list.swallowEvent('mousewheel');
12626         this.assetHeight = 0;
12627
12628         if(this.title){
12629             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12630             this.assetHeight += this.header.getHeight();
12631         }
12632
12633         this.innerList = this.list.createChild({cls:cls+'-inner'});
12634         this.innerList.on('mouseover', this.onViewOver, this);
12635         this.innerList.on('mousemove', this.onViewMove, this);
12636         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12637         
12638         if(this.allowBlank && !this.pageSize && !this.disableClear){
12639             this.footer = this.list.createChild({cls:cls+'-ft'});
12640             this.pageTb = new Roo.Toolbar(this.footer);
12641            
12642         }
12643         if(this.pageSize){
12644             this.footer = this.list.createChild({cls:cls+'-ft'});
12645             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12646                     {pageSize: this.pageSize});
12647             
12648         }
12649         
12650         if (this.pageTb && this.allowBlank && !this.disableClear) {
12651             var _this = this;
12652             this.pageTb.add(new Roo.Toolbar.Fill(), {
12653                 cls: 'x-btn-icon x-btn-clear',
12654                 text: '&#160;',
12655                 handler: function()
12656                 {
12657                     _this.collapse();
12658                     _this.clearValue();
12659                     _this.onSelect(false, -1);
12660                 }
12661             });
12662         }
12663         if (this.footer) {
12664             this.assetHeight += this.footer.getHeight();
12665         }
12666         */
12667             
12668         if(!this.tpl){
12669             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12670         }
12671
12672         this.view = new Roo.View(this.list, this.tpl, {
12673             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12674         });
12675         //this.view.wrapEl.setDisplayed(false);
12676         this.view.on('click', this.onViewClick, this);
12677         
12678         
12679         
12680         this.store.on('beforeload', this.onBeforeLoad, this);
12681         this.store.on('load', this.onLoad, this);
12682         this.store.on('loadexception', this.onLoadException, this);
12683         /*
12684         if(this.resizable){
12685             this.resizer = new Roo.Resizable(this.list,  {
12686                pinned:true, handles:'se'
12687             });
12688             this.resizer.on('resize', function(r, w, h){
12689                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12690                 this.listWidth = w;
12691                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12692                 this.restrictHeight();
12693             }, this);
12694             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12695         }
12696         */
12697         if(!this.editable){
12698             this.editable = true;
12699             this.setEditable(false);
12700         }
12701         
12702         /*
12703         
12704         if (typeof(this.events.add.listeners) != 'undefined') {
12705             
12706             this.addicon = this.wrap.createChild(
12707                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12708        
12709             this.addicon.on('click', function(e) {
12710                 this.fireEvent('add', this);
12711             }, this);
12712         }
12713         if (typeof(this.events.edit.listeners) != 'undefined') {
12714             
12715             this.editicon = this.wrap.createChild(
12716                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12717             if (this.addicon) {
12718                 this.editicon.setStyle('margin-left', '40px');
12719             }
12720             this.editicon.on('click', function(e) {
12721                 
12722                 // we fire even  if inothing is selected..
12723                 this.fireEvent('edit', this, this.lastData );
12724                 
12725             }, this);
12726         }
12727         */
12728         
12729         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12730             "up" : function(e){
12731                 this.inKeyMode = true;
12732                 this.selectPrev();
12733             },
12734
12735             "down" : function(e){
12736                 if(!this.isExpanded()){
12737                     this.onTriggerClick();
12738                 }else{
12739                     this.inKeyMode = true;
12740                     this.selectNext();
12741                 }
12742             },
12743
12744             "enter" : function(e){
12745 //                this.onViewClick();
12746                 //return true;
12747                 this.collapse();
12748                 
12749                 if(this.fireEvent("specialkey", this, e)){
12750                     this.onViewClick(false);
12751                 }
12752                 
12753                 return true;
12754             },
12755
12756             "esc" : function(e){
12757                 this.collapse();
12758             },
12759
12760             "tab" : function(e){
12761                 this.collapse();
12762                 
12763                 if(this.fireEvent("specialkey", this, e)){
12764                     this.onViewClick(false);
12765                 }
12766                 
12767                 return true;
12768             },
12769
12770             scope : this,
12771
12772             doRelay : function(foo, bar, hname){
12773                 if(hname == 'down' || this.scope.isExpanded()){
12774                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12775                 }
12776                 return true;
12777             },
12778
12779             forceKeyDown: true
12780         });
12781         
12782         
12783         this.queryDelay = Math.max(this.queryDelay || 10,
12784                 this.mode == 'local' ? 10 : 250);
12785         
12786         
12787         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12788         
12789         if(this.typeAhead){
12790             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12791         }
12792         if(this.editable !== false){
12793             this.inputEl().on("keyup", this.onKeyUp, this);
12794         }
12795         if(this.forceSelection){
12796             this.inputEl().on('blur', this.doForce, this);
12797         }
12798         
12799         if(this.multiple){
12800             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12801             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12802         }
12803     },
12804     
12805     initTickableEvents: function()
12806     {   
12807         this.createList();
12808         
12809         if(this.hiddenName){
12810             
12811             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12812             
12813             this.hiddenField.dom.value =
12814                 this.hiddenValue !== undefined ? this.hiddenValue :
12815                 this.value !== undefined ? this.value : '';
12816
12817             // prevent input submission
12818             this.el.dom.removeAttribute('name');
12819             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12820              
12821              
12822         }
12823         
12824 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12825         
12826         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12827         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12828         if(this.triggerList){
12829             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12830         }
12831          
12832         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12833         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12834         
12835         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12836         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12837         
12838         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12839         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12840         
12841         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12842         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12843         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12844         
12845         this.okBtn.hide();
12846         this.cancelBtn.hide();
12847         
12848         var _this = this;
12849         
12850         (function(){
12851             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12852             _this.list.setWidth(lw);
12853         }).defer(100);
12854         
12855         this.list.on('mouseover', this.onViewOver, this);
12856         this.list.on('mousemove', this.onViewMove, this);
12857         
12858         this.list.on('scroll', this.onViewScroll, this);
12859         
12860         if(!this.tpl){
12861             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>';
12862         }
12863
12864         this.view = new Roo.View(this.list, this.tpl, {
12865             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12866         });
12867         
12868         //this.view.wrapEl.setDisplayed(false);
12869         this.view.on('click', this.onViewClick, this);
12870         
12871         
12872         
12873         this.store.on('beforeload', this.onBeforeLoad, this);
12874         this.store.on('load', this.onLoad, this);
12875         this.store.on('loadexception', this.onLoadException, this);
12876         
12877         if(this.editable){
12878             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12879                 "up" : function(e){
12880                     this.inKeyMode = true;
12881                     this.selectPrev();
12882                 },
12883
12884                 "down" : function(e){
12885                     this.inKeyMode = true;
12886                     this.selectNext();
12887                 },
12888
12889                 "enter" : function(e){
12890                     if(this.fireEvent("specialkey", this, e)){
12891                         this.onViewClick(false);
12892                     }
12893                     
12894                     return true;
12895                 },
12896
12897                 "esc" : function(e){
12898                     this.onTickableFooterButtonClick(e, false, false);
12899                 },
12900
12901                 "tab" : function(e){
12902                     this.fireEvent("specialkey", this, e);
12903                     
12904                     this.onTickableFooterButtonClick(e, false, false);
12905                     
12906                     return true;
12907                 },
12908
12909                 scope : this,
12910
12911                 doRelay : function(e, fn, key){
12912                     if(this.scope.isExpanded()){
12913                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12914                     }
12915                     return true;
12916                 },
12917
12918                 forceKeyDown: true
12919             });
12920         }
12921         
12922         this.queryDelay = Math.max(this.queryDelay || 10,
12923                 this.mode == 'local' ? 10 : 250);
12924         
12925         
12926         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12927         
12928         if(this.typeAhead){
12929             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12930         }
12931         
12932         if(this.editable !== false){
12933             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12934         }
12935         
12936     },
12937
12938     onDestroy : function(){
12939         if(this.view){
12940             this.view.setStore(null);
12941             this.view.el.removeAllListeners();
12942             this.view.el.remove();
12943             this.view.purgeListeners();
12944         }
12945         if(this.list){
12946             this.list.dom.innerHTML  = '';
12947         }
12948         
12949         if(this.store){
12950             this.store.un('beforeload', this.onBeforeLoad, this);
12951             this.store.un('load', this.onLoad, this);
12952             this.store.un('loadexception', this.onLoadException, this);
12953         }
12954         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12955     },
12956
12957     // private
12958     fireKey : function(e){
12959         if(e.isNavKeyPress() && !this.list.isVisible()){
12960             this.fireEvent("specialkey", this, e);
12961         }
12962     },
12963
12964     // private
12965     onResize: function(w, h){
12966 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12967 //        
12968 //        if(typeof w != 'number'){
12969 //            // we do not handle it!?!?
12970 //            return;
12971 //        }
12972 //        var tw = this.trigger.getWidth();
12973 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12974 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12975 //        var x = w - tw;
12976 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12977 //            
12978 //        //this.trigger.setStyle('left', x+'px');
12979 //        
12980 //        if(this.list && this.listWidth === undefined){
12981 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12982 //            this.list.setWidth(lw);
12983 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12984 //        }
12985         
12986     
12987         
12988     },
12989
12990     /**
12991      * Allow or prevent the user from directly editing the field text.  If false is passed,
12992      * the user will only be able to select from the items defined in the dropdown list.  This method
12993      * is the runtime equivalent of setting the 'editable' config option at config time.
12994      * @param {Boolean} value True to allow the user to directly edit the field text
12995      */
12996     setEditable : function(value){
12997         if(value == this.editable){
12998             return;
12999         }
13000         this.editable = value;
13001         if(!value){
13002             this.inputEl().dom.setAttribute('readOnly', true);
13003             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13004             this.inputEl().addClass('x-combo-noedit');
13005         }else{
13006             this.inputEl().dom.setAttribute('readOnly', false);
13007             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13008             this.inputEl().removeClass('x-combo-noedit');
13009         }
13010     },
13011
13012     // private
13013     
13014     onBeforeLoad : function(combo,opts){
13015         if(!this.hasFocus){
13016             return;
13017         }
13018          if (!opts.add) {
13019             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13020          }
13021         this.restrictHeight();
13022         this.selectedIndex = -1;
13023     },
13024
13025     // private
13026     onLoad : function(){
13027         
13028         this.hasQuery = false;
13029         
13030         if(!this.hasFocus){
13031             return;
13032         }
13033         
13034         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13035             this.loading.hide();
13036         }
13037              
13038         if(this.store.getCount() > 0){
13039             this.expand();
13040             this.restrictHeight();
13041             if(this.lastQuery == this.allQuery){
13042                 if(this.editable && !this.tickable){
13043                     this.inputEl().dom.select();
13044                 }
13045                 
13046                 if(
13047                     !this.selectByValue(this.value, true) &&
13048                     this.autoFocus && 
13049                     (
13050                         !this.store.lastOptions ||
13051                         typeof(this.store.lastOptions.add) == 'undefined' || 
13052                         this.store.lastOptions.add != true
13053                     )
13054                 ){
13055                     this.select(0, true);
13056                 }
13057             }else{
13058                 if(this.autoFocus){
13059                     this.selectNext();
13060                 }
13061                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13062                     this.taTask.delay(this.typeAheadDelay);
13063                 }
13064             }
13065         }else{
13066             this.onEmptyResults();
13067         }
13068         
13069         //this.el.focus();
13070     },
13071     // private
13072     onLoadException : function()
13073     {
13074         this.hasQuery = false;
13075         
13076         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13077             this.loading.hide();
13078         }
13079         
13080         if(this.tickable && this.editable){
13081             return;
13082         }
13083         
13084         this.collapse();
13085         // only causes errors at present
13086         //Roo.log(this.store.reader.jsonData);
13087         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13088             // fixme
13089             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13090         //}
13091         
13092         
13093     },
13094     // private
13095     onTypeAhead : function(){
13096         if(this.store.getCount() > 0){
13097             var r = this.store.getAt(0);
13098             var newValue = r.data[this.displayField];
13099             var len = newValue.length;
13100             var selStart = this.getRawValue().length;
13101             
13102             if(selStart != len){
13103                 this.setRawValue(newValue);
13104                 this.selectText(selStart, newValue.length);
13105             }
13106         }
13107     },
13108
13109     // private
13110     onSelect : function(record, index){
13111         
13112         if(this.fireEvent('beforeselect', this, record, index) !== false){
13113         
13114             this.setFromData(index > -1 ? record.data : false);
13115             
13116             this.collapse();
13117             this.fireEvent('select', this, record, index);
13118         }
13119     },
13120
13121     /**
13122      * Returns the currently selected field value or empty string if no value is set.
13123      * @return {String} value The selected value
13124      */
13125     getValue : function()
13126     {
13127         if(Roo.isIOS && this.useNativeIOS){
13128             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13129         }
13130         
13131         if(this.multiple){
13132             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13133         }
13134         
13135         if(this.valueField){
13136             return typeof this.value != 'undefined' ? this.value : '';
13137         }else{
13138             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13139         }
13140     },
13141     
13142     getRawValue : function()
13143     {
13144         if(Roo.isIOS && this.useNativeIOS){
13145             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13146         }
13147         
13148         var v = this.inputEl().getValue();
13149         
13150         return v;
13151     },
13152
13153     /**
13154      * Clears any text/value currently set in the field
13155      */
13156     clearValue : function(){
13157         
13158         if(this.hiddenField){
13159             this.hiddenField.dom.value = '';
13160         }
13161         this.value = '';
13162         this.setRawValue('');
13163         this.lastSelectionText = '';
13164         this.lastData = false;
13165         
13166         var close = this.closeTriggerEl();
13167         
13168         if(close){
13169             close.hide();
13170         }
13171         
13172         this.validate();
13173         
13174     },
13175
13176     /**
13177      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13178      * will be displayed in the field.  If the value does not match the data value of an existing item,
13179      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13180      * Otherwise the field will be blank (although the value will still be set).
13181      * @param {String} value The value to match
13182      */
13183     setValue : function(v)
13184     {
13185         if(Roo.isIOS && this.useNativeIOS){
13186             this.setIOSValue(v);
13187             return;
13188         }
13189         
13190         if(this.multiple){
13191             this.syncValue();
13192             return;
13193         }
13194         
13195         var text = v;
13196         if(this.valueField){
13197             var r = this.findRecord(this.valueField, v);
13198             if(r){
13199                 text = r.data[this.displayField];
13200             }else if(this.valueNotFoundText !== undefined){
13201                 text = this.valueNotFoundText;
13202             }
13203         }
13204         this.lastSelectionText = text;
13205         if(this.hiddenField){
13206             this.hiddenField.dom.value = v;
13207         }
13208         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13209         this.value = v;
13210         
13211         var close = this.closeTriggerEl();
13212         
13213         if(close){
13214             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13215         }
13216         
13217         this.validate();
13218     },
13219     /**
13220      * @property {Object} the last set data for the element
13221      */
13222     
13223     lastData : false,
13224     /**
13225      * Sets the value of the field based on a object which is related to the record format for the store.
13226      * @param {Object} value the value to set as. or false on reset?
13227      */
13228     setFromData : function(o){
13229         
13230         if(this.multiple){
13231             this.addItem(o);
13232             return;
13233         }
13234             
13235         var dv = ''; // display value
13236         var vv = ''; // value value..
13237         this.lastData = o;
13238         if (this.displayField) {
13239             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13240         } else {
13241             // this is an error condition!!!
13242             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13243         }
13244         
13245         if(this.valueField){
13246             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13247         }
13248         
13249         var close = this.closeTriggerEl();
13250         
13251         if(close){
13252             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13253         }
13254         
13255         if(this.hiddenField){
13256             this.hiddenField.dom.value = vv;
13257             
13258             this.lastSelectionText = dv;
13259             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13260             this.value = vv;
13261             return;
13262         }
13263         // no hidden field.. - we store the value in 'value', but still display
13264         // display field!!!!
13265         this.lastSelectionText = dv;
13266         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13267         this.value = vv;
13268         
13269         
13270         
13271     },
13272     // private
13273     reset : function(){
13274         // overridden so that last data is reset..
13275         
13276         if(this.multiple){
13277             this.clearItem();
13278             return;
13279         }
13280         
13281         this.setValue(this.originalValue);
13282         //this.clearInvalid();
13283         this.lastData = false;
13284         if (this.view) {
13285             this.view.clearSelections();
13286         }
13287         
13288         this.validate();
13289     },
13290     // private
13291     findRecord : function(prop, value){
13292         var record;
13293         if(this.store.getCount() > 0){
13294             this.store.each(function(r){
13295                 if(r.data[prop] == value){
13296                     record = r;
13297                     return false;
13298                 }
13299                 return true;
13300             });
13301         }
13302         return record;
13303     },
13304     
13305     getName: function()
13306     {
13307         // returns hidden if it's set..
13308         if (!this.rendered) {return ''};
13309         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13310         
13311     },
13312     // private
13313     onViewMove : function(e, t){
13314         this.inKeyMode = false;
13315     },
13316
13317     // private
13318     onViewOver : function(e, t){
13319         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13320             return;
13321         }
13322         var item = this.view.findItemFromChild(t);
13323         
13324         if(item){
13325             var index = this.view.indexOf(item);
13326             this.select(index, false);
13327         }
13328     },
13329
13330     // private
13331     onViewClick : function(view, doFocus, el, e)
13332     {
13333         var index = this.view.getSelectedIndexes()[0];
13334         
13335         var r = this.store.getAt(index);
13336         
13337         if(this.tickable){
13338             
13339             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13340                 return;
13341             }
13342             
13343             var rm = false;
13344             var _this = this;
13345             
13346             Roo.each(this.tickItems, function(v,k){
13347                 
13348                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13349                     Roo.log(v);
13350                     _this.tickItems.splice(k, 1);
13351                     
13352                     if(typeof(e) == 'undefined' && view == false){
13353                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13354                     }
13355                     
13356                     rm = true;
13357                     return;
13358                 }
13359             });
13360             
13361             if(rm){
13362                 return;
13363             }
13364             
13365             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13366                 this.tickItems.push(r.data);
13367             }
13368             
13369             if(typeof(e) == 'undefined' && view == false){
13370                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13371             }
13372                     
13373             return;
13374         }
13375         
13376         if(r){
13377             this.onSelect(r, index);
13378         }
13379         if(doFocus !== false && !this.blockFocus){
13380             this.inputEl().focus();
13381         }
13382     },
13383
13384     // private
13385     restrictHeight : function(){
13386         //this.innerList.dom.style.height = '';
13387         //var inner = this.innerList.dom;
13388         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13389         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13390         //this.list.beginUpdate();
13391         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13392         this.list.alignTo(this.inputEl(), this.listAlign);
13393         this.list.alignTo(this.inputEl(), this.listAlign);
13394         //this.list.endUpdate();
13395     },
13396
13397     // private
13398     onEmptyResults : function(){
13399         
13400         if(this.tickable && this.editable){
13401             this.restrictHeight();
13402             return;
13403         }
13404         
13405         this.collapse();
13406     },
13407
13408     /**
13409      * Returns true if the dropdown list is expanded, else false.
13410      */
13411     isExpanded : function(){
13412         return this.list.isVisible();
13413     },
13414
13415     /**
13416      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13417      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13418      * @param {String} value The data value of the item to select
13419      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13420      * selected item if it is not currently in view (defaults to true)
13421      * @return {Boolean} True if the value matched an item in the list, else false
13422      */
13423     selectByValue : function(v, scrollIntoView){
13424         if(v !== undefined && v !== null){
13425             var r = this.findRecord(this.valueField || this.displayField, v);
13426             if(r){
13427                 this.select(this.store.indexOf(r), scrollIntoView);
13428                 return true;
13429             }
13430         }
13431         return false;
13432     },
13433
13434     /**
13435      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13436      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13437      * @param {Number} index The zero-based index of the list item to select
13438      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13439      * selected item if it is not currently in view (defaults to true)
13440      */
13441     select : function(index, scrollIntoView){
13442         this.selectedIndex = index;
13443         this.view.select(index);
13444         if(scrollIntoView !== false){
13445             var el = this.view.getNode(index);
13446             /*
13447              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13448              */
13449             if(el){
13450                 this.list.scrollChildIntoView(el, false);
13451             }
13452         }
13453     },
13454
13455     // private
13456     selectNext : function(){
13457         var ct = this.store.getCount();
13458         if(ct > 0){
13459             if(this.selectedIndex == -1){
13460                 this.select(0);
13461             }else if(this.selectedIndex < ct-1){
13462                 this.select(this.selectedIndex+1);
13463             }
13464         }
13465     },
13466
13467     // private
13468     selectPrev : function(){
13469         var ct = this.store.getCount();
13470         if(ct > 0){
13471             if(this.selectedIndex == -1){
13472                 this.select(0);
13473             }else if(this.selectedIndex != 0){
13474                 this.select(this.selectedIndex-1);
13475             }
13476         }
13477     },
13478
13479     // private
13480     onKeyUp : function(e){
13481         if(this.editable !== false && !e.isSpecialKey()){
13482             this.lastKey = e.getKey();
13483             this.dqTask.delay(this.queryDelay);
13484         }
13485     },
13486
13487     // private
13488     validateBlur : function(){
13489         return !this.list || !this.list.isVisible();   
13490     },
13491
13492     // private
13493     initQuery : function(){
13494         
13495         var v = this.getRawValue();
13496         
13497         if(this.tickable && this.editable){
13498             v = this.tickableInputEl().getValue();
13499         }
13500         
13501         this.doQuery(v);
13502     },
13503
13504     // private
13505     doForce : function(){
13506         if(this.inputEl().dom.value.length > 0){
13507             this.inputEl().dom.value =
13508                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13509              
13510         }
13511     },
13512
13513     /**
13514      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13515      * query allowing the query action to be canceled if needed.
13516      * @param {String} query The SQL query to execute
13517      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13518      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13519      * saved in the current store (defaults to false)
13520      */
13521     doQuery : function(q, forceAll){
13522         
13523         if(q === undefined || q === null){
13524             q = '';
13525         }
13526         var qe = {
13527             query: q,
13528             forceAll: forceAll,
13529             combo: this,
13530             cancel:false
13531         };
13532         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13533             return false;
13534         }
13535         q = qe.query;
13536         
13537         forceAll = qe.forceAll;
13538         if(forceAll === true || (q.length >= this.minChars)){
13539             
13540             this.hasQuery = true;
13541             
13542             if(this.lastQuery != q || this.alwaysQuery){
13543                 this.lastQuery = q;
13544                 if(this.mode == 'local'){
13545                     this.selectedIndex = -1;
13546                     if(forceAll){
13547                         this.store.clearFilter();
13548                     }else{
13549                         
13550                         if(this.specialFilter){
13551                             this.fireEvent('specialfilter', this);
13552                             this.onLoad();
13553                             return;
13554                         }
13555                         
13556                         this.store.filter(this.displayField, q);
13557                     }
13558                     
13559                     this.store.fireEvent("datachanged", this.store);
13560                     
13561                     this.onLoad();
13562                     
13563                     
13564                 }else{
13565                     
13566                     this.store.baseParams[this.queryParam] = q;
13567                     
13568                     var options = {params : this.getParams(q)};
13569                     
13570                     if(this.loadNext){
13571                         options.add = true;
13572                         options.params.start = this.page * this.pageSize;
13573                     }
13574                     
13575                     this.store.load(options);
13576                     
13577                     /*
13578                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13579                      *  we should expand the list on onLoad
13580                      *  so command out it
13581                      */
13582 //                    this.expand();
13583                 }
13584             }else{
13585                 this.selectedIndex = -1;
13586                 this.onLoad();   
13587             }
13588         }
13589         
13590         this.loadNext = false;
13591     },
13592     
13593     // private
13594     getParams : function(q){
13595         var p = {};
13596         //p[this.queryParam] = q;
13597         
13598         if(this.pageSize){
13599             p.start = 0;
13600             p.limit = this.pageSize;
13601         }
13602         return p;
13603     },
13604
13605     /**
13606      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13607      */
13608     collapse : function(){
13609         if(!this.isExpanded()){
13610             return;
13611         }
13612         
13613         this.list.hide();
13614         
13615         if(this.tickable){
13616             this.hasFocus = false;
13617             this.okBtn.hide();
13618             this.cancelBtn.hide();
13619             this.trigger.show();
13620             
13621             if(this.editable){
13622                 this.tickableInputEl().dom.value = '';
13623                 this.tickableInputEl().blur();
13624             }
13625             
13626         }
13627         
13628         Roo.get(document).un('mousedown', this.collapseIf, this);
13629         Roo.get(document).un('mousewheel', this.collapseIf, this);
13630         if (!this.editable) {
13631             Roo.get(document).un('keydown', this.listKeyPress, this);
13632         }
13633         this.fireEvent('collapse', this);
13634         
13635         this.validate();
13636     },
13637
13638     // private
13639     collapseIf : function(e){
13640         var in_combo  = e.within(this.el);
13641         var in_list =  e.within(this.list);
13642         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13643         
13644         if (in_combo || in_list || is_list) {
13645             //e.stopPropagation();
13646             return;
13647         }
13648         
13649         if(this.tickable){
13650             this.onTickableFooterButtonClick(e, false, false);
13651         }
13652
13653         this.collapse();
13654         
13655     },
13656
13657     /**
13658      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13659      */
13660     expand : function(){
13661        
13662         if(this.isExpanded() || !this.hasFocus){
13663             return;
13664         }
13665         
13666         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13667         this.list.setWidth(lw);
13668         
13669         
13670          Roo.log('expand');
13671         
13672         this.list.show();
13673         
13674         this.restrictHeight();
13675         
13676         if(this.tickable){
13677             
13678             this.tickItems = Roo.apply([], this.item);
13679             
13680             this.okBtn.show();
13681             this.cancelBtn.show();
13682             this.trigger.hide();
13683             
13684             if(this.editable){
13685                 this.tickableInputEl().focus();
13686             }
13687             
13688         }
13689         
13690         Roo.get(document).on('mousedown', this.collapseIf, this);
13691         Roo.get(document).on('mousewheel', this.collapseIf, this);
13692         if (!this.editable) {
13693             Roo.get(document).on('keydown', this.listKeyPress, this);
13694         }
13695         
13696         this.fireEvent('expand', this);
13697     },
13698
13699     // private
13700     // Implements the default empty TriggerField.onTriggerClick function
13701     onTriggerClick : function(e)
13702     {
13703         Roo.log('trigger click');
13704         
13705         if(this.disabled || !this.triggerList){
13706             return;
13707         }
13708         
13709         this.page = 0;
13710         this.loadNext = false;
13711         
13712         if(this.isExpanded()){
13713             this.collapse();
13714             if (!this.blockFocus) {
13715                 this.inputEl().focus();
13716             }
13717             
13718         }else {
13719             this.hasFocus = true;
13720             if(this.triggerAction == 'all') {
13721                 this.doQuery(this.allQuery, true);
13722             } else {
13723                 this.doQuery(this.getRawValue());
13724             }
13725             if (!this.blockFocus) {
13726                 this.inputEl().focus();
13727             }
13728         }
13729     },
13730     
13731     onTickableTriggerClick : function(e)
13732     {
13733         if(this.disabled){
13734             return;
13735         }
13736         
13737         this.page = 0;
13738         this.loadNext = false;
13739         this.hasFocus = true;
13740         
13741         if(this.triggerAction == 'all') {
13742             this.doQuery(this.allQuery, true);
13743         } else {
13744             this.doQuery(this.getRawValue());
13745         }
13746     },
13747     
13748     onSearchFieldClick : function(e)
13749     {
13750         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13751             this.onTickableFooterButtonClick(e, false, false);
13752             return;
13753         }
13754         
13755         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13756             return;
13757         }
13758         
13759         this.page = 0;
13760         this.loadNext = false;
13761         this.hasFocus = true;
13762         
13763         if(this.triggerAction == 'all') {
13764             this.doQuery(this.allQuery, true);
13765         } else {
13766             this.doQuery(this.getRawValue());
13767         }
13768     },
13769     
13770     listKeyPress : function(e)
13771     {
13772         //Roo.log('listkeypress');
13773         // scroll to first matching element based on key pres..
13774         if (e.isSpecialKey()) {
13775             return false;
13776         }
13777         var k = String.fromCharCode(e.getKey()).toUpperCase();
13778         //Roo.log(k);
13779         var match  = false;
13780         var csel = this.view.getSelectedNodes();
13781         var cselitem = false;
13782         if (csel.length) {
13783             var ix = this.view.indexOf(csel[0]);
13784             cselitem  = this.store.getAt(ix);
13785             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13786                 cselitem = false;
13787             }
13788             
13789         }
13790         
13791         this.store.each(function(v) { 
13792             if (cselitem) {
13793                 // start at existing selection.
13794                 if (cselitem.id == v.id) {
13795                     cselitem = false;
13796                 }
13797                 return true;
13798             }
13799                 
13800             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13801                 match = this.store.indexOf(v);
13802                 return false;
13803             }
13804             return true;
13805         }, this);
13806         
13807         if (match === false) {
13808             return true; // no more action?
13809         }
13810         // scroll to?
13811         this.view.select(match);
13812         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13813         sn.scrollIntoView(sn.dom.parentNode, false);
13814     },
13815     
13816     onViewScroll : function(e, t){
13817         
13818         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){
13819             return;
13820         }
13821         
13822         this.hasQuery = true;
13823         
13824         this.loading = this.list.select('.loading', true).first();
13825         
13826         if(this.loading === null){
13827             this.list.createChild({
13828                 tag: 'div',
13829                 cls: 'loading roo-select2-more-results roo-select2-active',
13830                 html: 'Loading more results...'
13831             });
13832             
13833             this.loading = this.list.select('.loading', true).first();
13834             
13835             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13836             
13837             this.loading.hide();
13838         }
13839         
13840         this.loading.show();
13841         
13842         var _combo = this;
13843         
13844         this.page++;
13845         this.loadNext = true;
13846         
13847         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13848         
13849         return;
13850     },
13851     
13852     addItem : function(o)
13853     {   
13854         var dv = ''; // display value
13855         
13856         if (this.displayField) {
13857             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13858         } else {
13859             // this is an error condition!!!
13860             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13861         }
13862         
13863         if(!dv.length){
13864             return;
13865         }
13866         
13867         var choice = this.choices.createChild({
13868             tag: 'li',
13869             cls: 'roo-select2-search-choice',
13870             cn: [
13871                 {
13872                     tag: 'div',
13873                     html: dv
13874                 },
13875                 {
13876                     tag: 'a',
13877                     href: '#',
13878                     cls: 'roo-select2-search-choice-close',
13879                     tabindex: '-1'
13880                 }
13881             ]
13882             
13883         }, this.searchField);
13884         
13885         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13886         
13887         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13888         
13889         this.item.push(o);
13890         
13891         this.lastData = o;
13892         
13893         this.syncValue();
13894         
13895         this.inputEl().dom.value = '';
13896         
13897         this.validate();
13898     },
13899     
13900     onRemoveItem : function(e, _self, o)
13901     {
13902         e.preventDefault();
13903         
13904         this.lastItem = Roo.apply([], this.item);
13905         
13906         var index = this.item.indexOf(o.data) * 1;
13907         
13908         if( index < 0){
13909             Roo.log('not this item?!');
13910             return;
13911         }
13912         
13913         this.item.splice(index, 1);
13914         o.item.remove();
13915         
13916         this.syncValue();
13917         
13918         this.fireEvent('remove', this, e);
13919         
13920         this.validate();
13921         
13922     },
13923     
13924     syncValue : function()
13925     {
13926         if(!this.item.length){
13927             this.clearValue();
13928             return;
13929         }
13930             
13931         var value = [];
13932         var _this = this;
13933         Roo.each(this.item, function(i){
13934             if(_this.valueField){
13935                 value.push(i[_this.valueField]);
13936                 return;
13937             }
13938
13939             value.push(i);
13940         });
13941
13942         this.value = value.join(',');
13943
13944         if(this.hiddenField){
13945             this.hiddenField.dom.value = this.value;
13946         }
13947         
13948         this.store.fireEvent("datachanged", this.store);
13949         
13950         this.validate();
13951     },
13952     
13953     clearItem : function()
13954     {
13955         if(!this.multiple){
13956             return;
13957         }
13958         
13959         this.item = [];
13960         
13961         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13962            c.remove();
13963         });
13964         
13965         this.syncValue();
13966         
13967         this.validate();
13968         
13969         if(this.tickable && !Roo.isTouch){
13970             this.view.refresh();
13971         }
13972     },
13973     
13974     inputEl: function ()
13975     {
13976         if(Roo.isIOS && this.useNativeIOS){
13977             return this.el.select('select.roo-ios-select', true).first();
13978         }
13979         
13980         if(Roo.isTouch && this.mobileTouchView){
13981             return this.el.select('input.form-control',true).first();
13982         }
13983         
13984         if(this.tickable){
13985             return this.searchField;
13986         }
13987         
13988         return this.el.select('input.form-control',true).first();
13989     },
13990     
13991     onTickableFooterButtonClick : function(e, btn, el)
13992     {
13993         e.preventDefault();
13994         
13995         this.lastItem = Roo.apply([], this.item);
13996         
13997         if(btn && btn.name == 'cancel'){
13998             this.tickItems = Roo.apply([], this.item);
13999             this.collapse();
14000             return;
14001         }
14002         
14003         this.clearItem();
14004         
14005         var _this = this;
14006         
14007         Roo.each(this.tickItems, function(o){
14008             _this.addItem(o);
14009         });
14010         
14011         this.collapse();
14012         
14013     },
14014     
14015     validate : function()
14016     {
14017         var v = this.getRawValue();
14018         
14019         if(this.multiple){
14020             v = this.getValue();
14021         }
14022         
14023         if(this.disabled || this.allowBlank || v.length){
14024             this.markValid();
14025             return true;
14026         }
14027         
14028         this.markInvalid();
14029         return false;
14030     },
14031     
14032     tickableInputEl : function()
14033     {
14034         if(!this.tickable || !this.editable){
14035             return this.inputEl();
14036         }
14037         
14038         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14039     },
14040     
14041     
14042     getAutoCreateTouchView : function()
14043     {
14044         var id = Roo.id();
14045         
14046         var cfg = {
14047             cls: 'form-group' //input-group
14048         };
14049         
14050         var input =  {
14051             tag: 'input',
14052             id : id,
14053             type : this.inputType,
14054             cls : 'form-control x-combo-noedit',
14055             autocomplete: 'new-password',
14056             placeholder : this.placeholder || '',
14057             readonly : true
14058         };
14059         
14060         if (this.name) {
14061             input.name = this.name;
14062         }
14063         
14064         if (this.size) {
14065             input.cls += ' input-' + this.size;
14066         }
14067         
14068         if (this.disabled) {
14069             input.disabled = true;
14070         }
14071         
14072         var inputblock = {
14073             cls : '',
14074             cn : [
14075                 input
14076             ]
14077         };
14078         
14079         if(this.before){
14080             inputblock.cls += ' input-group';
14081             
14082             inputblock.cn.unshift({
14083                 tag :'span',
14084                 cls : 'input-group-addon',
14085                 html : this.before
14086             });
14087         }
14088         
14089         if(this.removable && !this.multiple){
14090             inputblock.cls += ' roo-removable';
14091             
14092             inputblock.cn.push({
14093                 tag: 'button',
14094                 html : 'x',
14095                 cls : 'roo-combo-removable-btn close'
14096             });
14097         }
14098
14099         if(this.hasFeedback && !this.allowBlank){
14100             
14101             inputblock.cls += ' has-feedback';
14102             
14103             inputblock.cn.push({
14104                 tag: 'span',
14105                 cls: 'glyphicon form-control-feedback'
14106             });
14107             
14108         }
14109         
14110         if (this.after) {
14111             
14112             inputblock.cls += (this.before) ? '' : ' input-group';
14113             
14114             inputblock.cn.push({
14115                 tag :'span',
14116                 cls : 'input-group-addon',
14117                 html : this.after
14118             });
14119         }
14120
14121         var box = {
14122             tag: 'div',
14123             cn: [
14124                 {
14125                     tag: 'input',
14126                     type : 'hidden',
14127                     cls: 'form-hidden-field'
14128                 },
14129                 inputblock
14130             ]
14131             
14132         };
14133         
14134         if(this.multiple){
14135             box = {
14136                 tag: 'div',
14137                 cn: [
14138                     {
14139                         tag: 'input',
14140                         type : 'hidden',
14141                         cls: 'form-hidden-field'
14142                     },
14143                     {
14144                         tag: 'ul',
14145                         cls: 'roo-select2-choices',
14146                         cn:[
14147                             {
14148                                 tag: 'li',
14149                                 cls: 'roo-select2-search-field',
14150                                 cn: [
14151
14152                                     inputblock
14153                                 ]
14154                             }
14155                         ]
14156                     }
14157                 ]
14158             }
14159         };
14160         
14161         var combobox = {
14162             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14163             cn: [
14164                 box
14165             ]
14166         };
14167         
14168         if(!this.multiple && this.showToggleBtn){
14169             
14170             var caret = {
14171                         tag: 'span',
14172                         cls: 'caret'
14173             };
14174             
14175             if (this.caret != false) {
14176                 caret = {
14177                      tag: 'i',
14178                      cls: 'fa fa-' + this.caret
14179                 };
14180                 
14181             }
14182             
14183             combobox.cn.push({
14184                 tag :'span',
14185                 cls : 'input-group-addon btn dropdown-toggle',
14186                 cn : [
14187                     caret,
14188                     {
14189                         tag: 'span',
14190                         cls: 'combobox-clear',
14191                         cn  : [
14192                             {
14193                                 tag : 'i',
14194                                 cls: 'icon-remove'
14195                             }
14196                         ]
14197                     }
14198                 ]
14199
14200             })
14201         }
14202         
14203         if(this.multiple){
14204             combobox.cls += ' roo-select2-container-multi';
14205         }
14206         
14207         var align = this.labelAlign || this.parentLabelAlign();
14208         
14209         cfg.cn = combobox;
14210         
14211         if(this.fieldLabel.length && this.labelWidth){
14212             
14213             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14214             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14215             
14216             cfg.cn = [
14217                 {
14218                    tag : 'i',
14219                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14220                    tooltip : 'This field is required'
14221                 },
14222                 {
14223                     tag: 'label',
14224                     cls : 'control-label ' + lw,
14225                     html : this.fieldLabel
14226
14227                 },
14228                 {
14229                     cls : cw, 
14230                     cn: [
14231                         combobox
14232                     ]
14233                 }
14234             ];
14235             
14236             if(this.indicatorpos == 'right'){
14237                 cfg.cn = [
14238                     {
14239                         tag: 'label',
14240                         cls : 'control-label ' + lw,
14241                         html : this.fieldLabel
14242
14243                     },
14244                     {
14245                        tag : 'i',
14246                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14247                        tooltip : 'This field is required'
14248                     },
14249                     {
14250                         cls : cw, 
14251                         cn: [
14252                             combobox
14253                         ]
14254                     }
14255                 ];
14256             }
14257         }
14258         
14259         var settings = this;
14260         
14261         ['xs','sm','md','lg'].map(function(size){
14262             if (settings[size]) {
14263                 cfg.cls += ' col-' + size + '-' + settings[size];
14264             }
14265         });
14266         
14267         return cfg;
14268     },
14269     
14270     initTouchView : function()
14271     {
14272         this.renderTouchView();
14273         
14274         this.touchViewEl.on('scroll', function(){
14275             this.el.dom.scrollTop = 0;
14276         }, this);
14277         
14278         this.originalValue = this.getValue();
14279         
14280         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14281         
14282         this.inputEl().on("click", this.showTouchView, this);
14283         if (this.triggerEl) {
14284             this.triggerEl.on("click", this.showTouchView, this);
14285         }
14286         
14287         
14288         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14289         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14290         
14291         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14292         
14293         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14294         this.store.on('load', this.onTouchViewLoad, this);
14295         this.store.on('loadexception', this.onTouchViewLoadException, this);
14296         
14297         if(this.hiddenName){
14298             
14299             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14300             
14301             this.hiddenField.dom.value =
14302                 this.hiddenValue !== undefined ? this.hiddenValue :
14303                 this.value !== undefined ? this.value : '';
14304         
14305             this.el.dom.removeAttribute('name');
14306             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14307         }
14308         
14309         if(this.multiple){
14310             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14311             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14312         }
14313         
14314         if(this.removable && !this.multiple){
14315             var close = this.closeTriggerEl();
14316             if(close){
14317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14318                 close.on('click', this.removeBtnClick, this, close);
14319             }
14320         }
14321         /*
14322          * fix the bug in Safari iOS8
14323          */
14324         this.inputEl().on("focus", function(e){
14325             document.activeElement.blur();
14326         }, this);
14327         
14328         return;
14329         
14330         
14331     },
14332     
14333     renderTouchView : function()
14334     {
14335         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14336         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14337         
14338         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14339         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14340         
14341         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14342         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14343         this.touchViewBodyEl.setStyle('overflow', 'auto');
14344         
14345         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14346         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14347         
14348         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14349         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14350         
14351     },
14352     
14353     showTouchView : function()
14354     {
14355         if(this.disabled){
14356             return;
14357         }
14358         
14359         this.touchViewHeaderEl.hide();
14360
14361         if(this.modalTitle.length){
14362             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14363             this.touchViewHeaderEl.show();
14364         }
14365
14366         this.touchViewEl.show();
14367
14368         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14369         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14370                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14371
14372         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14373
14374         if(this.modalTitle.length){
14375             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14376         }
14377         
14378         this.touchViewBodyEl.setHeight(bodyHeight);
14379
14380         if(this.animate){
14381             var _this = this;
14382             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14383         }else{
14384             this.touchViewEl.addClass('in');
14385         }
14386
14387         this.doTouchViewQuery();
14388         
14389     },
14390     
14391     hideTouchView : function()
14392     {
14393         this.touchViewEl.removeClass('in');
14394
14395         if(this.animate){
14396             var _this = this;
14397             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14398         }else{
14399             this.touchViewEl.setStyle('display', 'none');
14400         }
14401         
14402     },
14403     
14404     setTouchViewValue : function()
14405     {
14406         if(this.multiple){
14407             this.clearItem();
14408         
14409             var _this = this;
14410
14411             Roo.each(this.tickItems, function(o){
14412                 this.addItem(o);
14413             }, this);
14414         }
14415         
14416         this.hideTouchView();
14417     },
14418     
14419     doTouchViewQuery : function()
14420     {
14421         var qe = {
14422             query: '',
14423             forceAll: true,
14424             combo: this,
14425             cancel:false
14426         };
14427         
14428         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14429             return false;
14430         }
14431         
14432         if(!this.alwaysQuery || this.mode == 'local'){
14433             this.onTouchViewLoad();
14434             return;
14435         }
14436         
14437         this.store.load();
14438     },
14439     
14440     onTouchViewBeforeLoad : function(combo,opts)
14441     {
14442         return;
14443     },
14444
14445     // private
14446     onTouchViewLoad : function()
14447     {
14448         if(this.store.getCount() < 1){
14449             this.onTouchViewEmptyResults();
14450             return;
14451         }
14452         
14453         this.clearTouchView();
14454         
14455         var rawValue = this.getRawValue();
14456         
14457         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14458         
14459         this.tickItems = [];
14460         
14461         this.store.data.each(function(d, rowIndex){
14462             var row = this.touchViewListGroup.createChild(template);
14463             
14464             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14465                 row.addClass(d.data.cls);
14466             }
14467             
14468             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14469                 var cfg = {
14470                     data : d.data,
14471                     html : d.data[this.displayField]
14472                 };
14473                 
14474                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14475                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14476                 }
14477             }
14478             row.removeClass('selected');
14479             if(!this.multiple && this.valueField &&
14480                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14481             {
14482                 // radio buttons..
14483                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14484                 row.addClass('selected');
14485             }
14486             
14487             if(this.multiple && this.valueField &&
14488                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14489             {
14490                 
14491                 // checkboxes...
14492                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14493                 this.tickItems.push(d.data);
14494             }
14495             
14496             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14497             
14498         }, this);
14499         
14500         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14501         
14502         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14503
14504         if(this.modalTitle.length){
14505             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14506         }
14507
14508         var listHeight = this.touchViewListGroup.getHeight();
14509         
14510         var _this = this;
14511         
14512         if(firstChecked && listHeight > bodyHeight){
14513             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14514         }
14515         
14516     },
14517     
14518     onTouchViewLoadException : function()
14519     {
14520         this.hideTouchView();
14521     },
14522     
14523     onTouchViewEmptyResults : function()
14524     {
14525         this.clearTouchView();
14526         
14527         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14528         
14529         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14530         
14531     },
14532     
14533     clearTouchView : function()
14534     {
14535         this.touchViewListGroup.dom.innerHTML = '';
14536     },
14537     
14538     onTouchViewClick : function(e, el, o)
14539     {
14540         e.preventDefault();
14541         
14542         var row = o.row;
14543         var rowIndex = o.rowIndex;
14544         
14545         var r = this.store.getAt(rowIndex);
14546         
14547         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14548             
14549             if(!this.multiple){
14550                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14551                     c.dom.removeAttribute('checked');
14552                 }, this);
14553
14554                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14555
14556                 this.setFromData(r.data);
14557
14558                 var close = this.closeTriggerEl();
14559
14560                 if(close){
14561                     close.show();
14562                 }
14563
14564                 this.hideTouchView();
14565
14566                 this.fireEvent('select', this, r, rowIndex);
14567
14568                 return;
14569             }
14570
14571             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14572                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14573                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14574                 return;
14575             }
14576
14577             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14578             this.addItem(r.data);
14579             this.tickItems.push(r.data);
14580         }
14581     },
14582     
14583     getAutoCreateNativeIOS : function()
14584     {
14585         var cfg = {
14586             cls: 'form-group' //input-group,
14587         };
14588         
14589         var combobox =  {
14590             tag: 'select',
14591             cls : 'roo-ios-select'
14592         };
14593         
14594         if (this.name) {
14595             combobox.name = this.name;
14596         }
14597         
14598         if (this.disabled) {
14599             combobox.disabled = true;
14600         }
14601         
14602         var settings = this;
14603         
14604         ['xs','sm','md','lg'].map(function(size){
14605             if (settings[size]) {
14606                 cfg.cls += ' col-' + size + '-' + settings[size];
14607             }
14608         });
14609         
14610         cfg.cn = combobox;
14611         
14612         return cfg;
14613         
14614     },
14615     
14616     initIOSView : function()
14617     {
14618         this.store.on('load', this.onIOSViewLoad, this);
14619         
14620         return;
14621     },
14622     
14623     onIOSViewLoad : function()
14624     {
14625         if(this.store.getCount() < 1){
14626             return;
14627         }
14628         
14629         this.clearIOSView();
14630         
14631         if(this.allowBlank) {
14632             
14633             var default_text = '-- SELECT --';
14634             
14635             var opt = this.inputEl().createChild({
14636                 tag: 'option',
14637                 value : 0,
14638                 html : default_text
14639             });
14640             
14641             var o = {};
14642             o[this.valueField] = 0;
14643             o[this.displayField] = default_text;
14644             
14645             this.ios_options.push({
14646                 data : o,
14647                 el : opt
14648             });
14649             
14650         }
14651         
14652         this.store.data.each(function(d, rowIndex){
14653             
14654             var html = '';
14655             
14656             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14657                 html = d.data[this.displayField];
14658             }
14659             
14660             var value = '';
14661             
14662             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14663                 value = d.data[this.valueField];
14664             }
14665             
14666             var option = {
14667                 tag: 'option',
14668                 value : value,
14669                 html : html
14670             };
14671             
14672             if(this.value == d.data[this.valueField]){
14673                 option['selected'] = true;
14674             }
14675             
14676             var opt = this.inputEl().createChild(option);
14677             
14678             this.ios_options.push({
14679                 data : d.data,
14680                 el : opt
14681             });
14682             
14683         }, this);
14684         
14685         this.inputEl().on('change', function(){
14686            this.fireEvent('select', this);
14687         }, this);
14688         
14689     },
14690     
14691     clearIOSView: function()
14692     {
14693         this.inputEl().dom.innerHTML = '';
14694         
14695         this.ios_options = [];
14696     },
14697     
14698     setIOSValue: function(v)
14699     {
14700         this.value = v;
14701         
14702         if(!this.ios_options){
14703             return;
14704         }
14705         
14706         Roo.each(this.ios_options, function(opts){
14707            
14708            opts.el.dom.removeAttribute('selected');
14709            
14710            if(opts.data[this.valueField] != v){
14711                return;
14712            }
14713            
14714            opts.el.dom.setAttribute('selected', true);
14715            
14716         }, this);
14717     }
14718
14719     /** 
14720     * @cfg {Boolean} grow 
14721     * @hide 
14722     */
14723     /** 
14724     * @cfg {Number} growMin 
14725     * @hide 
14726     */
14727     /** 
14728     * @cfg {Number} growMax 
14729     * @hide 
14730     */
14731     /**
14732      * @hide
14733      * @method autoSize
14734      */
14735 });
14736
14737 Roo.apply(Roo.bootstrap.ComboBox,  {
14738     
14739     header : {
14740         tag: 'div',
14741         cls: 'modal-header',
14742         cn: [
14743             {
14744                 tag: 'h4',
14745                 cls: 'modal-title'
14746             }
14747         ]
14748     },
14749     
14750     body : {
14751         tag: 'div',
14752         cls: 'modal-body',
14753         cn: [
14754             {
14755                 tag: 'ul',
14756                 cls: 'list-group'
14757             }
14758         ]
14759     },
14760     
14761     listItemRadio : {
14762         tag: 'li',
14763         cls: 'list-group-item',
14764         cn: [
14765             {
14766                 tag: 'span',
14767                 cls: 'roo-combobox-list-group-item-value'
14768             },
14769             {
14770                 tag: 'div',
14771                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14772                 cn: [
14773                     {
14774                         tag: 'input',
14775                         type: 'radio'
14776                     },
14777                     {
14778                         tag: 'label'
14779                     }
14780                 ]
14781             }
14782         ]
14783     },
14784     
14785     listItemCheckbox : {
14786         tag: 'li',
14787         cls: 'list-group-item',
14788         cn: [
14789             {
14790                 tag: 'span',
14791                 cls: 'roo-combobox-list-group-item-value'
14792             },
14793             {
14794                 tag: 'div',
14795                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14796                 cn: [
14797                     {
14798                         tag: 'input',
14799                         type: 'checkbox'
14800                     },
14801                     {
14802                         tag: 'label'
14803                     }
14804                 ]
14805             }
14806         ]
14807     },
14808     
14809     emptyResult : {
14810         tag: 'div',
14811         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14812     },
14813     
14814     footer : {
14815         tag: 'div',
14816         cls: 'modal-footer',
14817         cn: [
14818             {
14819                 tag: 'div',
14820                 cls: 'row',
14821                 cn: [
14822                     {
14823                         tag: 'div',
14824                         cls: 'col-xs-6 text-left',
14825                         cn: {
14826                             tag: 'button',
14827                             cls: 'btn btn-danger roo-touch-view-cancel',
14828                             html: 'Cancel'
14829                         }
14830                     },
14831                     {
14832                         tag: 'div',
14833                         cls: 'col-xs-6 text-right',
14834                         cn: {
14835                             tag: 'button',
14836                             cls: 'btn btn-success roo-touch-view-ok',
14837                             html: 'OK'
14838                         }
14839                     }
14840                 ]
14841             }
14842         ]
14843         
14844     }
14845 });
14846
14847 Roo.apply(Roo.bootstrap.ComboBox,  {
14848     
14849     touchViewTemplate : {
14850         tag: 'div',
14851         cls: 'modal fade roo-combobox-touch-view',
14852         cn: [
14853             {
14854                 tag: 'div',
14855                 cls: 'modal-dialog',
14856                 style : 'position:fixed', // we have to fix position....
14857                 cn: [
14858                     {
14859                         tag: 'div',
14860                         cls: 'modal-content',
14861                         cn: [
14862                             Roo.bootstrap.ComboBox.header,
14863                             Roo.bootstrap.ComboBox.body,
14864                             Roo.bootstrap.ComboBox.footer
14865                         ]
14866                     }
14867                 ]
14868             }
14869         ]
14870     }
14871 });/*
14872  * Based on:
14873  * Ext JS Library 1.1.1
14874  * Copyright(c) 2006-2007, Ext JS, LLC.
14875  *
14876  * Originally Released Under LGPL - original licence link has changed is not relivant.
14877  *
14878  * Fork - LGPL
14879  * <script type="text/javascript">
14880  */
14881
14882 /**
14883  * @class Roo.View
14884  * @extends Roo.util.Observable
14885  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14886  * This class also supports single and multi selection modes. <br>
14887  * Create a data model bound view:
14888  <pre><code>
14889  var store = new Roo.data.Store(...);
14890
14891  var view = new Roo.View({
14892     el : "my-element",
14893     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14894  
14895     singleSelect: true,
14896     selectedClass: "ydataview-selected",
14897     store: store
14898  });
14899
14900  // listen for node click?
14901  view.on("click", function(vw, index, node, e){
14902  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14903  });
14904
14905  // load XML data
14906  dataModel.load("foobar.xml");
14907  </code></pre>
14908  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14909  * <br><br>
14910  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14911  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14912  * 
14913  * Note: old style constructor is still suported (container, template, config)
14914  * 
14915  * @constructor
14916  * Create a new View
14917  * @param {Object} config The config object
14918  * 
14919  */
14920 Roo.View = function(config, depreciated_tpl, depreciated_config){
14921     
14922     this.parent = false;
14923     
14924     if (typeof(depreciated_tpl) == 'undefined') {
14925         // new way.. - universal constructor.
14926         Roo.apply(this, config);
14927         this.el  = Roo.get(this.el);
14928     } else {
14929         // old format..
14930         this.el  = Roo.get(config);
14931         this.tpl = depreciated_tpl;
14932         Roo.apply(this, depreciated_config);
14933     }
14934     this.wrapEl  = this.el.wrap().wrap();
14935     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14936     
14937     
14938     if(typeof(this.tpl) == "string"){
14939         this.tpl = new Roo.Template(this.tpl);
14940     } else {
14941         // support xtype ctors..
14942         this.tpl = new Roo.factory(this.tpl, Roo);
14943     }
14944     
14945     
14946     this.tpl.compile();
14947     
14948     /** @private */
14949     this.addEvents({
14950         /**
14951          * @event beforeclick
14952          * Fires before a click is processed. Returns false to cancel the default action.
14953          * @param {Roo.View} this
14954          * @param {Number} index The index of the target node
14955          * @param {HTMLElement} node The target node
14956          * @param {Roo.EventObject} e The raw event object
14957          */
14958             "beforeclick" : true,
14959         /**
14960          * @event click
14961          * Fires when a template node is clicked.
14962          * @param {Roo.View} this
14963          * @param {Number} index The index of the target node
14964          * @param {HTMLElement} node The target node
14965          * @param {Roo.EventObject} e The raw event object
14966          */
14967             "click" : true,
14968         /**
14969          * @event dblclick
14970          * Fires when a template node is double clicked.
14971          * @param {Roo.View} this
14972          * @param {Number} index The index of the target node
14973          * @param {HTMLElement} node The target node
14974          * @param {Roo.EventObject} e The raw event object
14975          */
14976             "dblclick" : true,
14977         /**
14978          * @event contextmenu
14979          * Fires when a template node is right clicked.
14980          * @param {Roo.View} this
14981          * @param {Number} index The index of the target node
14982          * @param {HTMLElement} node The target node
14983          * @param {Roo.EventObject} e The raw event object
14984          */
14985             "contextmenu" : true,
14986         /**
14987          * @event selectionchange
14988          * Fires when the selected nodes change.
14989          * @param {Roo.View} this
14990          * @param {Array} selections Array of the selected nodes
14991          */
14992             "selectionchange" : true,
14993     
14994         /**
14995          * @event beforeselect
14996          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14997          * @param {Roo.View} this
14998          * @param {HTMLElement} node The node to be selected
14999          * @param {Array} selections Array of currently selected nodes
15000          */
15001             "beforeselect" : true,
15002         /**
15003          * @event preparedata
15004          * Fires on every row to render, to allow you to change the data.
15005          * @param {Roo.View} this
15006          * @param {Object} data to be rendered (change this)
15007          */
15008           "preparedata" : true
15009           
15010           
15011         });
15012
15013
15014
15015     this.el.on({
15016         "click": this.onClick,
15017         "dblclick": this.onDblClick,
15018         "contextmenu": this.onContextMenu,
15019         scope:this
15020     });
15021
15022     this.selections = [];
15023     this.nodes = [];
15024     this.cmp = new Roo.CompositeElementLite([]);
15025     if(this.store){
15026         this.store = Roo.factory(this.store, Roo.data);
15027         this.setStore(this.store, true);
15028     }
15029     
15030     if ( this.footer && this.footer.xtype) {
15031            
15032          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15033         
15034         this.footer.dataSource = this.store;
15035         this.footer.container = fctr;
15036         this.footer = Roo.factory(this.footer, Roo);
15037         fctr.insertFirst(this.el);
15038         
15039         // this is a bit insane - as the paging toolbar seems to detach the el..
15040 //        dom.parentNode.parentNode.parentNode
15041          // they get detached?
15042     }
15043     
15044     
15045     Roo.View.superclass.constructor.call(this);
15046     
15047     
15048 };
15049
15050 Roo.extend(Roo.View, Roo.util.Observable, {
15051     
15052      /**
15053      * @cfg {Roo.data.Store} store Data store to load data from.
15054      */
15055     store : false,
15056     
15057     /**
15058      * @cfg {String|Roo.Element} el The container element.
15059      */
15060     el : '',
15061     
15062     /**
15063      * @cfg {String|Roo.Template} tpl The template used by this View 
15064      */
15065     tpl : false,
15066     /**
15067      * @cfg {String} dataName the named area of the template to use as the data area
15068      *                          Works with domtemplates roo-name="name"
15069      */
15070     dataName: false,
15071     /**
15072      * @cfg {String} selectedClass The css class to add to selected nodes
15073      */
15074     selectedClass : "x-view-selected",
15075      /**
15076      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15077      */
15078     emptyText : "",
15079     
15080     /**
15081      * @cfg {String} text to display on mask (default Loading)
15082      */
15083     mask : false,
15084     /**
15085      * @cfg {Boolean} multiSelect Allow multiple selection
15086      */
15087     multiSelect : false,
15088     /**
15089      * @cfg {Boolean} singleSelect Allow single selection
15090      */
15091     singleSelect:  false,
15092     
15093     /**
15094      * @cfg {Boolean} toggleSelect - selecting 
15095      */
15096     toggleSelect : false,
15097     
15098     /**
15099      * @cfg {Boolean} tickable - selecting 
15100      */
15101     tickable : false,
15102     
15103     /**
15104      * Returns the element this view is bound to.
15105      * @return {Roo.Element}
15106      */
15107     getEl : function(){
15108         return this.wrapEl;
15109     },
15110     
15111     
15112
15113     /**
15114      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15115      */
15116     refresh : function(){
15117         //Roo.log('refresh');
15118         var t = this.tpl;
15119         
15120         // if we are using something like 'domtemplate', then
15121         // the what gets used is:
15122         // t.applySubtemplate(NAME, data, wrapping data..)
15123         // the outer template then get' applied with
15124         //     the store 'extra data'
15125         // and the body get's added to the
15126         //      roo-name="data" node?
15127         //      <span class='roo-tpl-{name}'></span> ?????
15128         
15129         
15130         
15131         this.clearSelections();
15132         this.el.update("");
15133         var html = [];
15134         var records = this.store.getRange();
15135         if(records.length < 1) {
15136             
15137             // is this valid??  = should it render a template??
15138             
15139             this.el.update(this.emptyText);
15140             return;
15141         }
15142         var el = this.el;
15143         if (this.dataName) {
15144             this.el.update(t.apply(this.store.meta)); //????
15145             el = this.el.child('.roo-tpl-' + this.dataName);
15146         }
15147         
15148         for(var i = 0, len = records.length; i < len; i++){
15149             var data = this.prepareData(records[i].data, i, records[i]);
15150             this.fireEvent("preparedata", this, data, i, records[i]);
15151             
15152             var d = Roo.apply({}, data);
15153             
15154             if(this.tickable){
15155                 Roo.apply(d, {'roo-id' : Roo.id()});
15156                 
15157                 var _this = this;
15158             
15159                 Roo.each(this.parent.item, function(item){
15160                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15161                         return;
15162                     }
15163                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15164                 });
15165             }
15166             
15167             html[html.length] = Roo.util.Format.trim(
15168                 this.dataName ?
15169                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15170                     t.apply(d)
15171             );
15172         }
15173         
15174         
15175         
15176         el.update(html.join(""));
15177         this.nodes = el.dom.childNodes;
15178         this.updateIndexes(0);
15179     },
15180     
15181
15182     /**
15183      * Function to override to reformat the data that is sent to
15184      * the template for each node.
15185      * DEPRICATED - use the preparedata event handler.
15186      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15187      * a JSON object for an UpdateManager bound view).
15188      */
15189     prepareData : function(data, index, record)
15190     {
15191         this.fireEvent("preparedata", this, data, index, record);
15192         return data;
15193     },
15194
15195     onUpdate : function(ds, record){
15196         // Roo.log('on update');   
15197         this.clearSelections();
15198         var index = this.store.indexOf(record);
15199         var n = this.nodes[index];
15200         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15201         n.parentNode.removeChild(n);
15202         this.updateIndexes(index, index);
15203     },
15204
15205     
15206     
15207 // --------- FIXME     
15208     onAdd : function(ds, records, index)
15209     {
15210         //Roo.log(['on Add', ds, records, index] );        
15211         this.clearSelections();
15212         if(this.nodes.length == 0){
15213             this.refresh();
15214             return;
15215         }
15216         var n = this.nodes[index];
15217         for(var i = 0, len = records.length; i < len; i++){
15218             var d = this.prepareData(records[i].data, i, records[i]);
15219             if(n){
15220                 this.tpl.insertBefore(n, d);
15221             }else{
15222                 
15223                 this.tpl.append(this.el, d);
15224             }
15225         }
15226         this.updateIndexes(index);
15227     },
15228
15229     onRemove : function(ds, record, index){
15230        // Roo.log('onRemove');
15231         this.clearSelections();
15232         var el = this.dataName  ?
15233             this.el.child('.roo-tpl-' + this.dataName) :
15234             this.el; 
15235         
15236         el.dom.removeChild(this.nodes[index]);
15237         this.updateIndexes(index);
15238     },
15239
15240     /**
15241      * Refresh an individual node.
15242      * @param {Number} index
15243      */
15244     refreshNode : function(index){
15245         this.onUpdate(this.store, this.store.getAt(index));
15246     },
15247
15248     updateIndexes : function(startIndex, endIndex){
15249         var ns = this.nodes;
15250         startIndex = startIndex || 0;
15251         endIndex = endIndex || ns.length - 1;
15252         for(var i = startIndex; i <= endIndex; i++){
15253             ns[i].nodeIndex = i;
15254         }
15255     },
15256
15257     /**
15258      * Changes the data store this view uses and refresh the view.
15259      * @param {Store} store
15260      */
15261     setStore : function(store, initial){
15262         if(!initial && this.store){
15263             this.store.un("datachanged", this.refresh);
15264             this.store.un("add", this.onAdd);
15265             this.store.un("remove", this.onRemove);
15266             this.store.un("update", this.onUpdate);
15267             this.store.un("clear", this.refresh);
15268             this.store.un("beforeload", this.onBeforeLoad);
15269             this.store.un("load", this.onLoad);
15270             this.store.un("loadexception", this.onLoad);
15271         }
15272         if(store){
15273           
15274             store.on("datachanged", this.refresh, this);
15275             store.on("add", this.onAdd, this);
15276             store.on("remove", this.onRemove, this);
15277             store.on("update", this.onUpdate, this);
15278             store.on("clear", this.refresh, this);
15279             store.on("beforeload", this.onBeforeLoad, this);
15280             store.on("load", this.onLoad, this);
15281             store.on("loadexception", this.onLoad, this);
15282         }
15283         
15284         if(store){
15285             this.refresh();
15286         }
15287     },
15288     /**
15289      * onbeforeLoad - masks the loading area.
15290      *
15291      */
15292     onBeforeLoad : function(store,opts)
15293     {
15294          //Roo.log('onBeforeLoad');   
15295         if (!opts.add) {
15296             this.el.update("");
15297         }
15298         this.el.mask(this.mask ? this.mask : "Loading" ); 
15299     },
15300     onLoad : function ()
15301     {
15302         this.el.unmask();
15303     },
15304     
15305
15306     /**
15307      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15308      * @param {HTMLElement} node
15309      * @return {HTMLElement} The template node
15310      */
15311     findItemFromChild : function(node){
15312         var el = this.dataName  ?
15313             this.el.child('.roo-tpl-' + this.dataName,true) :
15314             this.el.dom; 
15315         
15316         if(!node || node.parentNode == el){
15317                     return node;
15318             }
15319             var p = node.parentNode;
15320             while(p && p != el){
15321             if(p.parentNode == el){
15322                 return p;
15323             }
15324             p = p.parentNode;
15325         }
15326             return null;
15327     },
15328
15329     /** @ignore */
15330     onClick : function(e){
15331         var item = this.findItemFromChild(e.getTarget());
15332         if(item){
15333             var index = this.indexOf(item);
15334             if(this.onItemClick(item, index, e) !== false){
15335                 this.fireEvent("click", this, index, item, e);
15336             }
15337         }else{
15338             this.clearSelections();
15339         }
15340     },
15341
15342     /** @ignore */
15343     onContextMenu : function(e){
15344         var item = this.findItemFromChild(e.getTarget());
15345         if(item){
15346             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15347         }
15348     },
15349
15350     /** @ignore */
15351     onDblClick : function(e){
15352         var item = this.findItemFromChild(e.getTarget());
15353         if(item){
15354             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15355         }
15356     },
15357
15358     onItemClick : function(item, index, e)
15359     {
15360         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15361             return false;
15362         }
15363         if (this.toggleSelect) {
15364             var m = this.isSelected(item) ? 'unselect' : 'select';
15365             //Roo.log(m);
15366             var _t = this;
15367             _t[m](item, true, false);
15368             return true;
15369         }
15370         if(this.multiSelect || this.singleSelect){
15371             if(this.multiSelect && e.shiftKey && this.lastSelection){
15372                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15373             }else{
15374                 this.select(item, this.multiSelect && e.ctrlKey);
15375                 this.lastSelection = item;
15376             }
15377             
15378             if(!this.tickable){
15379                 e.preventDefault();
15380             }
15381             
15382         }
15383         return true;
15384     },
15385
15386     /**
15387      * Get the number of selected nodes.
15388      * @return {Number}
15389      */
15390     getSelectionCount : function(){
15391         return this.selections.length;
15392     },
15393
15394     /**
15395      * Get the currently selected nodes.
15396      * @return {Array} An array of HTMLElements
15397      */
15398     getSelectedNodes : function(){
15399         return this.selections;
15400     },
15401
15402     /**
15403      * Get the indexes of the selected nodes.
15404      * @return {Array}
15405      */
15406     getSelectedIndexes : function(){
15407         var indexes = [], s = this.selections;
15408         for(var i = 0, len = s.length; i < len; i++){
15409             indexes.push(s[i].nodeIndex);
15410         }
15411         return indexes;
15412     },
15413
15414     /**
15415      * Clear all selections
15416      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15417      */
15418     clearSelections : function(suppressEvent){
15419         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15420             this.cmp.elements = this.selections;
15421             this.cmp.removeClass(this.selectedClass);
15422             this.selections = [];
15423             if(!suppressEvent){
15424                 this.fireEvent("selectionchange", this, this.selections);
15425             }
15426         }
15427     },
15428
15429     /**
15430      * Returns true if the passed node is selected
15431      * @param {HTMLElement/Number} node The node or node index
15432      * @return {Boolean}
15433      */
15434     isSelected : function(node){
15435         var s = this.selections;
15436         if(s.length < 1){
15437             return false;
15438         }
15439         node = this.getNode(node);
15440         return s.indexOf(node) !== -1;
15441     },
15442
15443     /**
15444      * Selects nodes.
15445      * @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
15446      * @param {Boolean} keepExisting (optional) true to keep existing selections
15447      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15448      */
15449     select : function(nodeInfo, keepExisting, suppressEvent){
15450         if(nodeInfo instanceof Array){
15451             if(!keepExisting){
15452                 this.clearSelections(true);
15453             }
15454             for(var i = 0, len = nodeInfo.length; i < len; i++){
15455                 this.select(nodeInfo[i], true, true);
15456             }
15457             return;
15458         } 
15459         var node = this.getNode(nodeInfo);
15460         if(!node || this.isSelected(node)){
15461             return; // already selected.
15462         }
15463         if(!keepExisting){
15464             this.clearSelections(true);
15465         }
15466         
15467         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15468             Roo.fly(node).addClass(this.selectedClass);
15469             this.selections.push(node);
15470             if(!suppressEvent){
15471                 this.fireEvent("selectionchange", this, this.selections);
15472             }
15473         }
15474         
15475         
15476     },
15477       /**
15478      * Unselects nodes.
15479      * @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
15480      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15481      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15482      */
15483     unselect : function(nodeInfo, keepExisting, suppressEvent)
15484     {
15485         if(nodeInfo instanceof Array){
15486             Roo.each(this.selections, function(s) {
15487                 this.unselect(s, nodeInfo);
15488             }, this);
15489             return;
15490         }
15491         var node = this.getNode(nodeInfo);
15492         if(!node || !this.isSelected(node)){
15493             //Roo.log("not selected");
15494             return; // not selected.
15495         }
15496         // fireevent???
15497         var ns = [];
15498         Roo.each(this.selections, function(s) {
15499             if (s == node ) {
15500                 Roo.fly(node).removeClass(this.selectedClass);
15501
15502                 return;
15503             }
15504             ns.push(s);
15505         },this);
15506         
15507         this.selections= ns;
15508         this.fireEvent("selectionchange", this, this.selections);
15509     },
15510
15511     /**
15512      * Gets a template node.
15513      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15514      * @return {HTMLElement} The node or null if it wasn't found
15515      */
15516     getNode : function(nodeInfo){
15517         if(typeof nodeInfo == "string"){
15518             return document.getElementById(nodeInfo);
15519         }else if(typeof nodeInfo == "number"){
15520             return this.nodes[nodeInfo];
15521         }
15522         return nodeInfo;
15523     },
15524
15525     /**
15526      * Gets a range template nodes.
15527      * @param {Number} startIndex
15528      * @param {Number} endIndex
15529      * @return {Array} An array of nodes
15530      */
15531     getNodes : function(start, end){
15532         var ns = this.nodes;
15533         start = start || 0;
15534         end = typeof end == "undefined" ? ns.length - 1 : end;
15535         var nodes = [];
15536         if(start <= end){
15537             for(var i = start; i <= end; i++){
15538                 nodes.push(ns[i]);
15539             }
15540         } else{
15541             for(var i = start; i >= end; i--){
15542                 nodes.push(ns[i]);
15543             }
15544         }
15545         return nodes;
15546     },
15547
15548     /**
15549      * Finds the index of the passed node
15550      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15551      * @return {Number} The index of the node or -1
15552      */
15553     indexOf : function(node){
15554         node = this.getNode(node);
15555         if(typeof node.nodeIndex == "number"){
15556             return node.nodeIndex;
15557         }
15558         var ns = this.nodes;
15559         for(var i = 0, len = ns.length; i < len; i++){
15560             if(ns[i] == node){
15561                 return i;
15562             }
15563         }
15564         return -1;
15565     }
15566 });
15567 /*
15568  * - LGPL
15569  *
15570  * based on jquery fullcalendar
15571  * 
15572  */
15573
15574 Roo.bootstrap = Roo.bootstrap || {};
15575 /**
15576  * @class Roo.bootstrap.Calendar
15577  * @extends Roo.bootstrap.Component
15578  * Bootstrap Calendar class
15579  * @cfg {Boolean} loadMask (true|false) default false
15580  * @cfg {Object} header generate the user specific header of the calendar, default false
15581
15582  * @constructor
15583  * Create a new Container
15584  * @param {Object} config The config object
15585  */
15586
15587
15588
15589 Roo.bootstrap.Calendar = function(config){
15590     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15591      this.addEvents({
15592         /**
15593              * @event select
15594              * Fires when a date is selected
15595              * @param {DatePicker} this
15596              * @param {Date} date The selected date
15597              */
15598         'select': true,
15599         /**
15600              * @event monthchange
15601              * Fires when the displayed month changes 
15602              * @param {DatePicker} this
15603              * @param {Date} date The selected month
15604              */
15605         'monthchange': true,
15606         /**
15607              * @event evententer
15608              * Fires when mouse over an event
15609              * @param {Calendar} this
15610              * @param {event} Event
15611              */
15612         'evententer': true,
15613         /**
15614              * @event eventleave
15615              * Fires when the mouse leaves an
15616              * @param {Calendar} this
15617              * @param {event}
15618              */
15619         'eventleave': true,
15620         /**
15621              * @event eventclick
15622              * Fires when the mouse click an
15623              * @param {Calendar} this
15624              * @param {event}
15625              */
15626         'eventclick': true
15627         
15628     });
15629
15630 };
15631
15632 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15633     
15634      /**
15635      * @cfg {Number} startDay
15636      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15637      */
15638     startDay : 0,
15639     
15640     loadMask : false,
15641     
15642     header : false,
15643       
15644     getAutoCreate : function(){
15645         
15646         
15647         var fc_button = function(name, corner, style, content ) {
15648             return Roo.apply({},{
15649                 tag : 'span',
15650                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15651                          (corner.length ?
15652                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15653                             ''
15654                         ),
15655                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15656                 unselectable: 'on'
15657             });
15658         };
15659         
15660         var header = {};
15661         
15662         if(!this.header){
15663             header = {
15664                 tag : 'table',
15665                 cls : 'fc-header',
15666                 style : 'width:100%',
15667                 cn : [
15668                     {
15669                         tag: 'tr',
15670                         cn : [
15671                             {
15672                                 tag : 'td',
15673                                 cls : 'fc-header-left',
15674                                 cn : [
15675                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15676                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15677                                     { tag: 'span', cls: 'fc-header-space' },
15678                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15679
15680
15681                                 ]
15682                             },
15683
15684                             {
15685                                 tag : 'td',
15686                                 cls : 'fc-header-center',
15687                                 cn : [
15688                                     {
15689                                         tag: 'span',
15690                                         cls: 'fc-header-title',
15691                                         cn : {
15692                                             tag: 'H2',
15693                                             html : 'month / year'
15694                                         }
15695                                     }
15696
15697                                 ]
15698                             },
15699                             {
15700                                 tag : 'td',
15701                                 cls : 'fc-header-right',
15702                                 cn : [
15703                               /*      fc_button('month', 'left', '', 'month' ),
15704                                     fc_button('week', '', '', 'week' ),
15705                                     fc_button('day', 'right', '', 'day' )
15706                                 */    
15707
15708                                 ]
15709                             }
15710
15711                         ]
15712                     }
15713                 ]
15714             };
15715         }
15716         
15717         header = this.header;
15718         
15719        
15720         var cal_heads = function() {
15721             var ret = [];
15722             // fixme - handle this.
15723             
15724             for (var i =0; i < Date.dayNames.length; i++) {
15725                 var d = Date.dayNames[i];
15726                 ret.push({
15727                     tag: 'th',
15728                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15729                     html : d.substring(0,3)
15730                 });
15731                 
15732             }
15733             ret[0].cls += ' fc-first';
15734             ret[6].cls += ' fc-last';
15735             return ret;
15736         };
15737         var cal_cell = function(n) {
15738             return  {
15739                 tag: 'td',
15740                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15741                 cn : [
15742                     {
15743                         cn : [
15744                             {
15745                                 cls: 'fc-day-number',
15746                                 html: 'D'
15747                             },
15748                             {
15749                                 cls: 'fc-day-content',
15750                              
15751                                 cn : [
15752                                      {
15753                                         style: 'position: relative;' // height: 17px;
15754                                     }
15755                                 ]
15756                             }
15757                             
15758                             
15759                         ]
15760                     }
15761                 ]
15762                 
15763             }
15764         };
15765         var cal_rows = function() {
15766             
15767             var ret = [];
15768             for (var r = 0; r < 6; r++) {
15769                 var row= {
15770                     tag : 'tr',
15771                     cls : 'fc-week',
15772                     cn : []
15773                 };
15774                 
15775                 for (var i =0; i < Date.dayNames.length; i++) {
15776                     var d = Date.dayNames[i];
15777                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15778
15779                 }
15780                 row.cn[0].cls+=' fc-first';
15781                 row.cn[0].cn[0].style = 'min-height:90px';
15782                 row.cn[6].cls+=' fc-last';
15783                 ret.push(row);
15784                 
15785             }
15786             ret[0].cls += ' fc-first';
15787             ret[4].cls += ' fc-prev-last';
15788             ret[5].cls += ' fc-last';
15789             return ret;
15790             
15791         };
15792         
15793         var cal_table = {
15794             tag: 'table',
15795             cls: 'fc-border-separate',
15796             style : 'width:100%',
15797             cellspacing  : 0,
15798             cn : [
15799                 { 
15800                     tag: 'thead',
15801                     cn : [
15802                         { 
15803                             tag: 'tr',
15804                             cls : 'fc-first fc-last',
15805                             cn : cal_heads()
15806                         }
15807                     ]
15808                 },
15809                 { 
15810                     tag: 'tbody',
15811                     cn : cal_rows()
15812                 }
15813                   
15814             ]
15815         };
15816          
15817          var cfg = {
15818             cls : 'fc fc-ltr',
15819             cn : [
15820                 header,
15821                 {
15822                     cls : 'fc-content',
15823                     style : "position: relative;",
15824                     cn : [
15825                         {
15826                             cls : 'fc-view fc-view-month fc-grid',
15827                             style : 'position: relative',
15828                             unselectable : 'on',
15829                             cn : [
15830                                 {
15831                                     cls : 'fc-event-container',
15832                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15833                                 },
15834                                 cal_table
15835                             ]
15836                         }
15837                     ]
15838     
15839                 }
15840            ] 
15841             
15842         };
15843         
15844          
15845         
15846         return cfg;
15847     },
15848     
15849     
15850     initEvents : function()
15851     {
15852         if(!this.store){
15853             throw "can not find store for calendar";
15854         }
15855         
15856         var mark = {
15857             tag: "div",
15858             cls:"x-dlg-mask",
15859             style: "text-align:center",
15860             cn: [
15861                 {
15862                     tag: "div",
15863                     style: "background-color:white;width:50%;margin:250 auto",
15864                     cn: [
15865                         {
15866                             tag: "img",
15867                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15868                         },
15869                         {
15870                             tag: "span",
15871                             html: "Loading"
15872                         }
15873                         
15874                     ]
15875                 }
15876             ]
15877         };
15878         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15879         
15880         var size = this.el.select('.fc-content', true).first().getSize();
15881         this.maskEl.setSize(size.width, size.height);
15882         this.maskEl.enableDisplayMode("block");
15883         if(!this.loadMask){
15884             this.maskEl.hide();
15885         }
15886         
15887         this.store = Roo.factory(this.store, Roo.data);
15888         this.store.on('load', this.onLoad, this);
15889         this.store.on('beforeload', this.onBeforeLoad, this);
15890         
15891         this.resize();
15892         
15893         this.cells = this.el.select('.fc-day',true);
15894         //Roo.log(this.cells);
15895         this.textNodes = this.el.query('.fc-day-number');
15896         this.cells.addClassOnOver('fc-state-hover');
15897         
15898         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15899         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15900         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15901         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15902         
15903         this.on('monthchange', this.onMonthChange, this);
15904         
15905         this.update(new Date().clearTime());
15906     },
15907     
15908     resize : function() {
15909         var sz  = this.el.getSize();
15910         
15911         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15912         this.el.select('.fc-day-content div',true).setHeight(34);
15913     },
15914     
15915     
15916     // private
15917     showPrevMonth : function(e){
15918         this.update(this.activeDate.add("mo", -1));
15919     },
15920     showToday : function(e){
15921         this.update(new Date().clearTime());
15922     },
15923     // private
15924     showNextMonth : function(e){
15925         this.update(this.activeDate.add("mo", 1));
15926     },
15927
15928     // private
15929     showPrevYear : function(){
15930         this.update(this.activeDate.add("y", -1));
15931     },
15932
15933     // private
15934     showNextYear : function(){
15935         this.update(this.activeDate.add("y", 1));
15936     },
15937
15938     
15939    // private
15940     update : function(date)
15941     {
15942         var vd = this.activeDate;
15943         this.activeDate = date;
15944 //        if(vd && this.el){
15945 //            var t = date.getTime();
15946 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15947 //                Roo.log('using add remove');
15948 //                
15949 //                this.fireEvent('monthchange', this, date);
15950 //                
15951 //                this.cells.removeClass("fc-state-highlight");
15952 //                this.cells.each(function(c){
15953 //                   if(c.dateValue == t){
15954 //                       c.addClass("fc-state-highlight");
15955 //                       setTimeout(function(){
15956 //                            try{c.dom.firstChild.focus();}catch(e){}
15957 //                       }, 50);
15958 //                       return false;
15959 //                   }
15960 //                   return true;
15961 //                });
15962 //                return;
15963 //            }
15964 //        }
15965         
15966         var days = date.getDaysInMonth();
15967         
15968         var firstOfMonth = date.getFirstDateOfMonth();
15969         var startingPos = firstOfMonth.getDay()-this.startDay;
15970         
15971         if(startingPos < this.startDay){
15972             startingPos += 7;
15973         }
15974         
15975         var pm = date.add(Date.MONTH, -1);
15976         var prevStart = pm.getDaysInMonth()-startingPos;
15977 //        
15978         this.cells = this.el.select('.fc-day',true);
15979         this.textNodes = this.el.query('.fc-day-number');
15980         this.cells.addClassOnOver('fc-state-hover');
15981         
15982         var cells = this.cells.elements;
15983         var textEls = this.textNodes;
15984         
15985         Roo.each(cells, function(cell){
15986             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15987         });
15988         
15989         days += startingPos;
15990
15991         // convert everything to numbers so it's fast
15992         var day = 86400000;
15993         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15994         //Roo.log(d);
15995         //Roo.log(pm);
15996         //Roo.log(prevStart);
15997         
15998         var today = new Date().clearTime().getTime();
15999         var sel = date.clearTime().getTime();
16000         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16001         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16002         var ddMatch = this.disabledDatesRE;
16003         var ddText = this.disabledDatesText;
16004         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16005         var ddaysText = this.disabledDaysText;
16006         var format = this.format;
16007         
16008         var setCellClass = function(cal, cell){
16009             cell.row = 0;
16010             cell.events = [];
16011             cell.more = [];
16012             //Roo.log('set Cell Class');
16013             cell.title = "";
16014             var t = d.getTime();
16015             
16016             //Roo.log(d);
16017             
16018             cell.dateValue = t;
16019             if(t == today){
16020                 cell.className += " fc-today";
16021                 cell.className += " fc-state-highlight";
16022                 cell.title = cal.todayText;
16023             }
16024             if(t == sel){
16025                 // disable highlight in other month..
16026                 //cell.className += " fc-state-highlight";
16027                 
16028             }
16029             // disabling
16030             if(t < min) {
16031                 cell.className = " fc-state-disabled";
16032                 cell.title = cal.minText;
16033                 return;
16034             }
16035             if(t > max) {
16036                 cell.className = " fc-state-disabled";
16037                 cell.title = cal.maxText;
16038                 return;
16039             }
16040             if(ddays){
16041                 if(ddays.indexOf(d.getDay()) != -1){
16042                     cell.title = ddaysText;
16043                     cell.className = " fc-state-disabled";
16044                 }
16045             }
16046             if(ddMatch && format){
16047                 var fvalue = d.dateFormat(format);
16048                 if(ddMatch.test(fvalue)){
16049                     cell.title = ddText.replace("%0", fvalue);
16050                     cell.className = " fc-state-disabled";
16051                 }
16052             }
16053             
16054             if (!cell.initialClassName) {
16055                 cell.initialClassName = cell.dom.className;
16056             }
16057             
16058             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16059         };
16060
16061         var i = 0;
16062         
16063         for(; i < startingPos; i++) {
16064             textEls[i].innerHTML = (++prevStart);
16065             d.setDate(d.getDate()+1);
16066             
16067             cells[i].className = "fc-past fc-other-month";
16068             setCellClass(this, cells[i]);
16069         }
16070         
16071         var intDay = 0;
16072         
16073         for(; i < days; i++){
16074             intDay = i - startingPos + 1;
16075             textEls[i].innerHTML = (intDay);
16076             d.setDate(d.getDate()+1);
16077             
16078             cells[i].className = ''; // "x-date-active";
16079             setCellClass(this, cells[i]);
16080         }
16081         var extraDays = 0;
16082         
16083         for(; i < 42; i++) {
16084             textEls[i].innerHTML = (++extraDays);
16085             d.setDate(d.getDate()+1);
16086             
16087             cells[i].className = "fc-future fc-other-month";
16088             setCellClass(this, cells[i]);
16089         }
16090         
16091         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16092         
16093         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16094         
16095         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16096         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16097         
16098         if(totalRows != 6){
16099             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16100             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16101         }
16102         
16103         this.fireEvent('monthchange', this, date);
16104         
16105         
16106         /*
16107         if(!this.internalRender){
16108             var main = this.el.dom.firstChild;
16109             var w = main.offsetWidth;
16110             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16111             Roo.fly(main).setWidth(w);
16112             this.internalRender = true;
16113             // opera does not respect the auto grow header center column
16114             // then, after it gets a width opera refuses to recalculate
16115             // without a second pass
16116             if(Roo.isOpera && !this.secondPass){
16117                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16118                 this.secondPass = true;
16119                 this.update.defer(10, this, [date]);
16120             }
16121         }
16122         */
16123         
16124     },
16125     
16126     findCell : function(dt) {
16127         dt = dt.clearTime().getTime();
16128         var ret = false;
16129         this.cells.each(function(c){
16130             //Roo.log("check " +c.dateValue + '?=' + dt);
16131             if(c.dateValue == dt){
16132                 ret = c;
16133                 return false;
16134             }
16135             return true;
16136         });
16137         
16138         return ret;
16139     },
16140     
16141     findCells : function(ev) {
16142         var s = ev.start.clone().clearTime().getTime();
16143        // Roo.log(s);
16144         var e= ev.end.clone().clearTime().getTime();
16145        // Roo.log(e);
16146         var ret = [];
16147         this.cells.each(function(c){
16148              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16149             
16150             if(c.dateValue > e){
16151                 return ;
16152             }
16153             if(c.dateValue < s){
16154                 return ;
16155             }
16156             ret.push(c);
16157         });
16158         
16159         return ret;    
16160     },
16161     
16162 //    findBestRow: function(cells)
16163 //    {
16164 //        var ret = 0;
16165 //        
16166 //        for (var i =0 ; i < cells.length;i++) {
16167 //            ret  = Math.max(cells[i].rows || 0,ret);
16168 //        }
16169 //        return ret;
16170 //        
16171 //    },
16172     
16173     
16174     addItem : function(ev)
16175     {
16176         // look for vertical location slot in
16177         var cells = this.findCells(ev);
16178         
16179 //        ev.row = this.findBestRow(cells);
16180         
16181         // work out the location.
16182         
16183         var crow = false;
16184         var rows = [];
16185         for(var i =0; i < cells.length; i++) {
16186             
16187             cells[i].row = cells[0].row;
16188             
16189             if(i == 0){
16190                 cells[i].row = cells[i].row + 1;
16191             }
16192             
16193             if (!crow) {
16194                 crow = {
16195                     start : cells[i],
16196                     end :  cells[i]
16197                 };
16198                 continue;
16199             }
16200             if (crow.start.getY() == cells[i].getY()) {
16201                 // on same row.
16202                 crow.end = cells[i];
16203                 continue;
16204             }
16205             // different row.
16206             rows.push(crow);
16207             crow = {
16208                 start: cells[i],
16209                 end : cells[i]
16210             };
16211             
16212         }
16213         
16214         rows.push(crow);
16215         ev.els = [];
16216         ev.rows = rows;
16217         ev.cells = cells;
16218         
16219         cells[0].events.push(ev);
16220         
16221         this.calevents.push(ev);
16222     },
16223     
16224     clearEvents: function() {
16225         
16226         if(!this.calevents){
16227             return;
16228         }
16229         
16230         Roo.each(this.cells.elements, function(c){
16231             c.row = 0;
16232             c.events = [];
16233             c.more = [];
16234         });
16235         
16236         Roo.each(this.calevents, function(e) {
16237             Roo.each(e.els, function(el) {
16238                 el.un('mouseenter' ,this.onEventEnter, this);
16239                 el.un('mouseleave' ,this.onEventLeave, this);
16240                 el.remove();
16241             },this);
16242         },this);
16243         
16244         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16245             e.remove();
16246         });
16247         
16248     },
16249     
16250     renderEvents: function()
16251     {   
16252         var _this = this;
16253         
16254         this.cells.each(function(c) {
16255             
16256             if(c.row < 5){
16257                 return;
16258             }
16259             
16260             var ev = c.events;
16261             
16262             var r = 4;
16263             if(c.row != c.events.length){
16264                 r = 4 - (4 - (c.row - c.events.length));
16265             }
16266             
16267             c.events = ev.slice(0, r);
16268             c.more = ev.slice(r);
16269             
16270             if(c.more.length && c.more.length == 1){
16271                 c.events.push(c.more.pop());
16272             }
16273             
16274             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16275             
16276         });
16277             
16278         this.cells.each(function(c) {
16279             
16280             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16281             
16282             
16283             for (var e = 0; e < c.events.length; e++){
16284                 var ev = c.events[e];
16285                 var rows = ev.rows;
16286                 
16287                 for(var i = 0; i < rows.length; i++) {
16288                 
16289                     // how many rows should it span..
16290
16291                     var  cfg = {
16292                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16293                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16294
16295                         unselectable : "on",
16296                         cn : [
16297                             {
16298                                 cls: 'fc-event-inner',
16299                                 cn : [
16300     //                                {
16301     //                                  tag:'span',
16302     //                                  cls: 'fc-event-time',
16303     //                                  html : cells.length > 1 ? '' : ev.time
16304     //                                },
16305                                     {
16306                                       tag:'span',
16307                                       cls: 'fc-event-title',
16308                                       html : String.format('{0}', ev.title)
16309                                     }
16310
16311
16312                                 ]
16313                             },
16314                             {
16315                                 cls: 'ui-resizable-handle ui-resizable-e',
16316                                 html : '&nbsp;&nbsp;&nbsp'
16317                             }
16318
16319                         ]
16320                     };
16321
16322                     if (i == 0) {
16323                         cfg.cls += ' fc-event-start';
16324                     }
16325                     if ((i+1) == rows.length) {
16326                         cfg.cls += ' fc-event-end';
16327                     }
16328
16329                     var ctr = _this.el.select('.fc-event-container',true).first();
16330                     var cg = ctr.createChild(cfg);
16331
16332                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16333                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16334
16335                     var r = (c.more.length) ? 1 : 0;
16336                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16337                     cg.setWidth(ebox.right - sbox.x -2);
16338
16339                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16340                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16341                     cg.on('click', _this.onEventClick, _this, ev);
16342
16343                     ev.els.push(cg);
16344                     
16345                 }
16346                 
16347             }
16348             
16349             
16350             if(c.more.length){
16351                 var  cfg = {
16352                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16353                     style : 'position: absolute',
16354                     unselectable : "on",
16355                     cn : [
16356                         {
16357                             cls: 'fc-event-inner',
16358                             cn : [
16359                                 {
16360                                   tag:'span',
16361                                   cls: 'fc-event-title',
16362                                   html : 'More'
16363                                 }
16364
16365
16366                             ]
16367                         },
16368                         {
16369                             cls: 'ui-resizable-handle ui-resizable-e',
16370                             html : '&nbsp;&nbsp;&nbsp'
16371                         }
16372
16373                     ]
16374                 };
16375
16376                 var ctr = _this.el.select('.fc-event-container',true).first();
16377                 var cg = ctr.createChild(cfg);
16378
16379                 var sbox = c.select('.fc-day-content',true).first().getBox();
16380                 var ebox = c.select('.fc-day-content',true).first().getBox();
16381                 //Roo.log(cg);
16382                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16383                 cg.setWidth(ebox.right - sbox.x -2);
16384
16385                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16386                 
16387             }
16388             
16389         });
16390         
16391         
16392         
16393     },
16394     
16395     onEventEnter: function (e, el,event,d) {
16396         this.fireEvent('evententer', this, el, event);
16397     },
16398     
16399     onEventLeave: function (e, el,event,d) {
16400         this.fireEvent('eventleave', this, el, event);
16401     },
16402     
16403     onEventClick: function (e, el,event,d) {
16404         this.fireEvent('eventclick', this, el, event);
16405     },
16406     
16407     onMonthChange: function () {
16408         this.store.load();
16409     },
16410     
16411     onMoreEventClick: function(e, el, more)
16412     {
16413         var _this = this;
16414         
16415         this.calpopover.placement = 'right';
16416         this.calpopover.setTitle('More');
16417         
16418         this.calpopover.setContent('');
16419         
16420         var ctr = this.calpopover.el.select('.popover-content', true).first();
16421         
16422         Roo.each(more, function(m){
16423             var cfg = {
16424                 cls : 'fc-event-hori fc-event-draggable',
16425                 html : m.title
16426             };
16427             var cg = ctr.createChild(cfg);
16428             
16429             cg.on('click', _this.onEventClick, _this, m);
16430         });
16431         
16432         this.calpopover.show(el);
16433         
16434         
16435     },
16436     
16437     onLoad: function () 
16438     {   
16439         this.calevents = [];
16440         var cal = this;
16441         
16442         if(this.store.getCount() > 0){
16443             this.store.data.each(function(d){
16444                cal.addItem({
16445                     id : d.data.id,
16446                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16447                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16448                     time : d.data.start_time,
16449                     title : d.data.title,
16450                     description : d.data.description,
16451                     venue : d.data.venue
16452                 });
16453             });
16454         }
16455         
16456         this.renderEvents();
16457         
16458         if(this.calevents.length && this.loadMask){
16459             this.maskEl.hide();
16460         }
16461     },
16462     
16463     onBeforeLoad: function()
16464     {
16465         this.clearEvents();
16466         if(this.loadMask){
16467             this.maskEl.show();
16468         }
16469     }
16470 });
16471
16472  
16473  /*
16474  * - LGPL
16475  *
16476  * element
16477  * 
16478  */
16479
16480 /**
16481  * @class Roo.bootstrap.Popover
16482  * @extends Roo.bootstrap.Component
16483  * Bootstrap Popover class
16484  * @cfg {String} html contents of the popover   (or false to use children..)
16485  * @cfg {String} title of popover (or false to hide)
16486  * @cfg {String} placement how it is placed
16487  * @cfg {String} trigger click || hover (or false to trigger manually)
16488  * @cfg {String} over what (parent or false to trigger manually.)
16489  * @cfg {Number} delay - delay before showing
16490  
16491  * @constructor
16492  * Create a new Popover
16493  * @param {Object} config The config object
16494  */
16495
16496 Roo.bootstrap.Popover = function(config){
16497     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16498     
16499     this.addEvents({
16500         // raw events
16501          /**
16502          * @event show
16503          * After the popover show
16504          * 
16505          * @param {Roo.bootstrap.Popover} this
16506          */
16507         "show" : true,
16508         /**
16509          * @event hide
16510          * After the popover hide
16511          * 
16512          * @param {Roo.bootstrap.Popover} this
16513          */
16514         "hide" : true
16515     });
16516 };
16517
16518 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16519     
16520     title: 'Fill in a title',
16521     html: false,
16522     
16523     placement : 'right',
16524     trigger : 'hover', // hover
16525     
16526     delay : 0,
16527     
16528     over: 'parent',
16529     
16530     can_build_overlaid : false,
16531     
16532     getChildContainer : function()
16533     {
16534         return this.el.select('.popover-content',true).first();
16535     },
16536     
16537     getAutoCreate : function(){
16538          
16539         var cfg = {
16540            cls : 'popover roo-dynamic',
16541            style: 'display:block',
16542            cn : [
16543                 {
16544                     cls : 'arrow'
16545                 },
16546                 {
16547                     cls : 'popover-inner',
16548                     cn : [
16549                         {
16550                             tag: 'h3',
16551                             cls: 'popover-title',
16552                             html : this.title
16553                         },
16554                         {
16555                             cls : 'popover-content',
16556                             html : this.html
16557                         }
16558                     ]
16559                     
16560                 }
16561            ]
16562         };
16563         
16564         return cfg;
16565     },
16566     setTitle: function(str)
16567     {
16568         this.title = str;
16569         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16570     },
16571     setContent: function(str)
16572     {
16573         this.html = str;
16574         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16575     },
16576     // as it get's added to the bottom of the page.
16577     onRender : function(ct, position)
16578     {
16579         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16580         if(!this.el){
16581             var cfg = Roo.apply({},  this.getAutoCreate());
16582             cfg.id = Roo.id();
16583             
16584             if (this.cls) {
16585                 cfg.cls += ' ' + this.cls;
16586             }
16587             if (this.style) {
16588                 cfg.style = this.style;
16589             }
16590             //Roo.log("adding to ");
16591             this.el = Roo.get(document.body).createChild(cfg, position);
16592 //            Roo.log(this.el);
16593         }
16594         this.initEvents();
16595     },
16596     
16597     initEvents : function()
16598     {
16599         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16600         this.el.enableDisplayMode('block');
16601         this.el.hide();
16602         if (this.over === false) {
16603             return; 
16604         }
16605         if (this.triggers === false) {
16606             return;
16607         }
16608         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16609         var triggers = this.trigger ? this.trigger.split(' ') : [];
16610         Roo.each(triggers, function(trigger) {
16611         
16612             if (trigger == 'click') {
16613                 on_el.on('click', this.toggle, this);
16614             } else if (trigger != 'manual') {
16615                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16616                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16617       
16618                 on_el.on(eventIn  ,this.enter, this);
16619                 on_el.on(eventOut, this.leave, this);
16620             }
16621         }, this);
16622         
16623     },
16624     
16625     
16626     // private
16627     timeout : null,
16628     hoverState : null,
16629     
16630     toggle : function () {
16631         this.hoverState == 'in' ? this.leave() : this.enter();
16632     },
16633     
16634     enter : function () {
16635         
16636         clearTimeout(this.timeout);
16637     
16638         this.hoverState = 'in';
16639     
16640         if (!this.delay || !this.delay.show) {
16641             this.show();
16642             return;
16643         }
16644         var _t = this;
16645         this.timeout = setTimeout(function () {
16646             if (_t.hoverState == 'in') {
16647                 _t.show();
16648             }
16649         }, this.delay.show)
16650     },
16651     
16652     leave : function() {
16653         clearTimeout(this.timeout);
16654     
16655         this.hoverState = 'out';
16656     
16657         if (!this.delay || !this.delay.hide) {
16658             this.hide();
16659             return;
16660         }
16661         var _t = this;
16662         this.timeout = setTimeout(function () {
16663             if (_t.hoverState == 'out') {
16664                 _t.hide();
16665             }
16666         }, this.delay.hide)
16667     },
16668     
16669     show : function (on_el)
16670     {
16671         if (!on_el) {
16672             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16673         }
16674         
16675         // set content.
16676         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16677         if (this.html !== false) {
16678             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16679         }
16680         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16681         if (!this.title.length) {
16682             this.el.select('.popover-title',true).hide();
16683         }
16684         
16685         var placement = typeof this.placement == 'function' ?
16686             this.placement.call(this, this.el, on_el) :
16687             this.placement;
16688             
16689         var autoToken = /\s?auto?\s?/i;
16690         var autoPlace = autoToken.test(placement);
16691         if (autoPlace) {
16692             placement = placement.replace(autoToken, '') || 'top';
16693         }
16694         
16695         //this.el.detach()
16696         //this.el.setXY([0,0]);
16697         this.el.show();
16698         this.el.dom.style.display='block';
16699         this.el.addClass(placement);
16700         
16701         //this.el.appendTo(on_el);
16702         
16703         var p = this.getPosition();
16704         var box = this.el.getBox();
16705         
16706         if (autoPlace) {
16707             // fixme..
16708         }
16709         var align = Roo.bootstrap.Popover.alignment[placement];
16710         this.el.alignTo(on_el, align[0],align[1]);
16711         //var arrow = this.el.select('.arrow',true).first();
16712         //arrow.set(align[2], 
16713         
16714         this.el.addClass('in');
16715         
16716         
16717         if (this.el.hasClass('fade')) {
16718             // fade it?
16719         }
16720         
16721         this.hoverState = 'in';
16722         
16723         this.fireEvent('show', this);
16724         
16725     },
16726     hide : function()
16727     {
16728         this.el.setXY([0,0]);
16729         this.el.removeClass('in');
16730         this.el.hide();
16731         this.hoverState = null;
16732         
16733         this.fireEvent('hide', this);
16734     }
16735     
16736 });
16737
16738 Roo.bootstrap.Popover.alignment = {
16739     'left' : ['r-l', [-10,0], 'right'],
16740     'right' : ['l-r', [10,0], 'left'],
16741     'bottom' : ['t-b', [0,10], 'top'],
16742     'top' : [ 'b-t', [0,-10], 'bottom']
16743 };
16744
16745  /*
16746  * - LGPL
16747  *
16748  * Progress
16749  * 
16750  */
16751
16752 /**
16753  * @class Roo.bootstrap.Progress
16754  * @extends Roo.bootstrap.Component
16755  * Bootstrap Progress class
16756  * @cfg {Boolean} striped striped of the progress bar
16757  * @cfg {Boolean} active animated of the progress bar
16758  * 
16759  * 
16760  * @constructor
16761  * Create a new Progress
16762  * @param {Object} config The config object
16763  */
16764
16765 Roo.bootstrap.Progress = function(config){
16766     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16767 };
16768
16769 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16770     
16771     striped : false,
16772     active: false,
16773     
16774     getAutoCreate : function(){
16775         var cfg = {
16776             tag: 'div',
16777             cls: 'progress'
16778         };
16779         
16780         
16781         if(this.striped){
16782             cfg.cls += ' progress-striped';
16783         }
16784       
16785         if(this.active){
16786             cfg.cls += ' active';
16787         }
16788         
16789         
16790         return cfg;
16791     }
16792    
16793 });
16794
16795  
16796
16797  /*
16798  * - LGPL
16799  *
16800  * ProgressBar
16801  * 
16802  */
16803
16804 /**
16805  * @class Roo.bootstrap.ProgressBar
16806  * @extends Roo.bootstrap.Component
16807  * Bootstrap ProgressBar class
16808  * @cfg {Number} aria_valuenow aria-value now
16809  * @cfg {Number} aria_valuemin aria-value min
16810  * @cfg {Number} aria_valuemax aria-value max
16811  * @cfg {String} label label for the progress bar
16812  * @cfg {String} panel (success | info | warning | danger )
16813  * @cfg {String} role role of the progress bar
16814  * @cfg {String} sr_only text
16815  * 
16816  * 
16817  * @constructor
16818  * Create a new ProgressBar
16819  * @param {Object} config The config object
16820  */
16821
16822 Roo.bootstrap.ProgressBar = function(config){
16823     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16824 };
16825
16826 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16827     
16828     aria_valuenow : 0,
16829     aria_valuemin : 0,
16830     aria_valuemax : 100,
16831     label : false,
16832     panel : false,
16833     role : false,
16834     sr_only: false,
16835     
16836     getAutoCreate : function()
16837     {
16838         
16839         var cfg = {
16840             tag: 'div',
16841             cls: 'progress-bar',
16842             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16843         };
16844         
16845         if(this.sr_only){
16846             cfg.cn = {
16847                 tag: 'span',
16848                 cls: 'sr-only',
16849                 html: this.sr_only
16850             }
16851         }
16852         
16853         if(this.role){
16854             cfg.role = this.role;
16855         }
16856         
16857         if(this.aria_valuenow){
16858             cfg['aria-valuenow'] = this.aria_valuenow;
16859         }
16860         
16861         if(this.aria_valuemin){
16862             cfg['aria-valuemin'] = this.aria_valuemin;
16863         }
16864         
16865         if(this.aria_valuemax){
16866             cfg['aria-valuemax'] = this.aria_valuemax;
16867         }
16868         
16869         if(this.label && !this.sr_only){
16870             cfg.html = this.label;
16871         }
16872         
16873         if(this.panel){
16874             cfg.cls += ' progress-bar-' + this.panel;
16875         }
16876         
16877         return cfg;
16878     },
16879     
16880     update : function(aria_valuenow)
16881     {
16882         this.aria_valuenow = aria_valuenow;
16883         
16884         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16885     }
16886    
16887 });
16888
16889  
16890
16891  /*
16892  * - LGPL
16893  *
16894  * column
16895  * 
16896  */
16897
16898 /**
16899  * @class Roo.bootstrap.TabGroup
16900  * @extends Roo.bootstrap.Column
16901  * Bootstrap Column class
16902  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16903  * @cfg {Boolean} carousel true to make the group behave like a carousel
16904  * @cfg {Boolean} bullets show bullets for the panels
16905  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16906  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16907  * @cfg {Boolean} showarrow (true|false) show arrow default true
16908  * 
16909  * @constructor
16910  * Create a new TabGroup
16911  * @param {Object} config The config object
16912  */
16913
16914 Roo.bootstrap.TabGroup = function(config){
16915     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16916     if (!this.navId) {
16917         this.navId = Roo.id();
16918     }
16919     this.tabs = [];
16920     Roo.bootstrap.TabGroup.register(this);
16921     
16922 };
16923
16924 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16925     
16926     carousel : false,
16927     transition : false,
16928     bullets : 0,
16929     timer : 0,
16930     autoslide : false,
16931     slideFn : false,
16932     slideOnTouch : false,
16933     showarrow : true,
16934     
16935     getAutoCreate : function()
16936     {
16937         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16938         
16939         cfg.cls += ' tab-content';
16940         
16941         if (this.carousel) {
16942             cfg.cls += ' carousel slide';
16943             
16944             cfg.cn = [{
16945                cls : 'carousel-inner',
16946                cn : []
16947             }];
16948         
16949             if(this.bullets  && !Roo.isTouch){
16950                 
16951                 var bullets = {
16952                     cls : 'carousel-bullets',
16953                     cn : []
16954                 };
16955                
16956                 if(this.bullets_cls){
16957                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16958                 }
16959                 
16960                 bullets.cn.push({
16961                     cls : 'clear'
16962                 });
16963                 
16964                 cfg.cn[0].cn.push(bullets);
16965             }
16966             
16967             if(this.showarrow){
16968                 cfg.cn[0].cn.push({
16969                     tag : 'div',
16970                     class : 'carousel-arrow',
16971                     cn : [
16972                         {
16973                             tag : 'div',
16974                             class : 'carousel-prev',
16975                             cn : [
16976                                 {
16977                                     tag : 'i',
16978                                     class : 'fa fa-chevron-left'
16979                                 }
16980                             ]
16981                         },
16982                         {
16983                             tag : 'div',
16984                             class : 'carousel-next',
16985                             cn : [
16986                                 {
16987                                     tag : 'i',
16988                                     class : 'fa fa-chevron-right'
16989                                 }
16990                             ]
16991                         }
16992                     ]
16993                 });
16994             }
16995             
16996         }
16997         
16998         return cfg;
16999     },
17000     
17001     initEvents:  function()
17002     {
17003 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17004 //            this.el.on("touchstart", this.onTouchStart, this);
17005 //        }
17006         
17007         if(this.autoslide){
17008             var _this = this;
17009             
17010             this.slideFn = window.setInterval(function() {
17011                 _this.showPanelNext();
17012             }, this.timer);
17013         }
17014         
17015         if(this.showarrow){
17016             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17017             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17018         }
17019         
17020         
17021     },
17022     
17023 //    onTouchStart : function(e, el, o)
17024 //    {
17025 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17026 //            return;
17027 //        }
17028 //        
17029 //        this.showPanelNext();
17030 //    },
17031     
17032     
17033     getChildContainer : function()
17034     {
17035         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17036     },
17037     
17038     /**
17039     * register a Navigation item
17040     * @param {Roo.bootstrap.NavItem} the navitem to add
17041     */
17042     register : function(item)
17043     {
17044         this.tabs.push( item);
17045         item.navId = this.navId; // not really needed..
17046         this.addBullet();
17047     
17048     },
17049     
17050     getActivePanel : function()
17051     {
17052         var r = false;
17053         Roo.each(this.tabs, function(t) {
17054             if (t.active) {
17055                 r = t;
17056                 return false;
17057             }
17058             return null;
17059         });
17060         return r;
17061         
17062     },
17063     getPanelByName : function(n)
17064     {
17065         var r = false;
17066         Roo.each(this.tabs, function(t) {
17067             if (t.tabId == n) {
17068                 r = t;
17069                 return false;
17070             }
17071             return null;
17072         });
17073         return r;
17074     },
17075     indexOfPanel : function(p)
17076     {
17077         var r = false;
17078         Roo.each(this.tabs, function(t,i) {
17079             if (t.tabId == p.tabId) {
17080                 r = i;
17081                 return false;
17082             }
17083             return null;
17084         });
17085         return r;
17086     },
17087     /**
17088      * show a specific panel
17089      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17090      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17091      */
17092     showPanel : function (pan)
17093     {
17094         if(this.transition || typeof(pan) == 'undefined'){
17095             Roo.log("waiting for the transitionend");
17096             return;
17097         }
17098         
17099         if (typeof(pan) == 'number') {
17100             pan = this.tabs[pan];
17101         }
17102         
17103         if (typeof(pan) == 'string') {
17104             pan = this.getPanelByName(pan);
17105         }
17106         
17107         var cur = this.getActivePanel();
17108         
17109         if(!pan || !cur){
17110             Roo.log('pan or acitve pan is undefined');
17111             return false;
17112         }
17113         
17114         if (pan.tabId == this.getActivePanel().tabId) {
17115             return true;
17116         }
17117         
17118         if (false === cur.fireEvent('beforedeactivate')) {
17119             return false;
17120         }
17121         
17122         if(this.bullets > 0 && !Roo.isTouch){
17123             this.setActiveBullet(this.indexOfPanel(pan));
17124         }
17125         
17126         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17127             
17128             this.transition = true;
17129             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17130             var lr = dir == 'next' ? 'left' : 'right';
17131             pan.el.addClass(dir); // or prev
17132             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17133             cur.el.addClass(lr); // or right
17134             pan.el.addClass(lr);
17135             
17136             var _this = this;
17137             cur.el.on('transitionend', function() {
17138                 Roo.log("trans end?");
17139                 
17140                 pan.el.removeClass([lr,dir]);
17141                 pan.setActive(true);
17142                 
17143                 cur.el.removeClass([lr]);
17144                 cur.setActive(false);
17145                 
17146                 _this.transition = false;
17147                 
17148             }, this, { single:  true } );
17149             
17150             return true;
17151         }
17152         
17153         cur.setActive(false);
17154         pan.setActive(true);
17155         
17156         return true;
17157         
17158     },
17159     showPanelNext : function()
17160     {
17161         var i = this.indexOfPanel(this.getActivePanel());
17162         
17163         if (i >= this.tabs.length - 1 && !this.autoslide) {
17164             return;
17165         }
17166         
17167         if (i >= this.tabs.length - 1 && this.autoslide) {
17168             i = -1;
17169         }
17170         
17171         this.showPanel(this.tabs[i+1]);
17172     },
17173     
17174     showPanelPrev : function()
17175     {
17176         var i = this.indexOfPanel(this.getActivePanel());
17177         
17178         if (i  < 1 && !this.autoslide) {
17179             return;
17180         }
17181         
17182         if (i < 1 && this.autoslide) {
17183             i = this.tabs.length;
17184         }
17185         
17186         this.showPanel(this.tabs[i-1]);
17187     },
17188     
17189     
17190     addBullet: function()
17191     {
17192         if(!this.bullets || Roo.isTouch){
17193             return;
17194         }
17195         var ctr = this.el.select('.carousel-bullets',true).first();
17196         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17197         var bullet = ctr.createChild({
17198             cls : 'bullet bullet-' + i
17199         },ctr.dom.lastChild);
17200         
17201         
17202         var _this = this;
17203         
17204         bullet.on('click', (function(e, el, o, ii, t){
17205
17206             e.preventDefault();
17207
17208             this.showPanel(ii);
17209
17210             if(this.autoslide && this.slideFn){
17211                 clearInterval(this.slideFn);
17212                 this.slideFn = window.setInterval(function() {
17213                     _this.showPanelNext();
17214                 }, this.timer);
17215             }
17216
17217         }).createDelegate(this, [i, bullet], true));
17218                 
17219         
17220     },
17221      
17222     setActiveBullet : function(i)
17223     {
17224         if(Roo.isTouch){
17225             return;
17226         }
17227         
17228         Roo.each(this.el.select('.bullet', true).elements, function(el){
17229             el.removeClass('selected');
17230         });
17231
17232         var bullet = this.el.select('.bullet-' + i, true).first();
17233         
17234         if(!bullet){
17235             return;
17236         }
17237         
17238         bullet.addClass('selected');
17239     }
17240     
17241     
17242   
17243 });
17244
17245  
17246
17247  
17248  
17249 Roo.apply(Roo.bootstrap.TabGroup, {
17250     
17251     groups: {},
17252      /**
17253     * register a Navigation Group
17254     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17255     */
17256     register : function(navgrp)
17257     {
17258         this.groups[navgrp.navId] = navgrp;
17259         
17260     },
17261     /**
17262     * fetch a Navigation Group based on the navigation ID
17263     * if one does not exist , it will get created.
17264     * @param {string} the navgroup to add
17265     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17266     */
17267     get: function(navId) {
17268         if (typeof(this.groups[navId]) == 'undefined') {
17269             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17270         }
17271         return this.groups[navId] ;
17272     }
17273     
17274     
17275     
17276 });
17277
17278  /*
17279  * - LGPL
17280  *
17281  * TabPanel
17282  * 
17283  */
17284
17285 /**
17286  * @class Roo.bootstrap.TabPanel
17287  * @extends Roo.bootstrap.Component
17288  * Bootstrap TabPanel class
17289  * @cfg {Boolean} active panel active
17290  * @cfg {String} html panel content
17291  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17292  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17293  * @cfg {String} href click to link..
17294  * 
17295  * 
17296  * @constructor
17297  * Create a new TabPanel
17298  * @param {Object} config The config object
17299  */
17300
17301 Roo.bootstrap.TabPanel = function(config){
17302     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17303     this.addEvents({
17304         /**
17305              * @event changed
17306              * Fires when the active status changes
17307              * @param {Roo.bootstrap.TabPanel} this
17308              * @param {Boolean} state the new state
17309             
17310          */
17311         'changed': true,
17312         /**
17313              * @event beforedeactivate
17314              * Fires before a tab is de-activated - can be used to do validation on a form.
17315              * @param {Roo.bootstrap.TabPanel} this
17316              * @return {Boolean} false if there is an error
17317             
17318          */
17319         'beforedeactivate': true
17320      });
17321     
17322     this.tabId = this.tabId || Roo.id();
17323   
17324 };
17325
17326 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17327     
17328     active: false,
17329     html: false,
17330     tabId: false,
17331     navId : false,
17332     href : '',
17333     
17334     getAutoCreate : function(){
17335         var cfg = {
17336             tag: 'div',
17337             // item is needed for carousel - not sure if it has any effect otherwise
17338             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17339             html: this.html || ''
17340         };
17341         
17342         if(this.active){
17343             cfg.cls += ' active';
17344         }
17345         
17346         if(this.tabId){
17347             cfg.tabId = this.tabId;
17348         }
17349         
17350         
17351         return cfg;
17352     },
17353     
17354     initEvents:  function()
17355     {
17356         var p = this.parent();
17357         
17358         this.navId = this.navId || p.navId;
17359         
17360         if (typeof(this.navId) != 'undefined') {
17361             // not really needed.. but just in case.. parent should be a NavGroup.
17362             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17363             
17364             tg.register(this);
17365             
17366             var i = tg.tabs.length - 1;
17367             
17368             if(this.active && tg.bullets > 0 && i < tg.bullets){
17369                 tg.setActiveBullet(i);
17370             }
17371         }
17372         
17373         this.el.on('click', this.onClick, this);
17374         
17375         if(Roo.isTouch){
17376             this.el.on("touchstart", this.onTouchStart, this);
17377             this.el.on("touchmove", this.onTouchMove, this);
17378             this.el.on("touchend", this.onTouchEnd, this);
17379         }
17380         
17381     },
17382     
17383     onRender : function(ct, position)
17384     {
17385         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17386     },
17387     
17388     setActive : function(state)
17389     {
17390         Roo.log("panel - set active " + this.tabId + "=" + state);
17391         
17392         this.active = state;
17393         if (!state) {
17394             this.el.removeClass('active');
17395             
17396         } else  if (!this.el.hasClass('active')) {
17397             this.el.addClass('active');
17398         }
17399         
17400         this.fireEvent('changed', this, state);
17401     },
17402     
17403     onClick : function(e)
17404     {
17405         e.preventDefault();
17406         
17407         if(!this.href.length){
17408             return;
17409         }
17410         
17411         window.location.href = this.href;
17412     },
17413     
17414     startX : 0,
17415     startY : 0,
17416     endX : 0,
17417     endY : 0,
17418     swiping : false,
17419     
17420     onTouchStart : function(e)
17421     {
17422         this.swiping = false;
17423         
17424         this.startX = e.browserEvent.touches[0].clientX;
17425         this.startY = e.browserEvent.touches[0].clientY;
17426     },
17427     
17428     onTouchMove : function(e)
17429     {
17430         this.swiping = true;
17431         
17432         this.endX = e.browserEvent.touches[0].clientX;
17433         this.endY = e.browserEvent.touches[0].clientY;
17434     },
17435     
17436     onTouchEnd : function(e)
17437     {
17438         if(!this.swiping){
17439             this.onClick(e);
17440             return;
17441         }
17442         
17443         var tabGroup = this.parent();
17444         
17445         if(this.endX > this.startX){ // swiping right
17446             tabGroup.showPanelPrev();
17447             return;
17448         }
17449         
17450         if(this.startX > this.endX){ // swiping left
17451             tabGroup.showPanelNext();
17452             return;
17453         }
17454     }
17455     
17456     
17457 });
17458  
17459
17460  
17461
17462  /*
17463  * - LGPL
17464  *
17465  * DateField
17466  * 
17467  */
17468
17469 /**
17470  * @class Roo.bootstrap.DateField
17471  * @extends Roo.bootstrap.Input
17472  * Bootstrap DateField class
17473  * @cfg {Number} weekStart default 0
17474  * @cfg {String} viewMode default empty, (months|years)
17475  * @cfg {String} minViewMode default empty, (months|years)
17476  * @cfg {Number} startDate default -Infinity
17477  * @cfg {Number} endDate default Infinity
17478  * @cfg {Boolean} todayHighlight default false
17479  * @cfg {Boolean} todayBtn default false
17480  * @cfg {Boolean} calendarWeeks default false
17481  * @cfg {Object} daysOfWeekDisabled default empty
17482  * @cfg {Boolean} singleMode default false (true | false)
17483  * 
17484  * @cfg {Boolean} keyboardNavigation default true
17485  * @cfg {String} language default en
17486  * 
17487  * @constructor
17488  * Create a new DateField
17489  * @param {Object} config The config object
17490  */
17491
17492 Roo.bootstrap.DateField = function(config){
17493     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17494      this.addEvents({
17495             /**
17496              * @event show
17497              * Fires when this field show.
17498              * @param {Roo.bootstrap.DateField} this
17499              * @param {Mixed} date The date value
17500              */
17501             show : true,
17502             /**
17503              * @event show
17504              * Fires when this field hide.
17505              * @param {Roo.bootstrap.DateField} this
17506              * @param {Mixed} date The date value
17507              */
17508             hide : true,
17509             /**
17510              * @event select
17511              * Fires when select a date.
17512              * @param {Roo.bootstrap.DateField} this
17513              * @param {Mixed} date The date value
17514              */
17515             select : true,
17516             /**
17517              * @event beforeselect
17518              * Fires when before select a date.
17519              * @param {Roo.bootstrap.DateField} this
17520              * @param {Mixed} date The date value
17521              */
17522             beforeselect : true
17523         });
17524 };
17525
17526 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17527     
17528     /**
17529      * @cfg {String} format
17530      * The default date format string which can be overriden for localization support.  The format must be
17531      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17532      */
17533     format : "m/d/y",
17534     /**
17535      * @cfg {String} altFormats
17536      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17537      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17538      */
17539     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17540     
17541     weekStart : 0,
17542     
17543     viewMode : '',
17544     
17545     minViewMode : '',
17546     
17547     todayHighlight : false,
17548     
17549     todayBtn: false,
17550     
17551     language: 'en',
17552     
17553     keyboardNavigation: true,
17554     
17555     calendarWeeks: false,
17556     
17557     startDate: -Infinity,
17558     
17559     endDate: Infinity,
17560     
17561     daysOfWeekDisabled: [],
17562     
17563     _events: [],
17564     
17565     singleMode : false,
17566     
17567     UTCDate: function()
17568     {
17569         return new Date(Date.UTC.apply(Date, arguments));
17570     },
17571     
17572     UTCToday: function()
17573     {
17574         var today = new Date();
17575         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17576     },
17577     
17578     getDate: function() {
17579             var d = this.getUTCDate();
17580             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17581     },
17582     
17583     getUTCDate: function() {
17584             return this.date;
17585     },
17586     
17587     setDate: function(d) {
17588             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17589     },
17590     
17591     setUTCDate: function(d) {
17592             this.date = d;
17593             this.setValue(this.formatDate(this.date));
17594     },
17595         
17596     onRender: function(ct, position)
17597     {
17598         
17599         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17600         
17601         this.language = this.language || 'en';
17602         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17603         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17604         
17605         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17606         this.format = this.format || 'm/d/y';
17607         this.isInline = false;
17608         this.isInput = true;
17609         this.component = this.el.select('.add-on', true).first() || false;
17610         this.component = (this.component && this.component.length === 0) ? false : this.component;
17611         this.hasInput = this.component && this.inputEl().length;
17612         
17613         if (typeof(this.minViewMode === 'string')) {
17614             switch (this.minViewMode) {
17615                 case 'months':
17616                     this.minViewMode = 1;
17617                     break;
17618                 case 'years':
17619                     this.minViewMode = 2;
17620                     break;
17621                 default:
17622                     this.minViewMode = 0;
17623                     break;
17624             }
17625         }
17626         
17627         if (typeof(this.viewMode === 'string')) {
17628             switch (this.viewMode) {
17629                 case 'months':
17630                     this.viewMode = 1;
17631                     break;
17632                 case 'years':
17633                     this.viewMode = 2;
17634                     break;
17635                 default:
17636                     this.viewMode = 0;
17637                     break;
17638             }
17639         }
17640                 
17641         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17642         
17643 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17644         
17645         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17646         
17647         this.picker().on('mousedown', this.onMousedown, this);
17648         this.picker().on('click', this.onClick, this);
17649         
17650         this.picker().addClass('datepicker-dropdown');
17651         
17652         this.startViewMode = this.viewMode;
17653         
17654         if(this.singleMode){
17655             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17656                 v.setVisibilityMode(Roo.Element.DISPLAY);
17657                 v.hide();
17658             });
17659             
17660             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17661                 v.setStyle('width', '189px');
17662             });
17663         }
17664         
17665         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17666             if(!this.calendarWeeks){
17667                 v.remove();
17668                 return;
17669             }
17670             
17671             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17672             v.attr('colspan', function(i, val){
17673                 return parseInt(val) + 1;
17674             });
17675         });
17676                         
17677         
17678         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17679         
17680         this.setStartDate(this.startDate);
17681         this.setEndDate(this.endDate);
17682         
17683         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17684         
17685         this.fillDow();
17686         this.fillMonths();
17687         this.update();
17688         this.showMode();
17689         
17690         if(this.isInline) {
17691             this.show();
17692         }
17693     },
17694     
17695     picker : function()
17696     {
17697         return this.pickerEl;
17698 //        return this.el.select('.datepicker', true).first();
17699     },
17700     
17701     fillDow: function()
17702     {
17703         var dowCnt = this.weekStart;
17704         
17705         var dow = {
17706             tag: 'tr',
17707             cn: [
17708                 
17709             ]
17710         };
17711         
17712         if(this.calendarWeeks){
17713             dow.cn.push({
17714                 tag: 'th',
17715                 cls: 'cw',
17716                 html: '&nbsp;'
17717             })
17718         }
17719         
17720         while (dowCnt < this.weekStart + 7) {
17721             dow.cn.push({
17722                 tag: 'th',
17723                 cls: 'dow',
17724                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17725             });
17726         }
17727         
17728         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17729     },
17730     
17731     fillMonths: function()
17732     {    
17733         var i = 0;
17734         var months = this.picker().select('>.datepicker-months td', true).first();
17735         
17736         months.dom.innerHTML = '';
17737         
17738         while (i < 12) {
17739             var month = {
17740                 tag: 'span',
17741                 cls: 'month',
17742                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17743             };
17744             
17745             months.createChild(month);
17746         }
17747         
17748     },
17749     
17750     update: function()
17751     {
17752         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;
17753         
17754         if (this.date < this.startDate) {
17755             this.viewDate = new Date(this.startDate);
17756         } else if (this.date > this.endDate) {
17757             this.viewDate = new Date(this.endDate);
17758         } else {
17759             this.viewDate = new Date(this.date);
17760         }
17761         
17762         this.fill();
17763     },
17764     
17765     fill: function() 
17766     {
17767         var d = new Date(this.viewDate),
17768                 year = d.getUTCFullYear(),
17769                 month = d.getUTCMonth(),
17770                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17771                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17772                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17773                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17774                 currentDate = this.date && this.date.valueOf(),
17775                 today = this.UTCToday();
17776         
17777         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17778         
17779 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17780         
17781 //        this.picker.select('>tfoot th.today').
17782 //                                              .text(dates[this.language].today)
17783 //                                              .toggle(this.todayBtn !== false);
17784     
17785         this.updateNavArrows();
17786         this.fillMonths();
17787                                                 
17788         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17789         
17790         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17791          
17792         prevMonth.setUTCDate(day);
17793         
17794         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17795         
17796         var nextMonth = new Date(prevMonth);
17797         
17798         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17799         
17800         nextMonth = nextMonth.valueOf();
17801         
17802         var fillMonths = false;
17803         
17804         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17805         
17806         while(prevMonth.valueOf() < nextMonth) {
17807             var clsName = '';
17808             
17809             if (prevMonth.getUTCDay() === this.weekStart) {
17810                 if(fillMonths){
17811                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17812                 }
17813                     
17814                 fillMonths = {
17815                     tag: 'tr',
17816                     cn: []
17817                 };
17818                 
17819                 if(this.calendarWeeks){
17820                     // ISO 8601: First week contains first thursday.
17821                     // ISO also states week starts on Monday, but we can be more abstract here.
17822                     var
17823                     // Start of current week: based on weekstart/current date
17824                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17825                     // Thursday of this week
17826                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17827                     // First Thursday of year, year from thursday
17828                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17829                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17830                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17831                     
17832                     fillMonths.cn.push({
17833                         tag: 'td',
17834                         cls: 'cw',
17835                         html: calWeek
17836                     });
17837                 }
17838             }
17839             
17840             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17841                 clsName += ' old';
17842             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17843                 clsName += ' new';
17844             }
17845             if (this.todayHighlight &&
17846                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17847                 prevMonth.getUTCMonth() == today.getMonth() &&
17848                 prevMonth.getUTCDate() == today.getDate()) {
17849                 clsName += ' today';
17850             }
17851             
17852             if (currentDate && prevMonth.valueOf() === currentDate) {
17853                 clsName += ' active';
17854             }
17855             
17856             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17857                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17858                     clsName += ' disabled';
17859             }
17860             
17861             fillMonths.cn.push({
17862                 tag: 'td',
17863                 cls: 'day ' + clsName,
17864                 html: prevMonth.getDate()
17865             });
17866             
17867             prevMonth.setDate(prevMonth.getDate()+1);
17868         }
17869           
17870         var currentYear = this.date && this.date.getUTCFullYear();
17871         var currentMonth = this.date && this.date.getUTCMonth();
17872         
17873         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17874         
17875         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17876             v.removeClass('active');
17877             
17878             if(currentYear === year && k === currentMonth){
17879                 v.addClass('active');
17880             }
17881             
17882             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17883                 v.addClass('disabled');
17884             }
17885             
17886         });
17887         
17888         
17889         year = parseInt(year/10, 10) * 10;
17890         
17891         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17892         
17893         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17894         
17895         year -= 1;
17896         for (var i = -1; i < 11; i++) {
17897             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17898                 tag: 'span',
17899                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17900                 html: year
17901             });
17902             
17903             year += 1;
17904         }
17905     },
17906     
17907     showMode: function(dir) 
17908     {
17909         if (dir) {
17910             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17911         }
17912         
17913         Roo.each(this.picker().select('>div',true).elements, function(v){
17914             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17915             v.hide();
17916         });
17917         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17918     },
17919     
17920     place: function()
17921     {
17922         if(this.isInline) {
17923             return;
17924         }
17925         
17926         this.picker().removeClass(['bottom', 'top']);
17927         
17928         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17929             /*
17930              * place to the top of element!
17931              *
17932              */
17933             
17934             this.picker().addClass('top');
17935             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17936             
17937             return;
17938         }
17939         
17940         this.picker().addClass('bottom');
17941         
17942         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17943     },
17944     
17945     parseDate : function(value)
17946     {
17947         if(!value || value instanceof Date){
17948             return value;
17949         }
17950         var v = Date.parseDate(value, this.format);
17951         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17952             v = Date.parseDate(value, 'Y-m-d');
17953         }
17954         if(!v && this.altFormats){
17955             if(!this.altFormatsArray){
17956                 this.altFormatsArray = this.altFormats.split("|");
17957             }
17958             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17959                 v = Date.parseDate(value, this.altFormatsArray[i]);
17960             }
17961         }
17962         return v;
17963     },
17964     
17965     formatDate : function(date, fmt)
17966     {   
17967         return (!date || !(date instanceof Date)) ?
17968         date : date.dateFormat(fmt || this.format);
17969     },
17970     
17971     onFocus : function()
17972     {
17973         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17974         this.show();
17975     },
17976     
17977     onBlur : function()
17978     {
17979         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17980         
17981         var d = this.inputEl().getValue();
17982         
17983         this.setValue(d);
17984                 
17985         this.hide();
17986     },
17987     
17988     show : function()
17989     {
17990         this.picker().show();
17991         this.update();
17992         this.place();
17993         
17994         this.fireEvent('show', this, this.date);
17995     },
17996     
17997     hide : function()
17998     {
17999         if(this.isInline) {
18000             return;
18001         }
18002         this.picker().hide();
18003         this.viewMode = this.startViewMode;
18004         this.showMode();
18005         
18006         this.fireEvent('hide', this, this.date);
18007         
18008     },
18009     
18010     onMousedown: function(e)
18011     {
18012         e.stopPropagation();
18013         e.preventDefault();
18014     },
18015     
18016     keyup: function(e)
18017     {
18018         Roo.bootstrap.DateField.superclass.keyup.call(this);
18019         this.update();
18020     },
18021
18022     setValue: function(v)
18023     {
18024         if(this.fireEvent('beforeselect', this, v) !== false){
18025             var d = new Date(this.parseDate(v) ).clearTime();
18026         
18027             if(isNaN(d.getTime())){
18028                 this.date = this.viewDate = '';
18029                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18030                 return;
18031             }
18032
18033             v = this.formatDate(d);
18034
18035             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18036
18037             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18038
18039             this.update();
18040
18041             this.fireEvent('select', this, this.date);
18042         }
18043     },
18044     
18045     getValue: function()
18046     {
18047         return this.formatDate(this.date);
18048     },
18049     
18050     fireKey: function(e)
18051     {
18052         if (!this.picker().isVisible()){
18053             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18054                 this.show();
18055             }
18056             return;
18057         }
18058         
18059         var dateChanged = false,
18060         dir, day, month,
18061         newDate, newViewDate;
18062         
18063         switch(e.keyCode){
18064             case 27: // escape
18065                 this.hide();
18066                 e.preventDefault();
18067                 break;
18068             case 37: // left
18069             case 39: // right
18070                 if (!this.keyboardNavigation) {
18071                     break;
18072                 }
18073                 dir = e.keyCode == 37 ? -1 : 1;
18074                 
18075                 if (e.ctrlKey){
18076                     newDate = this.moveYear(this.date, dir);
18077                     newViewDate = this.moveYear(this.viewDate, dir);
18078                 } else if (e.shiftKey){
18079                     newDate = this.moveMonth(this.date, dir);
18080                     newViewDate = this.moveMonth(this.viewDate, dir);
18081                 } else {
18082                     newDate = new Date(this.date);
18083                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18084                     newViewDate = new Date(this.viewDate);
18085                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18086                 }
18087                 if (this.dateWithinRange(newDate)){
18088                     this.date = newDate;
18089                     this.viewDate = newViewDate;
18090                     this.setValue(this.formatDate(this.date));
18091 //                    this.update();
18092                     e.preventDefault();
18093                     dateChanged = true;
18094                 }
18095                 break;
18096             case 38: // up
18097             case 40: // down
18098                 if (!this.keyboardNavigation) {
18099                     break;
18100                 }
18101                 dir = e.keyCode == 38 ? -1 : 1;
18102                 if (e.ctrlKey){
18103                     newDate = this.moveYear(this.date, dir);
18104                     newViewDate = this.moveYear(this.viewDate, dir);
18105                 } else if (e.shiftKey){
18106                     newDate = this.moveMonth(this.date, dir);
18107                     newViewDate = this.moveMonth(this.viewDate, dir);
18108                 } else {
18109                     newDate = new Date(this.date);
18110                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18111                     newViewDate = new Date(this.viewDate);
18112                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18113                 }
18114                 if (this.dateWithinRange(newDate)){
18115                     this.date = newDate;
18116                     this.viewDate = newViewDate;
18117                     this.setValue(this.formatDate(this.date));
18118 //                    this.update();
18119                     e.preventDefault();
18120                     dateChanged = true;
18121                 }
18122                 break;
18123             case 13: // enter
18124                 this.setValue(this.formatDate(this.date));
18125                 this.hide();
18126                 e.preventDefault();
18127                 break;
18128             case 9: // tab
18129                 this.setValue(this.formatDate(this.date));
18130                 this.hide();
18131                 break;
18132             case 16: // shift
18133             case 17: // ctrl
18134             case 18: // alt
18135                 break;
18136             default :
18137                 this.hide();
18138                 
18139         }
18140     },
18141     
18142     
18143     onClick: function(e) 
18144     {
18145         e.stopPropagation();
18146         e.preventDefault();
18147         
18148         var target = e.getTarget();
18149         
18150         if(target.nodeName.toLowerCase() === 'i'){
18151             target = Roo.get(target).dom.parentNode;
18152         }
18153         
18154         var nodeName = target.nodeName;
18155         var className = target.className;
18156         var html = target.innerHTML;
18157         //Roo.log(nodeName);
18158         
18159         switch(nodeName.toLowerCase()) {
18160             case 'th':
18161                 switch(className) {
18162                     case 'switch':
18163                         this.showMode(1);
18164                         break;
18165                     case 'prev':
18166                     case 'next':
18167                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18168                         switch(this.viewMode){
18169                                 case 0:
18170                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18171                                         break;
18172                                 case 1:
18173                                 case 2:
18174                                         this.viewDate = this.moveYear(this.viewDate, dir);
18175                                         break;
18176                         }
18177                         this.fill();
18178                         break;
18179                     case 'today':
18180                         var date = new Date();
18181                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18182 //                        this.fill()
18183                         this.setValue(this.formatDate(this.date));
18184                         
18185                         this.hide();
18186                         break;
18187                 }
18188                 break;
18189             case 'span':
18190                 if (className.indexOf('disabled') < 0) {
18191                     this.viewDate.setUTCDate(1);
18192                     if (className.indexOf('month') > -1) {
18193                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18194                     } else {
18195                         var year = parseInt(html, 10) || 0;
18196                         this.viewDate.setUTCFullYear(year);
18197                         
18198                     }
18199                     
18200                     if(this.singleMode){
18201                         this.setValue(this.formatDate(this.viewDate));
18202                         this.hide();
18203                         return;
18204                     }
18205                     
18206                     this.showMode(-1);
18207                     this.fill();
18208                 }
18209                 break;
18210                 
18211             case 'td':
18212                 //Roo.log(className);
18213                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18214                     var day = parseInt(html, 10) || 1;
18215                     var year = this.viewDate.getUTCFullYear(),
18216                         month = this.viewDate.getUTCMonth();
18217
18218                     if (className.indexOf('old') > -1) {
18219                         if(month === 0 ){
18220                             month = 11;
18221                             year -= 1;
18222                         }else{
18223                             month -= 1;
18224                         }
18225                     } else if (className.indexOf('new') > -1) {
18226                         if (month == 11) {
18227                             month = 0;
18228                             year += 1;
18229                         } else {
18230                             month += 1;
18231                         }
18232                     }
18233                     //Roo.log([year,month,day]);
18234                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18235                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18236 //                    this.fill();
18237                     //Roo.log(this.formatDate(this.date));
18238                     this.setValue(this.formatDate(this.date));
18239                     this.hide();
18240                 }
18241                 break;
18242         }
18243     },
18244     
18245     setStartDate: function(startDate)
18246     {
18247         this.startDate = startDate || -Infinity;
18248         if (this.startDate !== -Infinity) {
18249             this.startDate = this.parseDate(this.startDate);
18250         }
18251         this.update();
18252         this.updateNavArrows();
18253     },
18254
18255     setEndDate: function(endDate)
18256     {
18257         this.endDate = endDate || Infinity;
18258         if (this.endDate !== Infinity) {
18259             this.endDate = this.parseDate(this.endDate);
18260         }
18261         this.update();
18262         this.updateNavArrows();
18263     },
18264     
18265     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18266     {
18267         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18268         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18269             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18270         }
18271         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18272             return parseInt(d, 10);
18273         });
18274         this.update();
18275         this.updateNavArrows();
18276     },
18277     
18278     updateNavArrows: function() 
18279     {
18280         if(this.singleMode){
18281             return;
18282         }
18283         
18284         var d = new Date(this.viewDate),
18285         year = d.getUTCFullYear(),
18286         month = d.getUTCMonth();
18287         
18288         Roo.each(this.picker().select('.prev', true).elements, function(v){
18289             v.show();
18290             switch (this.viewMode) {
18291                 case 0:
18292
18293                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18294                         v.hide();
18295                     }
18296                     break;
18297                 case 1:
18298                 case 2:
18299                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18300                         v.hide();
18301                     }
18302                     break;
18303             }
18304         });
18305         
18306         Roo.each(this.picker().select('.next', true).elements, function(v){
18307             v.show();
18308             switch (this.viewMode) {
18309                 case 0:
18310
18311                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18312                         v.hide();
18313                     }
18314                     break;
18315                 case 1:
18316                 case 2:
18317                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18318                         v.hide();
18319                     }
18320                     break;
18321             }
18322         })
18323     },
18324     
18325     moveMonth: function(date, dir)
18326     {
18327         if (!dir) {
18328             return date;
18329         }
18330         var new_date = new Date(date.valueOf()),
18331         day = new_date.getUTCDate(),
18332         month = new_date.getUTCMonth(),
18333         mag = Math.abs(dir),
18334         new_month, test;
18335         dir = dir > 0 ? 1 : -1;
18336         if (mag == 1){
18337             test = dir == -1
18338             // If going back one month, make sure month is not current month
18339             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18340             ? function(){
18341                 return new_date.getUTCMonth() == month;
18342             }
18343             // If going forward one month, make sure month is as expected
18344             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18345             : function(){
18346                 return new_date.getUTCMonth() != new_month;
18347             };
18348             new_month = month + dir;
18349             new_date.setUTCMonth(new_month);
18350             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18351             if (new_month < 0 || new_month > 11) {
18352                 new_month = (new_month + 12) % 12;
18353             }
18354         } else {
18355             // For magnitudes >1, move one month at a time...
18356             for (var i=0; i<mag; i++) {
18357                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18358                 new_date = this.moveMonth(new_date, dir);
18359             }
18360             // ...then reset the day, keeping it in the new month
18361             new_month = new_date.getUTCMonth();
18362             new_date.setUTCDate(day);
18363             test = function(){
18364                 return new_month != new_date.getUTCMonth();
18365             };
18366         }
18367         // Common date-resetting loop -- if date is beyond end of month, make it
18368         // end of month
18369         while (test()){
18370             new_date.setUTCDate(--day);
18371             new_date.setUTCMonth(new_month);
18372         }
18373         return new_date;
18374     },
18375
18376     moveYear: function(date, dir)
18377     {
18378         return this.moveMonth(date, dir*12);
18379     },
18380
18381     dateWithinRange: function(date)
18382     {
18383         return date >= this.startDate && date <= this.endDate;
18384     },
18385
18386     
18387     remove: function() 
18388     {
18389         this.picker().remove();
18390     },
18391     
18392     validateValue : function(value)
18393     {
18394         if(value.length < 1)  {
18395             if(this.allowBlank){
18396                 return true;
18397             }
18398             return false;
18399         }
18400         
18401         if(value.length < this.minLength){
18402             return false;
18403         }
18404         if(value.length > this.maxLength){
18405             return false;
18406         }
18407         if(this.vtype){
18408             var vt = Roo.form.VTypes;
18409             if(!vt[this.vtype](value, this)){
18410                 return false;
18411             }
18412         }
18413         if(typeof this.validator == "function"){
18414             var msg = this.validator(value);
18415             if(msg !== true){
18416                 return false;
18417             }
18418         }
18419         
18420         if(this.regex && !this.regex.test(value)){
18421             return false;
18422         }
18423         
18424         if(typeof(this.parseDate(value)) == 'undefined'){
18425             return false;
18426         }
18427         
18428         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18429             return false;
18430         }      
18431         
18432         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18433             return false;
18434         } 
18435         
18436         
18437         return true;
18438     }
18439    
18440 });
18441
18442 Roo.apply(Roo.bootstrap.DateField,  {
18443     
18444     head : {
18445         tag: 'thead',
18446         cn: [
18447         {
18448             tag: 'tr',
18449             cn: [
18450             {
18451                 tag: 'th',
18452                 cls: 'prev',
18453                 html: '<i class="fa fa-arrow-left"/>'
18454             },
18455             {
18456                 tag: 'th',
18457                 cls: 'switch',
18458                 colspan: '5'
18459             },
18460             {
18461                 tag: 'th',
18462                 cls: 'next',
18463                 html: '<i class="fa fa-arrow-right"/>'
18464             }
18465
18466             ]
18467         }
18468         ]
18469     },
18470     
18471     content : {
18472         tag: 'tbody',
18473         cn: [
18474         {
18475             tag: 'tr',
18476             cn: [
18477             {
18478                 tag: 'td',
18479                 colspan: '7'
18480             }
18481             ]
18482         }
18483         ]
18484     },
18485     
18486     footer : {
18487         tag: 'tfoot',
18488         cn: [
18489         {
18490             tag: 'tr',
18491             cn: [
18492             {
18493                 tag: 'th',
18494                 colspan: '7',
18495                 cls: 'today'
18496             }
18497                     
18498             ]
18499         }
18500         ]
18501     },
18502     
18503     dates:{
18504         en: {
18505             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18506             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18507             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18508             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18509             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18510             today: "Today"
18511         }
18512     },
18513     
18514     modes: [
18515     {
18516         clsName: 'days',
18517         navFnc: 'Month',
18518         navStep: 1
18519     },
18520     {
18521         clsName: 'months',
18522         navFnc: 'FullYear',
18523         navStep: 1
18524     },
18525     {
18526         clsName: 'years',
18527         navFnc: 'FullYear',
18528         navStep: 10
18529     }]
18530 });
18531
18532 Roo.apply(Roo.bootstrap.DateField,  {
18533   
18534     template : {
18535         tag: 'div',
18536         cls: 'datepicker dropdown-menu roo-dynamic',
18537         cn: [
18538         {
18539             tag: 'div',
18540             cls: 'datepicker-days',
18541             cn: [
18542             {
18543                 tag: 'table',
18544                 cls: 'table-condensed',
18545                 cn:[
18546                 Roo.bootstrap.DateField.head,
18547                 {
18548                     tag: 'tbody'
18549                 },
18550                 Roo.bootstrap.DateField.footer
18551                 ]
18552             }
18553             ]
18554         },
18555         {
18556             tag: 'div',
18557             cls: 'datepicker-months',
18558             cn: [
18559             {
18560                 tag: 'table',
18561                 cls: 'table-condensed',
18562                 cn:[
18563                 Roo.bootstrap.DateField.head,
18564                 Roo.bootstrap.DateField.content,
18565                 Roo.bootstrap.DateField.footer
18566                 ]
18567             }
18568             ]
18569         },
18570         {
18571             tag: 'div',
18572             cls: 'datepicker-years',
18573             cn: [
18574             {
18575                 tag: 'table',
18576                 cls: 'table-condensed',
18577                 cn:[
18578                 Roo.bootstrap.DateField.head,
18579                 Roo.bootstrap.DateField.content,
18580                 Roo.bootstrap.DateField.footer
18581                 ]
18582             }
18583             ]
18584         }
18585         ]
18586     }
18587 });
18588
18589  
18590
18591  /*
18592  * - LGPL
18593  *
18594  * TimeField
18595  * 
18596  */
18597
18598 /**
18599  * @class Roo.bootstrap.TimeField
18600  * @extends Roo.bootstrap.Input
18601  * Bootstrap DateField class
18602  * 
18603  * 
18604  * @constructor
18605  * Create a new TimeField
18606  * @param {Object} config The config object
18607  */
18608
18609 Roo.bootstrap.TimeField = function(config){
18610     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18611     this.addEvents({
18612             /**
18613              * @event show
18614              * Fires when this field show.
18615              * @param {Roo.bootstrap.DateField} thisthis
18616              * @param {Mixed} date The date value
18617              */
18618             show : true,
18619             /**
18620              * @event show
18621              * Fires when this field hide.
18622              * @param {Roo.bootstrap.DateField} this
18623              * @param {Mixed} date The date value
18624              */
18625             hide : true,
18626             /**
18627              * @event select
18628              * Fires when select a date.
18629              * @param {Roo.bootstrap.DateField} this
18630              * @param {Mixed} date The date value
18631              */
18632             select : true
18633         });
18634 };
18635
18636 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18637     
18638     /**
18639      * @cfg {String} format
18640      * The default time format string which can be overriden for localization support.  The format must be
18641      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18642      */
18643     format : "H:i",
18644        
18645     onRender: function(ct, position)
18646     {
18647         
18648         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18649                 
18650         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18651         
18652         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18653         
18654         this.pop = this.picker().select('>.datepicker-time',true).first();
18655         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18656         
18657         this.picker().on('mousedown', this.onMousedown, this);
18658         this.picker().on('click', this.onClick, this);
18659         
18660         this.picker().addClass('datepicker-dropdown');
18661     
18662         this.fillTime();
18663         this.update();
18664             
18665         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18666         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18667         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18668         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18669         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18670         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18671
18672     },
18673     
18674     fireKey: function(e){
18675         if (!this.picker().isVisible()){
18676             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18677                 this.show();
18678             }
18679             return;
18680         }
18681
18682         e.preventDefault();
18683         
18684         switch(e.keyCode){
18685             case 27: // escape
18686                 this.hide();
18687                 break;
18688             case 37: // left
18689             case 39: // right
18690                 this.onTogglePeriod();
18691                 break;
18692             case 38: // up
18693                 this.onIncrementMinutes();
18694                 break;
18695             case 40: // down
18696                 this.onDecrementMinutes();
18697                 break;
18698             case 13: // enter
18699             case 9: // tab
18700                 this.setTime();
18701                 break;
18702         }
18703     },
18704     
18705     onClick: function(e) {
18706         e.stopPropagation();
18707         e.preventDefault();
18708     },
18709     
18710     picker : function()
18711     {
18712         return this.el.select('.datepicker', true).first();
18713     },
18714     
18715     fillTime: function()
18716     {    
18717         var time = this.pop.select('tbody', true).first();
18718         
18719         time.dom.innerHTML = '';
18720         
18721         time.createChild({
18722             tag: 'tr',
18723             cn: [
18724                 {
18725                     tag: 'td',
18726                     cn: [
18727                         {
18728                             tag: 'a',
18729                             href: '#',
18730                             cls: 'btn',
18731                             cn: [
18732                                 {
18733                                     tag: 'span',
18734                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18735                                 }
18736                             ]
18737                         } 
18738                     ]
18739                 },
18740                 {
18741                     tag: 'td',
18742                     cls: 'separator'
18743                 },
18744                 {
18745                     tag: 'td',
18746                     cn: [
18747                         {
18748                             tag: 'a',
18749                             href: '#',
18750                             cls: 'btn',
18751                             cn: [
18752                                 {
18753                                     tag: 'span',
18754                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18755                                 }
18756                             ]
18757                         }
18758                     ]
18759                 },
18760                 {
18761                     tag: 'td',
18762                     cls: 'separator'
18763                 }
18764             ]
18765         });
18766         
18767         time.createChild({
18768             tag: 'tr',
18769             cn: [
18770                 {
18771                     tag: 'td',
18772                     cn: [
18773                         {
18774                             tag: 'span',
18775                             cls: 'timepicker-hour',
18776                             html: '00'
18777                         }  
18778                     ]
18779                 },
18780                 {
18781                     tag: 'td',
18782                     cls: 'separator',
18783                     html: ':'
18784                 },
18785                 {
18786                     tag: 'td',
18787                     cn: [
18788                         {
18789                             tag: 'span',
18790                             cls: 'timepicker-minute',
18791                             html: '00'
18792                         }  
18793                     ]
18794                 },
18795                 {
18796                     tag: 'td',
18797                     cls: 'separator'
18798                 },
18799                 {
18800                     tag: 'td',
18801                     cn: [
18802                         {
18803                             tag: 'button',
18804                             type: 'button',
18805                             cls: 'btn btn-primary period',
18806                             html: 'AM'
18807                             
18808                         }
18809                     ]
18810                 }
18811             ]
18812         });
18813         
18814         time.createChild({
18815             tag: 'tr',
18816             cn: [
18817                 {
18818                     tag: 'td',
18819                     cn: [
18820                         {
18821                             tag: 'a',
18822                             href: '#',
18823                             cls: 'btn',
18824                             cn: [
18825                                 {
18826                                     tag: 'span',
18827                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18828                                 }
18829                             ]
18830                         }
18831                     ]
18832                 },
18833                 {
18834                     tag: 'td',
18835                     cls: 'separator'
18836                 },
18837                 {
18838                     tag: 'td',
18839                     cn: [
18840                         {
18841                             tag: 'a',
18842                             href: '#',
18843                             cls: 'btn',
18844                             cn: [
18845                                 {
18846                                     tag: 'span',
18847                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18848                                 }
18849                             ]
18850                         }
18851                     ]
18852                 },
18853                 {
18854                     tag: 'td',
18855                     cls: 'separator'
18856                 }
18857             ]
18858         });
18859         
18860     },
18861     
18862     update: function()
18863     {
18864         
18865         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18866         
18867         this.fill();
18868     },
18869     
18870     fill: function() 
18871     {
18872         var hours = this.time.getHours();
18873         var minutes = this.time.getMinutes();
18874         var period = 'AM';
18875         
18876         if(hours > 11){
18877             period = 'PM';
18878         }
18879         
18880         if(hours == 0){
18881             hours = 12;
18882         }
18883         
18884         
18885         if(hours > 12){
18886             hours = hours - 12;
18887         }
18888         
18889         if(hours < 10){
18890             hours = '0' + hours;
18891         }
18892         
18893         if(minutes < 10){
18894             minutes = '0' + minutes;
18895         }
18896         
18897         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18898         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18899         this.pop.select('button', true).first().dom.innerHTML = period;
18900         
18901     },
18902     
18903     place: function()
18904     {   
18905         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18906         
18907         var cls = ['bottom'];
18908         
18909         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18910             cls.pop();
18911             cls.push('top');
18912         }
18913         
18914         cls.push('right');
18915         
18916         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18917             cls.pop();
18918             cls.push('left');
18919         }
18920         
18921         this.picker().addClass(cls.join('-'));
18922         
18923         var _this = this;
18924         
18925         Roo.each(cls, function(c){
18926             if(c == 'bottom'){
18927                 _this.picker().setTop(_this.inputEl().getHeight());
18928                 return;
18929             }
18930             if(c == 'top'){
18931                 _this.picker().setTop(0 - _this.picker().getHeight());
18932                 return;
18933             }
18934             
18935             if(c == 'left'){
18936                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18937                 return;
18938             }
18939             if(c == 'right'){
18940                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18941                 return;
18942             }
18943         });
18944         
18945     },
18946   
18947     onFocus : function()
18948     {
18949         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18950         this.show();
18951     },
18952     
18953     onBlur : function()
18954     {
18955         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18956         this.hide();
18957     },
18958     
18959     show : function()
18960     {
18961         this.picker().show();
18962         this.pop.show();
18963         this.update();
18964         this.place();
18965         
18966         this.fireEvent('show', this, this.date);
18967     },
18968     
18969     hide : function()
18970     {
18971         this.picker().hide();
18972         this.pop.hide();
18973         
18974         this.fireEvent('hide', this, this.date);
18975     },
18976     
18977     setTime : function()
18978     {
18979         this.hide();
18980         this.setValue(this.time.format(this.format));
18981         
18982         this.fireEvent('select', this, this.date);
18983         
18984         
18985     },
18986     
18987     onMousedown: function(e){
18988         e.stopPropagation();
18989         e.preventDefault();
18990     },
18991     
18992     onIncrementHours: function()
18993     {
18994         Roo.log('onIncrementHours');
18995         this.time = this.time.add(Date.HOUR, 1);
18996         this.update();
18997         
18998     },
18999     
19000     onDecrementHours: function()
19001     {
19002         Roo.log('onDecrementHours');
19003         this.time = this.time.add(Date.HOUR, -1);
19004         this.update();
19005     },
19006     
19007     onIncrementMinutes: function()
19008     {
19009         Roo.log('onIncrementMinutes');
19010         this.time = this.time.add(Date.MINUTE, 1);
19011         this.update();
19012     },
19013     
19014     onDecrementMinutes: function()
19015     {
19016         Roo.log('onDecrementMinutes');
19017         this.time = this.time.add(Date.MINUTE, -1);
19018         this.update();
19019     },
19020     
19021     onTogglePeriod: function()
19022     {
19023         Roo.log('onTogglePeriod');
19024         this.time = this.time.add(Date.HOUR, 12);
19025         this.update();
19026     }
19027     
19028    
19029 });
19030
19031 Roo.apply(Roo.bootstrap.TimeField,  {
19032     
19033     content : {
19034         tag: 'tbody',
19035         cn: [
19036             {
19037                 tag: 'tr',
19038                 cn: [
19039                 {
19040                     tag: 'td',
19041                     colspan: '7'
19042                 }
19043                 ]
19044             }
19045         ]
19046     },
19047     
19048     footer : {
19049         tag: 'tfoot',
19050         cn: [
19051             {
19052                 tag: 'tr',
19053                 cn: [
19054                 {
19055                     tag: 'th',
19056                     colspan: '7',
19057                     cls: '',
19058                     cn: [
19059                         {
19060                             tag: 'button',
19061                             cls: 'btn btn-info ok',
19062                             html: 'OK'
19063                         }
19064                     ]
19065                 }
19066
19067                 ]
19068             }
19069         ]
19070     }
19071 });
19072
19073 Roo.apply(Roo.bootstrap.TimeField,  {
19074   
19075     template : {
19076         tag: 'div',
19077         cls: 'datepicker dropdown-menu',
19078         cn: [
19079             {
19080                 tag: 'div',
19081                 cls: 'datepicker-time',
19082                 cn: [
19083                 {
19084                     tag: 'table',
19085                     cls: 'table-condensed',
19086                     cn:[
19087                     Roo.bootstrap.TimeField.content,
19088                     Roo.bootstrap.TimeField.footer
19089                     ]
19090                 }
19091                 ]
19092             }
19093         ]
19094     }
19095 });
19096
19097  
19098
19099  /*
19100  * - LGPL
19101  *
19102  * MonthField
19103  * 
19104  */
19105
19106 /**
19107  * @class Roo.bootstrap.MonthField
19108  * @extends Roo.bootstrap.Input
19109  * Bootstrap MonthField class
19110  * 
19111  * @cfg {String} language default en
19112  * 
19113  * @constructor
19114  * Create a new MonthField
19115  * @param {Object} config The config object
19116  */
19117
19118 Roo.bootstrap.MonthField = function(config){
19119     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19120     
19121     this.addEvents({
19122         /**
19123          * @event show
19124          * Fires when this field show.
19125          * @param {Roo.bootstrap.MonthField} this
19126          * @param {Mixed} date The date value
19127          */
19128         show : true,
19129         /**
19130          * @event show
19131          * Fires when this field hide.
19132          * @param {Roo.bootstrap.MonthField} this
19133          * @param {Mixed} date The date value
19134          */
19135         hide : true,
19136         /**
19137          * @event select
19138          * Fires when select a date.
19139          * @param {Roo.bootstrap.MonthField} this
19140          * @param {String} oldvalue The old value
19141          * @param {String} newvalue The new value
19142          */
19143         select : true
19144     });
19145 };
19146
19147 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19148     
19149     onRender: function(ct, position)
19150     {
19151         
19152         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19153         
19154         this.language = this.language || 'en';
19155         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19156         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19157         
19158         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19159         this.isInline = false;
19160         this.isInput = true;
19161         this.component = this.el.select('.add-on', true).first() || false;
19162         this.component = (this.component && this.component.length === 0) ? false : this.component;
19163         this.hasInput = this.component && this.inputEL().length;
19164         
19165         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19166         
19167         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19168         
19169         this.picker().on('mousedown', this.onMousedown, this);
19170         this.picker().on('click', this.onClick, this);
19171         
19172         this.picker().addClass('datepicker-dropdown');
19173         
19174         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19175             v.setStyle('width', '189px');
19176         });
19177         
19178         this.fillMonths();
19179         
19180         this.update();
19181         
19182         if(this.isInline) {
19183             this.show();
19184         }
19185         
19186     },
19187     
19188     setValue: function(v, suppressEvent)
19189     {   
19190         var o = this.getValue();
19191         
19192         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19193         
19194         this.update();
19195
19196         if(suppressEvent !== true){
19197             this.fireEvent('select', this, o, v);
19198         }
19199         
19200     },
19201     
19202     getValue: function()
19203     {
19204         return this.value;
19205     },
19206     
19207     onClick: function(e) 
19208     {
19209         e.stopPropagation();
19210         e.preventDefault();
19211         
19212         var target = e.getTarget();
19213         
19214         if(target.nodeName.toLowerCase() === 'i'){
19215             target = Roo.get(target).dom.parentNode;
19216         }
19217         
19218         var nodeName = target.nodeName;
19219         var className = target.className;
19220         var html = target.innerHTML;
19221         
19222         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19223             return;
19224         }
19225         
19226         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19227         
19228         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19229         
19230         this.hide();
19231                         
19232     },
19233     
19234     picker : function()
19235     {
19236         return this.pickerEl;
19237     },
19238     
19239     fillMonths: function()
19240     {    
19241         var i = 0;
19242         var months = this.picker().select('>.datepicker-months td', true).first();
19243         
19244         months.dom.innerHTML = '';
19245         
19246         while (i < 12) {
19247             var month = {
19248                 tag: 'span',
19249                 cls: 'month',
19250                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19251             };
19252             
19253             months.createChild(month);
19254         }
19255         
19256     },
19257     
19258     update: function()
19259     {
19260         var _this = this;
19261         
19262         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19263             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19264         }
19265         
19266         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19267             e.removeClass('active');
19268             
19269             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19270                 e.addClass('active');
19271             }
19272         })
19273     },
19274     
19275     place: function()
19276     {
19277         if(this.isInline) {
19278             return;
19279         }
19280         
19281         this.picker().removeClass(['bottom', 'top']);
19282         
19283         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19284             /*
19285              * place to the top of element!
19286              *
19287              */
19288             
19289             this.picker().addClass('top');
19290             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19291             
19292             return;
19293         }
19294         
19295         this.picker().addClass('bottom');
19296         
19297         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19298     },
19299     
19300     onFocus : function()
19301     {
19302         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19303         this.show();
19304     },
19305     
19306     onBlur : function()
19307     {
19308         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19309         
19310         var d = this.inputEl().getValue();
19311         
19312         this.setValue(d);
19313                 
19314         this.hide();
19315     },
19316     
19317     show : function()
19318     {
19319         this.picker().show();
19320         this.picker().select('>.datepicker-months', true).first().show();
19321         this.update();
19322         this.place();
19323         
19324         this.fireEvent('show', this, this.date);
19325     },
19326     
19327     hide : function()
19328     {
19329         if(this.isInline) {
19330             return;
19331         }
19332         this.picker().hide();
19333         this.fireEvent('hide', this, this.date);
19334         
19335     },
19336     
19337     onMousedown: function(e)
19338     {
19339         e.stopPropagation();
19340         e.preventDefault();
19341     },
19342     
19343     keyup: function(e)
19344     {
19345         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19346         this.update();
19347     },
19348
19349     fireKey: function(e)
19350     {
19351         if (!this.picker().isVisible()){
19352             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19353                 this.show();
19354             }
19355             return;
19356         }
19357         
19358         var dir;
19359         
19360         switch(e.keyCode){
19361             case 27: // escape
19362                 this.hide();
19363                 e.preventDefault();
19364                 break;
19365             case 37: // left
19366             case 39: // right
19367                 dir = e.keyCode == 37 ? -1 : 1;
19368                 
19369                 this.vIndex = this.vIndex + dir;
19370                 
19371                 if(this.vIndex < 0){
19372                     this.vIndex = 0;
19373                 }
19374                 
19375                 if(this.vIndex > 11){
19376                     this.vIndex = 11;
19377                 }
19378                 
19379                 if(isNaN(this.vIndex)){
19380                     this.vIndex = 0;
19381                 }
19382                 
19383                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19384                 
19385                 break;
19386             case 38: // up
19387             case 40: // down
19388                 
19389                 dir = e.keyCode == 38 ? -1 : 1;
19390                 
19391                 this.vIndex = this.vIndex + dir * 4;
19392                 
19393                 if(this.vIndex < 0){
19394                     this.vIndex = 0;
19395                 }
19396                 
19397                 if(this.vIndex > 11){
19398                     this.vIndex = 11;
19399                 }
19400                 
19401                 if(isNaN(this.vIndex)){
19402                     this.vIndex = 0;
19403                 }
19404                 
19405                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19406                 break;
19407                 
19408             case 13: // enter
19409                 
19410                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19411                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19412                 }
19413                 
19414                 this.hide();
19415                 e.preventDefault();
19416                 break;
19417             case 9: // tab
19418                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19419                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19420                 }
19421                 this.hide();
19422                 break;
19423             case 16: // shift
19424             case 17: // ctrl
19425             case 18: // alt
19426                 break;
19427             default :
19428                 this.hide();
19429                 
19430         }
19431     },
19432     
19433     remove: function() 
19434     {
19435         this.picker().remove();
19436     }
19437    
19438 });
19439
19440 Roo.apply(Roo.bootstrap.MonthField,  {
19441     
19442     content : {
19443         tag: 'tbody',
19444         cn: [
19445         {
19446             tag: 'tr',
19447             cn: [
19448             {
19449                 tag: 'td',
19450                 colspan: '7'
19451             }
19452             ]
19453         }
19454         ]
19455     },
19456     
19457     dates:{
19458         en: {
19459             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19460             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19461         }
19462     }
19463 });
19464
19465 Roo.apply(Roo.bootstrap.MonthField,  {
19466   
19467     template : {
19468         tag: 'div',
19469         cls: 'datepicker dropdown-menu roo-dynamic',
19470         cn: [
19471             {
19472                 tag: 'div',
19473                 cls: 'datepicker-months',
19474                 cn: [
19475                 {
19476                     tag: 'table',
19477                     cls: 'table-condensed',
19478                     cn:[
19479                         Roo.bootstrap.DateField.content
19480                     ]
19481                 }
19482                 ]
19483             }
19484         ]
19485     }
19486 });
19487
19488  
19489
19490  
19491  /*
19492  * - LGPL
19493  *
19494  * CheckBox
19495  * 
19496  */
19497
19498 /**
19499  * @class Roo.bootstrap.CheckBox
19500  * @extends Roo.bootstrap.Input
19501  * Bootstrap CheckBox class
19502  * 
19503  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19504  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19505  * @cfg {String} boxLabel The text that appears beside the checkbox
19506  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19507  * @cfg {Boolean} checked initnal the element
19508  * @cfg {Boolean} inline inline the element (default false)
19509  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19510  * 
19511  * @constructor
19512  * Create a new CheckBox
19513  * @param {Object} config The config object
19514  */
19515
19516 Roo.bootstrap.CheckBox = function(config){
19517     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19518    
19519     this.addEvents({
19520         /**
19521         * @event check
19522         * Fires when the element is checked or unchecked.
19523         * @param {Roo.bootstrap.CheckBox} this This input
19524         * @param {Boolean} checked The new checked value
19525         */
19526        check : true
19527     });
19528     
19529 };
19530
19531 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19532   
19533     inputType: 'checkbox',
19534     inputValue: 1,
19535     valueOff: 0,
19536     boxLabel: false,
19537     checked: false,
19538     weight : false,
19539     inline: false,
19540     
19541     getAutoCreate : function()
19542     {
19543         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19544         
19545         var id = Roo.id();
19546         
19547         var cfg = {};
19548         
19549         cfg.cls = 'form-group ' + this.inputType; //input-group
19550         
19551         if(this.inline){
19552             cfg.cls += ' ' + this.inputType + '-inline';
19553         }
19554         
19555         var input =  {
19556             tag: 'input',
19557             id : id,
19558             type : this.inputType,
19559             value : this.inputValue,
19560             cls : 'roo-' + this.inputType, //'form-box',
19561             placeholder : this.placeholder || ''
19562             
19563         };
19564         
19565         if(this.inputType != 'radio'){
19566             var hidden =  {
19567                 tag: 'input',
19568                 type : 'hidden',
19569                 cls : 'roo-hidden-value',
19570                 value : this.checked ? this.valueOff : this.inputValue
19571             };
19572         }
19573         
19574             
19575         if (this.weight) { // Validity check?
19576             cfg.cls += " " + this.inputType + "-" + this.weight;
19577         }
19578         
19579         if (this.disabled) {
19580             input.disabled=true;
19581         }
19582         
19583         if(this.checked){
19584             input.checked = this.checked;
19585             
19586         }
19587         
19588         
19589         if (this.name) {
19590             
19591             input.name = this.name;
19592             
19593             if(this.inputType != 'radio'){
19594                 hidden.name = this.name;
19595                 input.name = '_hidden_' + this.name;
19596             }
19597         }
19598         
19599         if (this.size) {
19600             input.cls += ' input-' + this.size;
19601         }
19602         
19603         var settings=this;
19604         
19605         ['xs','sm','md','lg'].map(function(size){
19606             if (settings[size]) {
19607                 cfg.cls += ' col-' + size + '-' + settings[size];
19608             }
19609         });
19610         
19611         var inputblock = input;
19612          
19613         if (this.before || this.after) {
19614             
19615             inputblock = {
19616                 cls : 'input-group',
19617                 cn :  [] 
19618             };
19619             
19620             if (this.before) {
19621                 inputblock.cn.push({
19622                     tag :'span',
19623                     cls : 'input-group-addon',
19624                     html : this.before
19625                 });
19626             }
19627             
19628             inputblock.cn.push(input);
19629             
19630             if(this.inputType != 'radio'){
19631                 inputblock.cn.push(hidden);
19632             }
19633             
19634             if (this.after) {
19635                 inputblock.cn.push({
19636                     tag :'span',
19637                     cls : 'input-group-addon',
19638                     html : this.after
19639                 });
19640             }
19641             
19642         }
19643         
19644         if (align ==='left' && this.fieldLabel.length) {
19645 //                Roo.log("left and has label");
19646                 cfg.cn = [
19647                     
19648                     {
19649                         tag: 'label',
19650                         'for' :  id,
19651                         cls : 'control-label col-md-' + this.labelWidth,
19652                         html : this.fieldLabel
19653                         
19654                     },
19655                     {
19656                         cls : "col-md-" + (12 - this.labelWidth), 
19657                         cn: [
19658                             inputblock
19659                         ]
19660                     }
19661                     
19662                 ];
19663         } else if ( this.fieldLabel.length) {
19664 //                Roo.log(" label");
19665                 cfg.cn = [
19666                    
19667                     {
19668                         tag: this.boxLabel ? 'span' : 'label',
19669                         'for': id,
19670                         cls: 'control-label box-input-label',
19671                         //cls : 'input-group-addon',
19672                         html : this.fieldLabel
19673                         
19674                     },
19675                     
19676                     inputblock
19677                     
19678                 ];
19679
19680         } else {
19681             
19682 //                Roo.log(" no label && no align");
19683                 cfg.cn = [  inputblock ] ;
19684                 
19685                 
19686         }
19687         
19688         if(this.boxLabel){
19689              var boxLabelCfg = {
19690                 tag: 'label',
19691                 //'for': id, // box label is handled by onclick - so no for...
19692                 cls: 'box-label',
19693                 html: this.boxLabel
19694             };
19695             
19696             if(this.tooltip){
19697                 boxLabelCfg.tooltip = this.tooltip;
19698             }
19699              
19700             cfg.cn.push(boxLabelCfg);
19701         }
19702         
19703         if(this.inputType != 'radio'){
19704             cfg.cn.push(hidden);
19705         }
19706         
19707         return cfg;
19708         
19709     },
19710     
19711     /**
19712      * return the real input element.
19713      */
19714     inputEl: function ()
19715     {
19716         return this.el.select('input.roo-' + this.inputType,true).first();
19717     },
19718     hiddenEl: function ()
19719     {
19720         return this.el.select('input.roo-hidden-value',true).first();
19721     },
19722     
19723     labelEl: function()
19724     {
19725         return this.el.select('label.control-label',true).first();
19726     },
19727     /* depricated... */
19728     
19729     label: function()
19730     {
19731         return this.labelEl();
19732     },
19733     
19734     boxLabelEl: function()
19735     {
19736         return this.el.select('label.box-label',true).first();
19737     },
19738     
19739     initEvents : function()
19740     {
19741 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19742         
19743         this.inputEl().on('click', this.onClick,  this);
19744         
19745         if (this.boxLabel) { 
19746             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19747         }
19748         
19749         this.startValue = this.getValue();
19750         
19751         if(this.groupId){
19752             Roo.bootstrap.CheckBox.register(this);
19753         }
19754     },
19755     
19756     onClick : function()
19757     {   
19758         this.setChecked(!this.checked);
19759     },
19760     
19761     setChecked : function(state,suppressEvent)
19762     {
19763         this.startValue = this.getValue();
19764         
19765         if(this.inputType == 'radio'){
19766             
19767             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19768                 e.dom.checked = false;
19769             });
19770             
19771             this.inputEl().dom.checked = true;
19772             
19773             this.inputEl().dom.value = this.inputValue;
19774             
19775             if(suppressEvent !== true){
19776                 this.fireEvent('check', this, true);
19777             }
19778             
19779             this.validate();
19780             
19781             return;
19782         }
19783         
19784         this.checked = state;
19785         
19786         this.inputEl().dom.checked = state;
19787         
19788         
19789         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19790         
19791         if(suppressEvent !== true){
19792             this.fireEvent('check', this, state);
19793         }
19794         
19795         this.validate();
19796     },
19797     
19798     getValue : function()
19799     {
19800         if(this.inputType == 'radio'){
19801             return this.getGroupValue();
19802         }
19803         
19804         return this.hiddenEl().dom.value;
19805         
19806     },
19807     
19808     getGroupValue : function()
19809     {
19810         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19811             return '';
19812         }
19813         
19814         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19815     },
19816     
19817     setValue : function(v,suppressEvent)
19818     {
19819         if(this.inputType == 'radio'){
19820             this.setGroupValue(v, suppressEvent);
19821             return;
19822         }
19823         
19824         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19825         
19826         this.validate();
19827     },
19828     
19829     setGroupValue : function(v, suppressEvent)
19830     {
19831         this.startValue = this.getValue();
19832         
19833         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19834             e.dom.checked = false;
19835             
19836             if(e.dom.value == v){
19837                 e.dom.checked = true;
19838             }
19839         });
19840         
19841         if(suppressEvent !== true){
19842             this.fireEvent('check', this, true);
19843         }
19844
19845         this.validate();
19846         
19847         return;
19848     },
19849     
19850     validate : function()
19851     {
19852         if(
19853                 this.disabled || 
19854                 (this.inputType == 'radio' && this.validateRadio()) ||
19855                 (this.inputType == 'checkbox' && this.validateCheckbox())
19856         ){
19857             this.markValid();
19858             return true;
19859         }
19860         
19861         this.markInvalid();
19862         return false;
19863     },
19864     
19865     validateRadio : function()
19866     {
19867         if(this.allowBlank){
19868             return true;
19869         }
19870         
19871         var valid = false;
19872         
19873         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19874             if(!e.dom.checked){
19875                 return;
19876             }
19877             
19878             valid = true;
19879             
19880             return false;
19881         });
19882         
19883         return valid;
19884     },
19885     
19886     validateCheckbox : function()
19887     {
19888         if(!this.groupId){
19889             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19890         }
19891         
19892         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19893         
19894         if(!group){
19895             return false;
19896         }
19897         
19898         var r = false;
19899         
19900         for(var i in group){
19901             if(r){
19902                 break;
19903             }
19904             
19905             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19906         }
19907         
19908         return r;
19909     },
19910     
19911     /**
19912      * Mark this field as valid
19913      */
19914     markValid : function()
19915     {
19916         if(this.allowBlank){
19917             return;
19918         }
19919         
19920         var _this = this;
19921         
19922         this.fireEvent('valid', this);
19923         
19924         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19925         
19926         if(this.groupId){
19927             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19928         }
19929         
19930         if(label){
19931             label.markValid();
19932         }
19933
19934         if(this.inputType == 'radio'){
19935             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19936                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19937                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19938             });
19939             
19940             return;
19941         }
19942         
19943         if(!this.groupId){
19944             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19945             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19946             return;
19947         }
19948         
19949         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19950             
19951         if(!group){
19952             return;
19953         }
19954         
19955         for(var i in group){
19956             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19957             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19958         }
19959     },
19960     
19961      /**
19962      * Mark this field as invalid
19963      * @param {String} msg The validation message
19964      */
19965     markInvalid : function(msg)
19966     {
19967         if(this.allowBlank){
19968             return;
19969         }
19970         
19971         var _this = this;
19972         
19973         this.fireEvent('invalid', this, msg);
19974         
19975         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19976         
19977         if(this.groupId){
19978             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19979         }
19980         
19981         if(label){
19982             label.markInvalid();
19983         }
19984             
19985         if(this.inputType == 'radio'){
19986             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19987                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19988                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19989             });
19990             
19991             return;
19992         }
19993         
19994         if(!this.groupId){
19995             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19996             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19997             return;
19998         }
19999         
20000         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20001         
20002         if(!group){
20003             return;
20004         }
20005         
20006         for(var i in group){
20007             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20008             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20009         }
20010         
20011     },
20012     
20013     disable : function()
20014     {
20015         if(this.inputType != 'radio'){
20016             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20017             return;
20018         }
20019         
20020         var _this = this;
20021         
20022         if(this.rendered){
20023             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20024                 _this.getActionEl().addClass(this.disabledClass);
20025                 e.dom.disabled = true;
20026             });
20027         }
20028         
20029         this.disabled = true;
20030         this.fireEvent("disable", this);
20031         return this;
20032     },
20033
20034     enable : function()
20035     {
20036         if(this.inputType != 'radio'){
20037             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20038             return;
20039         }
20040         
20041         var _this = this;
20042         
20043         if(this.rendered){
20044             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20045                 _this.getActionEl().removeClass(this.disabledClass);
20046                 e.dom.disabled = false;
20047             });
20048         }
20049         
20050         this.disabled = false;
20051         this.fireEvent("enable", this);
20052         return this;
20053     }
20054
20055 });
20056
20057 Roo.apply(Roo.bootstrap.CheckBox, {
20058     
20059     groups: {},
20060     
20061      /**
20062     * register a CheckBox Group
20063     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20064     */
20065     register : function(checkbox)
20066     {
20067         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20068             this.groups[checkbox.groupId] = {};
20069         }
20070         
20071         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20072             return;
20073         }
20074         
20075         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20076         
20077     },
20078     /**
20079     * fetch a CheckBox Group based on the group ID
20080     * @param {string} the group ID
20081     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20082     */
20083     get: function(groupId) {
20084         if (typeof(this.groups[groupId]) == 'undefined') {
20085             return false;
20086         }
20087         
20088         return this.groups[groupId] ;
20089     }
20090     
20091     
20092 });
20093 /*
20094  * - LGPL
20095  *
20096  * Radio
20097  *
20098  *
20099  * not inline
20100  *<div class="radio">
20101   <label>
20102     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20103     Option one is this and that&mdash;be sure to include why it's great
20104   </label>
20105 </div>
20106  *
20107  *
20108  *inline
20109  *<span>
20110  *<label class="radio-inline">fieldLabel</label>
20111  *<label class="radio-inline">
20112   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20113 </label>
20114 <span>
20115  *
20116  *
20117  */
20118
20119 /**
20120  * @class Roo.bootstrap.Radio
20121  * @extends Roo.bootstrap.CheckBox
20122  * Bootstrap Radio class
20123
20124  * @constructor
20125  * Create a new Radio
20126  * @param {Object} config The config object
20127  */
20128
20129 Roo.bootstrap.Radio = function(config){
20130     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20131
20132 };
20133
20134 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20135
20136     inputType: 'radio',
20137     inputValue: '',
20138     valueOff: '',
20139
20140     getAutoCreate : function()
20141     {
20142         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20143         align = align || 'left'; // default...
20144
20145
20146
20147         var id = Roo.id();
20148
20149         var cfg = {
20150                 tag : this.inline ? 'span' : 'div',
20151                 cls : 'form-group',
20152                 cn : []
20153         };
20154
20155         var inline = this.inline ? ' radio-inline' : '';
20156
20157         var lbl = {
20158                 tag: 'label' ,
20159                 // does not need for, as we wrap the input with it..
20160                 'for' : id,
20161                 cls : 'control-label box-label' + inline,
20162                 cn : []
20163         };
20164         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20165
20166         var fieldLabel = {
20167             tag: 'label' ,
20168             //cls : 'control-label' + inline,
20169             html : this.fieldLabel,
20170             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20171         };
20172
20173         var input =  {
20174             tag: 'input',
20175             id : id,
20176             type : this.inputType,
20177             //value : (!this.checked) ? this.valueOff : this.inputValue,
20178             value : this.inputValue,
20179             cls : 'roo-radio',
20180             placeholder : this.placeholder || '' // ?? needed????
20181
20182         };
20183         if (this.weight) { // Validity check?
20184             input.cls += " radio-" + this.weight;
20185         }
20186         if (this.disabled) {
20187             input.disabled=true;
20188         }
20189
20190         if(this.checked){
20191             input.checked = this.checked;
20192         }
20193
20194         if (this.name) {
20195             input.name = this.name;
20196         }
20197
20198         if (this.size) {
20199             input.cls += ' input-' + this.size;
20200         }
20201
20202         //?? can span's inline have a width??
20203
20204         var settings=this;
20205         ['xs','sm','md','lg'].map(function(size){
20206             if (settings[size]) {
20207                 cfg.cls += ' col-' + size + '-' + settings[size];
20208             }
20209         });
20210
20211         var inputblock = input;
20212
20213         if (this.before || this.after) {
20214
20215             inputblock = {
20216                 cls : 'input-group',
20217                 tag : 'span',
20218                 cn :  []
20219             };
20220             if (this.before) {
20221                 inputblock.cn.push({
20222                     tag :'span',
20223                     cls : 'input-group-addon',
20224                     html : this.before
20225                 });
20226             }
20227             inputblock.cn.push(input);
20228             if (this.after) {
20229                 inputblock.cn.push({
20230                     tag :'span',
20231                     cls : 'input-group-addon',
20232                     html : this.after
20233                 });
20234             }
20235
20236         };
20237
20238
20239         if (this.fieldLabel && this.fieldLabel.length) {
20240             cfg.cn.push(fieldLabel);
20241         }
20242
20243         // normal bootstrap puts the input inside the label.
20244         // however with our styled version - it has to go after the input.
20245
20246         //lbl.cn.push(inputblock);
20247
20248         var lblwrap =  {
20249             tag: 'span',
20250             cls: 'radio' + inline,
20251             cn: [
20252                 inputblock,
20253                 lbl
20254             ]
20255         };
20256
20257         cfg.cn.push( lblwrap);
20258
20259         if(this.boxLabel){
20260             lbl.cn.push({
20261                 tag: 'span',
20262                 html: this.boxLabel
20263             })
20264         }
20265
20266
20267         return cfg;
20268
20269     },
20270
20271     initEvents : function()
20272     {
20273 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20274
20275         this.inputEl().on('click', this.onClick,  this);
20276         if (this.boxLabel) {
20277             //Roo.log('find label');
20278             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20279         }
20280
20281     },
20282
20283     inputEl: function ()
20284     {
20285         return this.el.select('input.roo-radio',true).first();
20286     },
20287     onClick : function()
20288     {
20289         Roo.log("click");
20290         this.setChecked(true);
20291     },
20292
20293     setChecked : function(state,suppressEvent)
20294     {
20295         if(state){
20296             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20297                 v.dom.checked = false;
20298             });
20299         }
20300         Roo.log(this.inputEl().dom);
20301         this.checked = state;
20302         this.inputEl().dom.checked = state;
20303
20304         if(suppressEvent !== true){
20305             this.fireEvent('check', this, state);
20306         }
20307
20308         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20309
20310     },
20311
20312     getGroupValue : function()
20313     {
20314         var value = '';
20315         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20316             if(v.dom.checked == true){
20317                 value = v.dom.value;
20318             }
20319         });
20320
20321         return value;
20322     },
20323
20324     /**
20325      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20326      * @return {Mixed} value The field value
20327      */
20328     getValue : function(){
20329         return this.getGroupValue();
20330     }
20331
20332 });
20333 //<script type="text/javascript">
20334
20335 /*
20336  * Based  Ext JS Library 1.1.1
20337  * Copyright(c) 2006-2007, Ext JS, LLC.
20338  * LGPL
20339  *
20340  */
20341  
20342 /**
20343  * @class Roo.HtmlEditorCore
20344  * @extends Roo.Component
20345  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20346  *
20347  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20348  */
20349
20350 Roo.HtmlEditorCore = function(config){
20351     
20352     
20353     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20354     
20355     
20356     this.addEvents({
20357         /**
20358          * @event initialize
20359          * Fires when the editor is fully initialized (including the iframe)
20360          * @param {Roo.HtmlEditorCore} this
20361          */
20362         initialize: true,
20363         /**
20364          * @event activate
20365          * Fires when the editor is first receives the focus. Any insertion must wait
20366          * until after this event.
20367          * @param {Roo.HtmlEditorCore} this
20368          */
20369         activate: true,
20370          /**
20371          * @event beforesync
20372          * Fires before the textarea is updated with content from the editor iframe. Return false
20373          * to cancel the sync.
20374          * @param {Roo.HtmlEditorCore} this
20375          * @param {String} html
20376          */
20377         beforesync: true,
20378          /**
20379          * @event beforepush
20380          * Fires before the iframe editor is updated with content from the textarea. Return false
20381          * to cancel the push.
20382          * @param {Roo.HtmlEditorCore} this
20383          * @param {String} html
20384          */
20385         beforepush: true,
20386          /**
20387          * @event sync
20388          * Fires when the textarea is updated with content from the editor iframe.
20389          * @param {Roo.HtmlEditorCore} this
20390          * @param {String} html
20391          */
20392         sync: true,
20393          /**
20394          * @event push
20395          * Fires when the iframe editor is updated with content from the textarea.
20396          * @param {Roo.HtmlEditorCore} this
20397          * @param {String} html
20398          */
20399         push: true,
20400         
20401         /**
20402          * @event editorevent
20403          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20404          * @param {Roo.HtmlEditorCore} this
20405          */
20406         editorevent: true
20407         
20408     });
20409     
20410     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20411     
20412     // defaults : white / black...
20413     this.applyBlacklists();
20414     
20415     
20416     
20417 };
20418
20419
20420 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20421
20422
20423      /**
20424      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20425      */
20426     
20427     owner : false,
20428     
20429      /**
20430      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20431      *                        Roo.resizable.
20432      */
20433     resizable : false,
20434      /**
20435      * @cfg {Number} height (in pixels)
20436      */   
20437     height: 300,
20438    /**
20439      * @cfg {Number} width (in pixels)
20440      */   
20441     width: 500,
20442     
20443     /**
20444      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20445      * 
20446      */
20447     stylesheets: false,
20448     
20449     // id of frame..
20450     frameId: false,
20451     
20452     // private properties
20453     validationEvent : false,
20454     deferHeight: true,
20455     initialized : false,
20456     activated : false,
20457     sourceEditMode : false,
20458     onFocus : Roo.emptyFn,
20459     iframePad:3,
20460     hideMode:'offsets',
20461     
20462     clearUp: true,
20463     
20464     // blacklist + whitelisted elements..
20465     black: false,
20466     white: false,
20467      
20468     
20469
20470     /**
20471      * Protected method that will not generally be called directly. It
20472      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20473      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20474      */
20475     getDocMarkup : function(){
20476         // body styles..
20477         var st = '';
20478         
20479         // inherit styels from page...?? 
20480         if (this.stylesheets === false) {
20481             
20482             Roo.get(document.head).select('style').each(function(node) {
20483                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20484             });
20485             
20486             Roo.get(document.head).select('link').each(function(node) { 
20487                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20488             });
20489             
20490         } else if (!this.stylesheets.length) {
20491                 // simple..
20492                 st = '<style type="text/css">' +
20493                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20494                    '</style>';
20495         } else { 
20496             
20497         }
20498         
20499         st +=  '<style type="text/css">' +
20500             'IMG { cursor: pointer } ' +
20501         '</style>';
20502
20503         
20504         return '<html><head>' + st  +
20505             //<style type="text/css">' +
20506             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20507             //'</style>' +
20508             ' </head><body class="roo-htmleditor-body"></body></html>';
20509     },
20510
20511     // private
20512     onRender : function(ct, position)
20513     {
20514         var _t = this;
20515         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20516         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20517         
20518         
20519         this.el.dom.style.border = '0 none';
20520         this.el.dom.setAttribute('tabIndex', -1);
20521         this.el.addClass('x-hidden hide');
20522         
20523         
20524         
20525         if(Roo.isIE){ // fix IE 1px bogus margin
20526             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20527         }
20528        
20529         
20530         this.frameId = Roo.id();
20531         
20532          
20533         
20534         var iframe = this.owner.wrap.createChild({
20535             tag: 'iframe',
20536             cls: 'form-control', // bootstrap..
20537             id: this.frameId,
20538             name: this.frameId,
20539             frameBorder : 'no',
20540             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20541         }, this.el
20542         );
20543         
20544         
20545         this.iframe = iframe.dom;
20546
20547          this.assignDocWin();
20548         
20549         this.doc.designMode = 'on';
20550        
20551         this.doc.open();
20552         this.doc.write(this.getDocMarkup());
20553         this.doc.close();
20554
20555         
20556         var task = { // must defer to wait for browser to be ready
20557             run : function(){
20558                 //console.log("run task?" + this.doc.readyState);
20559                 this.assignDocWin();
20560                 if(this.doc.body || this.doc.readyState == 'complete'){
20561                     try {
20562                         this.doc.designMode="on";
20563                     } catch (e) {
20564                         return;
20565                     }
20566                     Roo.TaskMgr.stop(task);
20567                     this.initEditor.defer(10, this);
20568                 }
20569             },
20570             interval : 10,
20571             duration: 10000,
20572             scope: this
20573         };
20574         Roo.TaskMgr.start(task);
20575
20576     },
20577
20578     // private
20579     onResize : function(w, h)
20580     {
20581          Roo.log('resize: ' +w + ',' + h );
20582         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20583         if(!this.iframe){
20584             return;
20585         }
20586         if(typeof w == 'number'){
20587             
20588             this.iframe.style.width = w + 'px';
20589         }
20590         if(typeof h == 'number'){
20591             
20592             this.iframe.style.height = h + 'px';
20593             if(this.doc){
20594                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20595             }
20596         }
20597         
20598     },
20599
20600     /**
20601      * Toggles the editor between standard and source edit mode.
20602      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20603      */
20604     toggleSourceEdit : function(sourceEditMode){
20605         
20606         this.sourceEditMode = sourceEditMode === true;
20607         
20608         if(this.sourceEditMode){
20609  
20610             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20611             
20612         }else{
20613             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20614             //this.iframe.className = '';
20615             this.deferFocus();
20616         }
20617         //this.setSize(this.owner.wrap.getSize());
20618         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20619     },
20620
20621     
20622   
20623
20624     /**
20625      * Protected method that will not generally be called directly. If you need/want
20626      * custom HTML cleanup, this is the method you should override.
20627      * @param {String} html The HTML to be cleaned
20628      * return {String} The cleaned HTML
20629      */
20630     cleanHtml : function(html){
20631         html = String(html);
20632         if(html.length > 5){
20633             if(Roo.isSafari){ // strip safari nonsense
20634                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20635             }
20636         }
20637         if(html == '&nbsp;'){
20638             html = '';
20639         }
20640         return html;
20641     },
20642
20643     /**
20644      * HTML Editor -> Textarea
20645      * Protected method that will not generally be called directly. Syncs the contents
20646      * of the editor iframe with the textarea.
20647      */
20648     syncValue : function(){
20649         if(this.initialized){
20650             var bd = (this.doc.body || this.doc.documentElement);
20651             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20652             var html = bd.innerHTML;
20653             if(Roo.isSafari){
20654                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20655                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20656                 if(m && m[1]){
20657                     html = '<div style="'+m[0]+'">' + html + '</div>';
20658                 }
20659             }
20660             html = this.cleanHtml(html);
20661             // fix up the special chars.. normaly like back quotes in word...
20662             // however we do not want to do this with chinese..
20663             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20664                 var cc = b.charCodeAt();
20665                 if (
20666                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20667                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20668                     (cc >= 0xf900 && cc < 0xfb00 )
20669                 ) {
20670                         return b;
20671                 }
20672                 return "&#"+cc+";" 
20673             });
20674             if(this.owner.fireEvent('beforesync', this, html) !== false){
20675                 this.el.dom.value = html;
20676                 this.owner.fireEvent('sync', this, html);
20677             }
20678         }
20679     },
20680
20681     /**
20682      * Protected method that will not generally be called directly. Pushes the value of the textarea
20683      * into the iframe editor.
20684      */
20685     pushValue : function(){
20686         if(this.initialized){
20687             var v = this.el.dom.value.trim();
20688             
20689 //            if(v.length < 1){
20690 //                v = '&#160;';
20691 //            }
20692             
20693             if(this.owner.fireEvent('beforepush', this, v) !== false){
20694                 var d = (this.doc.body || this.doc.documentElement);
20695                 d.innerHTML = v;
20696                 this.cleanUpPaste();
20697                 this.el.dom.value = d.innerHTML;
20698                 this.owner.fireEvent('push', this, v);
20699             }
20700         }
20701     },
20702
20703     // private
20704     deferFocus : function(){
20705         this.focus.defer(10, this);
20706     },
20707
20708     // doc'ed in Field
20709     focus : function(){
20710         if(this.win && !this.sourceEditMode){
20711             this.win.focus();
20712         }else{
20713             this.el.focus();
20714         }
20715     },
20716     
20717     assignDocWin: function()
20718     {
20719         var iframe = this.iframe;
20720         
20721          if(Roo.isIE){
20722             this.doc = iframe.contentWindow.document;
20723             this.win = iframe.contentWindow;
20724         } else {
20725 //            if (!Roo.get(this.frameId)) {
20726 //                return;
20727 //            }
20728 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20729 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20730             
20731             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20732                 return;
20733             }
20734             
20735             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20736             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20737         }
20738     },
20739     
20740     // private
20741     initEditor : function(){
20742         //console.log("INIT EDITOR");
20743         this.assignDocWin();
20744         
20745         
20746         
20747         this.doc.designMode="on";
20748         this.doc.open();
20749         this.doc.write(this.getDocMarkup());
20750         this.doc.close();
20751         
20752         var dbody = (this.doc.body || this.doc.documentElement);
20753         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20754         // this copies styles from the containing element into thsi one..
20755         // not sure why we need all of this..
20756         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20757         
20758         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20759         //ss['background-attachment'] = 'fixed'; // w3c
20760         dbody.bgProperties = 'fixed'; // ie
20761         //Roo.DomHelper.applyStyles(dbody, ss);
20762         Roo.EventManager.on(this.doc, {
20763             //'mousedown': this.onEditorEvent,
20764             'mouseup': this.onEditorEvent,
20765             'dblclick': this.onEditorEvent,
20766             'click': this.onEditorEvent,
20767             'keyup': this.onEditorEvent,
20768             buffer:100,
20769             scope: this
20770         });
20771         if(Roo.isGecko){
20772             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20773         }
20774         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20775             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20776         }
20777         this.initialized = true;
20778
20779         this.owner.fireEvent('initialize', this);
20780         this.pushValue();
20781     },
20782
20783     // private
20784     onDestroy : function(){
20785         
20786         
20787         
20788         if(this.rendered){
20789             
20790             //for (var i =0; i < this.toolbars.length;i++) {
20791             //    // fixme - ask toolbars for heights?
20792             //    this.toolbars[i].onDestroy();
20793            // }
20794             
20795             //this.wrap.dom.innerHTML = '';
20796             //this.wrap.remove();
20797         }
20798     },
20799
20800     // private
20801     onFirstFocus : function(){
20802         
20803         this.assignDocWin();
20804         
20805         
20806         this.activated = true;
20807          
20808     
20809         if(Roo.isGecko){ // prevent silly gecko errors
20810             this.win.focus();
20811             var s = this.win.getSelection();
20812             if(!s.focusNode || s.focusNode.nodeType != 3){
20813                 var r = s.getRangeAt(0);
20814                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20815                 r.collapse(true);
20816                 this.deferFocus();
20817             }
20818             try{
20819                 this.execCmd('useCSS', true);
20820                 this.execCmd('styleWithCSS', false);
20821             }catch(e){}
20822         }
20823         this.owner.fireEvent('activate', this);
20824     },
20825
20826     // private
20827     adjustFont: function(btn){
20828         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20829         //if(Roo.isSafari){ // safari
20830         //    adjust *= 2;
20831        // }
20832         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20833         if(Roo.isSafari){ // safari
20834             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20835             v =  (v < 10) ? 10 : v;
20836             v =  (v > 48) ? 48 : v;
20837             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20838             
20839         }
20840         
20841         
20842         v = Math.max(1, v+adjust);
20843         
20844         this.execCmd('FontSize', v  );
20845     },
20846
20847     onEditorEvent : function(e)
20848     {
20849         this.owner.fireEvent('editorevent', this, e);
20850       //  this.updateToolbar();
20851         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20852     },
20853
20854     insertTag : function(tg)
20855     {
20856         // could be a bit smarter... -> wrap the current selected tRoo..
20857         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20858             
20859             range = this.createRange(this.getSelection());
20860             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20861             wrappingNode.appendChild(range.extractContents());
20862             range.insertNode(wrappingNode);
20863
20864             return;
20865             
20866             
20867             
20868         }
20869         this.execCmd("formatblock",   tg);
20870         
20871     },
20872     
20873     insertText : function(txt)
20874     {
20875         
20876         
20877         var range = this.createRange();
20878         range.deleteContents();
20879                //alert(Sender.getAttribute('label'));
20880                
20881         range.insertNode(this.doc.createTextNode(txt));
20882     } ,
20883     
20884      
20885
20886     /**
20887      * Executes a Midas editor command on the editor document and performs necessary focus and
20888      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20889      * @param {String} cmd The Midas command
20890      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20891      */
20892     relayCmd : function(cmd, value){
20893         this.win.focus();
20894         this.execCmd(cmd, value);
20895         this.owner.fireEvent('editorevent', this);
20896         //this.updateToolbar();
20897         this.owner.deferFocus();
20898     },
20899
20900     /**
20901      * Executes a Midas editor command directly on the editor document.
20902      * For visual commands, you should use {@link #relayCmd} instead.
20903      * <b>This should only be called after the editor is initialized.</b>
20904      * @param {String} cmd The Midas command
20905      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20906      */
20907     execCmd : function(cmd, value){
20908         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20909         this.syncValue();
20910     },
20911  
20912  
20913    
20914     /**
20915      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20916      * to insert tRoo.
20917      * @param {String} text | dom node.. 
20918      */
20919     insertAtCursor : function(text)
20920     {
20921         
20922         
20923         
20924         if(!this.activated){
20925             return;
20926         }
20927         /*
20928         if(Roo.isIE){
20929             this.win.focus();
20930             var r = this.doc.selection.createRange();
20931             if(r){
20932                 r.collapse(true);
20933                 r.pasteHTML(text);
20934                 this.syncValue();
20935                 this.deferFocus();
20936             
20937             }
20938             return;
20939         }
20940         */
20941         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20942             this.win.focus();
20943             
20944             
20945             // from jquery ui (MIT licenced)
20946             var range, node;
20947             var win = this.win;
20948             
20949             if (win.getSelection && win.getSelection().getRangeAt) {
20950                 range = win.getSelection().getRangeAt(0);
20951                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20952                 range.insertNode(node);
20953             } else if (win.document.selection && win.document.selection.createRange) {
20954                 // no firefox support
20955                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20956                 win.document.selection.createRange().pasteHTML(txt);
20957             } else {
20958                 // no firefox support
20959                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20960                 this.execCmd('InsertHTML', txt);
20961             } 
20962             
20963             this.syncValue();
20964             
20965             this.deferFocus();
20966         }
20967     },
20968  // private
20969     mozKeyPress : function(e){
20970         if(e.ctrlKey){
20971             var c = e.getCharCode(), cmd;
20972           
20973             if(c > 0){
20974                 c = String.fromCharCode(c).toLowerCase();
20975                 switch(c){
20976                     case 'b':
20977                         cmd = 'bold';
20978                         break;
20979                     case 'i':
20980                         cmd = 'italic';
20981                         break;
20982                     
20983                     case 'u':
20984                         cmd = 'underline';
20985                         break;
20986                     
20987                     case 'v':
20988                         this.cleanUpPaste.defer(100, this);
20989                         return;
20990                         
20991                 }
20992                 if(cmd){
20993                     this.win.focus();
20994                     this.execCmd(cmd);
20995                     this.deferFocus();
20996                     e.preventDefault();
20997                 }
20998                 
20999             }
21000         }
21001     },
21002
21003     // private
21004     fixKeys : function(){ // load time branching for fastest keydown performance
21005         if(Roo.isIE){
21006             return function(e){
21007                 var k = e.getKey(), r;
21008                 if(k == e.TAB){
21009                     e.stopEvent();
21010                     r = this.doc.selection.createRange();
21011                     if(r){
21012                         r.collapse(true);
21013                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21014                         this.deferFocus();
21015                     }
21016                     return;
21017                 }
21018                 
21019                 if(k == e.ENTER){
21020                     r = this.doc.selection.createRange();
21021                     if(r){
21022                         var target = r.parentElement();
21023                         if(!target || target.tagName.toLowerCase() != 'li'){
21024                             e.stopEvent();
21025                             r.pasteHTML('<br />');
21026                             r.collapse(false);
21027                             r.select();
21028                         }
21029                     }
21030                 }
21031                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21032                     this.cleanUpPaste.defer(100, this);
21033                     return;
21034                 }
21035                 
21036                 
21037             };
21038         }else if(Roo.isOpera){
21039             return function(e){
21040                 var k = e.getKey();
21041                 if(k == e.TAB){
21042                     e.stopEvent();
21043                     this.win.focus();
21044                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21045                     this.deferFocus();
21046                 }
21047                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21048                     this.cleanUpPaste.defer(100, this);
21049                     return;
21050                 }
21051                 
21052             };
21053         }else if(Roo.isSafari){
21054             return function(e){
21055                 var k = e.getKey();
21056                 
21057                 if(k == e.TAB){
21058                     e.stopEvent();
21059                     this.execCmd('InsertText','\t');
21060                     this.deferFocus();
21061                     return;
21062                 }
21063                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21064                     this.cleanUpPaste.defer(100, this);
21065                     return;
21066                 }
21067                 
21068              };
21069         }
21070     }(),
21071     
21072     getAllAncestors: function()
21073     {
21074         var p = this.getSelectedNode();
21075         var a = [];
21076         if (!p) {
21077             a.push(p); // push blank onto stack..
21078             p = this.getParentElement();
21079         }
21080         
21081         
21082         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21083             a.push(p);
21084             p = p.parentNode;
21085         }
21086         a.push(this.doc.body);
21087         return a;
21088     },
21089     lastSel : false,
21090     lastSelNode : false,
21091     
21092     
21093     getSelection : function() 
21094     {
21095         this.assignDocWin();
21096         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21097     },
21098     
21099     getSelectedNode: function() 
21100     {
21101         // this may only work on Gecko!!!
21102         
21103         // should we cache this!!!!
21104         
21105         
21106         
21107          
21108         var range = this.createRange(this.getSelection()).cloneRange();
21109         
21110         if (Roo.isIE) {
21111             var parent = range.parentElement();
21112             while (true) {
21113                 var testRange = range.duplicate();
21114                 testRange.moveToElementText(parent);
21115                 if (testRange.inRange(range)) {
21116                     break;
21117                 }
21118                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21119                     break;
21120                 }
21121                 parent = parent.parentElement;
21122             }
21123             return parent;
21124         }
21125         
21126         // is ancestor a text element.
21127         var ac =  range.commonAncestorContainer;
21128         if (ac.nodeType == 3) {
21129             ac = ac.parentNode;
21130         }
21131         
21132         var ar = ac.childNodes;
21133          
21134         var nodes = [];
21135         var other_nodes = [];
21136         var has_other_nodes = false;
21137         for (var i=0;i<ar.length;i++) {
21138             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21139                 continue;
21140             }
21141             // fullly contained node.
21142             
21143             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21144                 nodes.push(ar[i]);
21145                 continue;
21146             }
21147             
21148             // probably selected..
21149             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21150                 other_nodes.push(ar[i]);
21151                 continue;
21152             }
21153             // outer..
21154             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21155                 continue;
21156             }
21157             
21158             
21159             has_other_nodes = true;
21160         }
21161         if (!nodes.length && other_nodes.length) {
21162             nodes= other_nodes;
21163         }
21164         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21165             return false;
21166         }
21167         
21168         return nodes[0];
21169     },
21170     createRange: function(sel)
21171     {
21172         // this has strange effects when using with 
21173         // top toolbar - not sure if it's a great idea.
21174         //this.editor.contentWindow.focus();
21175         if (typeof sel != "undefined") {
21176             try {
21177                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21178             } catch(e) {
21179                 return this.doc.createRange();
21180             }
21181         } else {
21182             return this.doc.createRange();
21183         }
21184     },
21185     getParentElement: function()
21186     {
21187         
21188         this.assignDocWin();
21189         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21190         
21191         var range = this.createRange(sel);
21192          
21193         try {
21194             var p = range.commonAncestorContainer;
21195             while (p.nodeType == 3) { // text node
21196                 p = p.parentNode;
21197             }
21198             return p;
21199         } catch (e) {
21200             return null;
21201         }
21202     
21203     },
21204     /***
21205      *
21206      * Range intersection.. the hard stuff...
21207      *  '-1' = before
21208      *  '0' = hits..
21209      *  '1' = after.
21210      *         [ -- selected range --- ]
21211      *   [fail]                        [fail]
21212      *
21213      *    basically..
21214      *      if end is before start or  hits it. fail.
21215      *      if start is after end or hits it fail.
21216      *
21217      *   if either hits (but other is outside. - then it's not 
21218      *   
21219      *    
21220      **/
21221     
21222     
21223     // @see http://www.thismuchiknow.co.uk/?p=64.
21224     rangeIntersectsNode : function(range, node)
21225     {
21226         var nodeRange = node.ownerDocument.createRange();
21227         try {
21228             nodeRange.selectNode(node);
21229         } catch (e) {
21230             nodeRange.selectNodeContents(node);
21231         }
21232     
21233         var rangeStartRange = range.cloneRange();
21234         rangeStartRange.collapse(true);
21235     
21236         var rangeEndRange = range.cloneRange();
21237         rangeEndRange.collapse(false);
21238     
21239         var nodeStartRange = nodeRange.cloneRange();
21240         nodeStartRange.collapse(true);
21241     
21242         var nodeEndRange = nodeRange.cloneRange();
21243         nodeEndRange.collapse(false);
21244     
21245         return rangeStartRange.compareBoundaryPoints(
21246                  Range.START_TO_START, nodeEndRange) == -1 &&
21247                rangeEndRange.compareBoundaryPoints(
21248                  Range.START_TO_START, nodeStartRange) == 1;
21249         
21250          
21251     },
21252     rangeCompareNode : function(range, node)
21253     {
21254         var nodeRange = node.ownerDocument.createRange();
21255         try {
21256             nodeRange.selectNode(node);
21257         } catch (e) {
21258             nodeRange.selectNodeContents(node);
21259         }
21260         
21261         
21262         range.collapse(true);
21263     
21264         nodeRange.collapse(true);
21265      
21266         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21267         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21268          
21269         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21270         
21271         var nodeIsBefore   =  ss == 1;
21272         var nodeIsAfter    = ee == -1;
21273         
21274         if (nodeIsBefore && nodeIsAfter) {
21275             return 0; // outer
21276         }
21277         if (!nodeIsBefore && nodeIsAfter) {
21278             return 1; //right trailed.
21279         }
21280         
21281         if (nodeIsBefore && !nodeIsAfter) {
21282             return 2;  // left trailed.
21283         }
21284         // fully contined.
21285         return 3;
21286     },
21287
21288     // private? - in a new class?
21289     cleanUpPaste :  function()
21290     {
21291         // cleans up the whole document..
21292         Roo.log('cleanuppaste');
21293         
21294         this.cleanUpChildren(this.doc.body);
21295         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21296         if (clean != this.doc.body.innerHTML) {
21297             this.doc.body.innerHTML = clean;
21298         }
21299         
21300     },
21301     
21302     cleanWordChars : function(input) {// change the chars to hex code
21303         var he = Roo.HtmlEditorCore;
21304         
21305         var output = input;
21306         Roo.each(he.swapCodes, function(sw) { 
21307             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21308             
21309             output = output.replace(swapper, sw[1]);
21310         });
21311         
21312         return output;
21313     },
21314     
21315     
21316     cleanUpChildren : function (n)
21317     {
21318         if (!n.childNodes.length) {
21319             return;
21320         }
21321         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21322            this.cleanUpChild(n.childNodes[i]);
21323         }
21324     },
21325     
21326     
21327         
21328     
21329     cleanUpChild : function (node)
21330     {
21331         var ed = this;
21332         //console.log(node);
21333         if (node.nodeName == "#text") {
21334             // clean up silly Windows -- stuff?
21335             return; 
21336         }
21337         if (node.nodeName == "#comment") {
21338             node.parentNode.removeChild(node);
21339             // clean up silly Windows -- stuff?
21340             return; 
21341         }
21342         var lcname = node.tagName.toLowerCase();
21343         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21344         // whitelist of tags..
21345         
21346         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21347             // remove node.
21348             node.parentNode.removeChild(node);
21349             return;
21350             
21351         }
21352         
21353         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21354         
21355         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21356         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21357         
21358         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21359         //    remove_keep_children = true;
21360         //}
21361         
21362         if (remove_keep_children) {
21363             this.cleanUpChildren(node);
21364             // inserts everything just before this node...
21365             while (node.childNodes.length) {
21366                 var cn = node.childNodes[0];
21367                 node.removeChild(cn);
21368                 node.parentNode.insertBefore(cn, node);
21369             }
21370             node.parentNode.removeChild(node);
21371             return;
21372         }
21373         
21374         if (!node.attributes || !node.attributes.length) {
21375             this.cleanUpChildren(node);
21376             return;
21377         }
21378         
21379         function cleanAttr(n,v)
21380         {
21381             
21382             if (v.match(/^\./) || v.match(/^\//)) {
21383                 return;
21384             }
21385             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21386                 return;
21387             }
21388             if (v.match(/^#/)) {
21389                 return;
21390             }
21391 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21392             node.removeAttribute(n);
21393             
21394         }
21395         
21396         var cwhite = this.cwhite;
21397         var cblack = this.cblack;
21398             
21399         function cleanStyle(n,v)
21400         {
21401             if (v.match(/expression/)) { //XSS?? should we even bother..
21402                 node.removeAttribute(n);
21403                 return;
21404             }
21405             
21406             var parts = v.split(/;/);
21407             var clean = [];
21408             
21409             Roo.each(parts, function(p) {
21410                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21411                 if (!p.length) {
21412                     return true;
21413                 }
21414                 var l = p.split(':').shift().replace(/\s+/g,'');
21415                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21416                 
21417                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21418 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21419                     //node.removeAttribute(n);
21420                     return true;
21421                 }
21422                 //Roo.log()
21423                 // only allow 'c whitelisted system attributes'
21424                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21425 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21426                     //node.removeAttribute(n);
21427                     return true;
21428                 }
21429                 
21430                 
21431                  
21432                 
21433                 clean.push(p);
21434                 return true;
21435             });
21436             if (clean.length) { 
21437                 node.setAttribute(n, clean.join(';'));
21438             } else {
21439                 node.removeAttribute(n);
21440             }
21441             
21442         }
21443         
21444         
21445         for (var i = node.attributes.length-1; i > -1 ; i--) {
21446             var a = node.attributes[i];
21447             //console.log(a);
21448             
21449             if (a.name.toLowerCase().substr(0,2)=='on')  {
21450                 node.removeAttribute(a.name);
21451                 continue;
21452             }
21453             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21454                 node.removeAttribute(a.name);
21455                 continue;
21456             }
21457             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21458                 cleanAttr(a.name,a.value); // fixme..
21459                 continue;
21460             }
21461             if (a.name == 'style') {
21462                 cleanStyle(a.name,a.value);
21463                 continue;
21464             }
21465             /// clean up MS crap..
21466             // tecnically this should be a list of valid class'es..
21467             
21468             
21469             if (a.name == 'class') {
21470                 if (a.value.match(/^Mso/)) {
21471                     node.className = '';
21472                 }
21473                 
21474                 if (a.value.match(/body/)) {
21475                     node.className = '';
21476                 }
21477                 continue;
21478             }
21479             
21480             // style cleanup!?
21481             // class cleanup?
21482             
21483         }
21484         
21485         
21486         this.cleanUpChildren(node);
21487         
21488         
21489     },
21490     
21491     /**
21492      * Clean up MS wordisms...
21493      */
21494     cleanWord : function(node)
21495     {
21496         
21497         
21498         if (!node) {
21499             this.cleanWord(this.doc.body);
21500             return;
21501         }
21502         if (node.nodeName == "#text") {
21503             // clean up silly Windows -- stuff?
21504             return; 
21505         }
21506         if (node.nodeName == "#comment") {
21507             node.parentNode.removeChild(node);
21508             // clean up silly Windows -- stuff?
21509             return; 
21510         }
21511         
21512         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21513             node.parentNode.removeChild(node);
21514             return;
21515         }
21516         
21517         // remove - but keep children..
21518         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21519             while (node.childNodes.length) {
21520                 var cn = node.childNodes[0];
21521                 node.removeChild(cn);
21522                 node.parentNode.insertBefore(cn, node);
21523             }
21524             node.parentNode.removeChild(node);
21525             this.iterateChildren(node, this.cleanWord);
21526             return;
21527         }
21528         // clean styles
21529         if (node.className.length) {
21530             
21531             var cn = node.className.split(/\W+/);
21532             var cna = [];
21533             Roo.each(cn, function(cls) {
21534                 if (cls.match(/Mso[a-zA-Z]+/)) {
21535                     return;
21536                 }
21537                 cna.push(cls);
21538             });
21539             node.className = cna.length ? cna.join(' ') : '';
21540             if (!cna.length) {
21541                 node.removeAttribute("class");
21542             }
21543         }
21544         
21545         if (node.hasAttribute("lang")) {
21546             node.removeAttribute("lang");
21547         }
21548         
21549         if (node.hasAttribute("style")) {
21550             
21551             var styles = node.getAttribute("style").split(";");
21552             var nstyle = [];
21553             Roo.each(styles, function(s) {
21554                 if (!s.match(/:/)) {
21555                     return;
21556                 }
21557                 var kv = s.split(":");
21558                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21559                     return;
21560                 }
21561                 // what ever is left... we allow.
21562                 nstyle.push(s);
21563             });
21564             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21565             if (!nstyle.length) {
21566                 node.removeAttribute('style');
21567             }
21568         }
21569         this.iterateChildren(node, this.cleanWord);
21570         
21571         
21572         
21573     },
21574     /**
21575      * iterateChildren of a Node, calling fn each time, using this as the scole..
21576      * @param {DomNode} node node to iterate children of.
21577      * @param {Function} fn method of this class to call on each item.
21578      */
21579     iterateChildren : function(node, fn)
21580     {
21581         if (!node.childNodes.length) {
21582                 return;
21583         }
21584         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21585            fn.call(this, node.childNodes[i])
21586         }
21587     },
21588     
21589     
21590     /**
21591      * cleanTableWidths.
21592      *
21593      * Quite often pasting from word etc.. results in tables with column and widths.
21594      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21595      *
21596      */
21597     cleanTableWidths : function(node)
21598     {
21599          
21600          
21601         if (!node) {
21602             this.cleanTableWidths(this.doc.body);
21603             return;
21604         }
21605         
21606         // ignore list...
21607         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21608             return; 
21609         }
21610         Roo.log(node.tagName);
21611         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21612             this.iterateChildren(node, this.cleanTableWidths);
21613             return;
21614         }
21615         if (node.hasAttribute('width')) {
21616             node.removeAttribute('width');
21617         }
21618         
21619          
21620         if (node.hasAttribute("style")) {
21621             // pretty basic...
21622             
21623             var styles = node.getAttribute("style").split(";");
21624             var nstyle = [];
21625             Roo.each(styles, function(s) {
21626                 if (!s.match(/:/)) {
21627                     return;
21628                 }
21629                 var kv = s.split(":");
21630                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21631                     return;
21632                 }
21633                 // what ever is left... we allow.
21634                 nstyle.push(s);
21635             });
21636             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21637             if (!nstyle.length) {
21638                 node.removeAttribute('style');
21639             }
21640         }
21641         
21642         this.iterateChildren(node, this.cleanTableWidths);
21643         
21644         
21645     },
21646     
21647     
21648     
21649     
21650     domToHTML : function(currentElement, depth, nopadtext) {
21651         
21652         depth = depth || 0;
21653         nopadtext = nopadtext || false;
21654     
21655         if (!currentElement) {
21656             return this.domToHTML(this.doc.body);
21657         }
21658         
21659         //Roo.log(currentElement);
21660         var j;
21661         var allText = false;
21662         var nodeName = currentElement.nodeName;
21663         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21664         
21665         if  (nodeName == '#text') {
21666             
21667             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21668         }
21669         
21670         
21671         var ret = '';
21672         if (nodeName != 'BODY') {
21673              
21674             var i = 0;
21675             // Prints the node tagName, such as <A>, <IMG>, etc
21676             if (tagName) {
21677                 var attr = [];
21678                 for(i = 0; i < currentElement.attributes.length;i++) {
21679                     // quoting?
21680                     var aname = currentElement.attributes.item(i).name;
21681                     if (!currentElement.attributes.item(i).value.length) {
21682                         continue;
21683                     }
21684                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21685                 }
21686                 
21687                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21688             } 
21689             else {
21690                 
21691                 // eack
21692             }
21693         } else {
21694             tagName = false;
21695         }
21696         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21697             return ret;
21698         }
21699         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21700             nopadtext = true;
21701         }
21702         
21703         
21704         // Traverse the tree
21705         i = 0;
21706         var currentElementChild = currentElement.childNodes.item(i);
21707         var allText = true;
21708         var innerHTML  = '';
21709         lastnode = '';
21710         while (currentElementChild) {
21711             // Formatting code (indent the tree so it looks nice on the screen)
21712             var nopad = nopadtext;
21713             if (lastnode == 'SPAN') {
21714                 nopad  = true;
21715             }
21716             // text
21717             if  (currentElementChild.nodeName == '#text') {
21718                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21719                 toadd = nopadtext ? toadd : toadd.trim();
21720                 if (!nopad && toadd.length > 80) {
21721                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21722                 }
21723                 innerHTML  += toadd;
21724                 
21725                 i++;
21726                 currentElementChild = currentElement.childNodes.item(i);
21727                 lastNode = '';
21728                 continue;
21729             }
21730             allText = false;
21731             
21732             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21733                 
21734             // Recursively traverse the tree structure of the child node
21735             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21736             lastnode = currentElementChild.nodeName;
21737             i++;
21738             currentElementChild=currentElement.childNodes.item(i);
21739         }
21740         
21741         ret += innerHTML;
21742         
21743         if (!allText) {
21744                 // The remaining code is mostly for formatting the tree
21745             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21746         }
21747         
21748         
21749         if (tagName) {
21750             ret+= "</"+tagName+">";
21751         }
21752         return ret;
21753         
21754     },
21755         
21756     applyBlacklists : function()
21757     {
21758         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21759         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21760         
21761         this.white = [];
21762         this.black = [];
21763         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21764             if (b.indexOf(tag) > -1) {
21765                 return;
21766             }
21767             this.white.push(tag);
21768             
21769         }, this);
21770         
21771         Roo.each(w, function(tag) {
21772             if (b.indexOf(tag) > -1) {
21773                 return;
21774             }
21775             if (this.white.indexOf(tag) > -1) {
21776                 return;
21777             }
21778             this.white.push(tag);
21779             
21780         }, this);
21781         
21782         
21783         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21784             if (w.indexOf(tag) > -1) {
21785                 return;
21786             }
21787             this.black.push(tag);
21788             
21789         }, this);
21790         
21791         Roo.each(b, function(tag) {
21792             if (w.indexOf(tag) > -1) {
21793                 return;
21794             }
21795             if (this.black.indexOf(tag) > -1) {
21796                 return;
21797             }
21798             this.black.push(tag);
21799             
21800         }, this);
21801         
21802         
21803         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21804         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21805         
21806         this.cwhite = [];
21807         this.cblack = [];
21808         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21809             if (b.indexOf(tag) > -1) {
21810                 return;
21811             }
21812             this.cwhite.push(tag);
21813             
21814         }, this);
21815         
21816         Roo.each(w, function(tag) {
21817             if (b.indexOf(tag) > -1) {
21818                 return;
21819             }
21820             if (this.cwhite.indexOf(tag) > -1) {
21821                 return;
21822             }
21823             this.cwhite.push(tag);
21824             
21825         }, this);
21826         
21827         
21828         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21829             if (w.indexOf(tag) > -1) {
21830                 return;
21831             }
21832             this.cblack.push(tag);
21833             
21834         }, this);
21835         
21836         Roo.each(b, function(tag) {
21837             if (w.indexOf(tag) > -1) {
21838                 return;
21839             }
21840             if (this.cblack.indexOf(tag) > -1) {
21841                 return;
21842             }
21843             this.cblack.push(tag);
21844             
21845         }, this);
21846     },
21847     
21848     setStylesheets : function(stylesheets)
21849     {
21850         if(typeof(stylesheets) == 'string'){
21851             Roo.get(this.iframe.contentDocument.head).createChild({
21852                 tag : 'link',
21853                 rel : 'stylesheet',
21854                 type : 'text/css',
21855                 href : stylesheets
21856             });
21857             
21858             return;
21859         }
21860         var _this = this;
21861      
21862         Roo.each(stylesheets, function(s) {
21863             if(!s.length){
21864                 return;
21865             }
21866             
21867             Roo.get(_this.iframe.contentDocument.head).createChild({
21868                 tag : 'link',
21869                 rel : 'stylesheet',
21870                 type : 'text/css',
21871                 href : s
21872             });
21873         });
21874
21875         
21876     },
21877     
21878     removeStylesheets : function()
21879     {
21880         var _this = this;
21881         
21882         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21883             s.remove();
21884         });
21885     }
21886     
21887     // hide stuff that is not compatible
21888     /**
21889      * @event blur
21890      * @hide
21891      */
21892     /**
21893      * @event change
21894      * @hide
21895      */
21896     /**
21897      * @event focus
21898      * @hide
21899      */
21900     /**
21901      * @event specialkey
21902      * @hide
21903      */
21904     /**
21905      * @cfg {String} fieldClass @hide
21906      */
21907     /**
21908      * @cfg {String} focusClass @hide
21909      */
21910     /**
21911      * @cfg {String} autoCreate @hide
21912      */
21913     /**
21914      * @cfg {String} inputType @hide
21915      */
21916     /**
21917      * @cfg {String} invalidClass @hide
21918      */
21919     /**
21920      * @cfg {String} invalidText @hide
21921      */
21922     /**
21923      * @cfg {String} msgFx @hide
21924      */
21925     /**
21926      * @cfg {String} validateOnBlur @hide
21927      */
21928 });
21929
21930 Roo.HtmlEditorCore.white = [
21931         'area', 'br', 'img', 'input', 'hr', 'wbr',
21932         
21933        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21934        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21935        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21936        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21937        'table',   'ul',         'xmp', 
21938        
21939        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21940       'thead',   'tr', 
21941      
21942       'dir', 'menu', 'ol', 'ul', 'dl',
21943        
21944       'embed',  'object'
21945 ];
21946
21947
21948 Roo.HtmlEditorCore.black = [
21949     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21950         'applet', // 
21951         'base',   'basefont', 'bgsound', 'blink',  'body', 
21952         'frame',  'frameset', 'head',    'html',   'ilayer', 
21953         'iframe', 'layer',  'link',     'meta',    'object',   
21954         'script', 'style' ,'title',  'xml' // clean later..
21955 ];
21956 Roo.HtmlEditorCore.clean = [
21957     'script', 'style', 'title', 'xml'
21958 ];
21959 Roo.HtmlEditorCore.remove = [
21960     'font'
21961 ];
21962 // attributes..
21963
21964 Roo.HtmlEditorCore.ablack = [
21965     'on'
21966 ];
21967     
21968 Roo.HtmlEditorCore.aclean = [ 
21969     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21970 ];
21971
21972 // protocols..
21973 Roo.HtmlEditorCore.pwhite= [
21974         'http',  'https',  'mailto'
21975 ];
21976
21977 // white listed style attributes.
21978 Roo.HtmlEditorCore.cwhite= [
21979       //  'text-align', /// default is to allow most things..
21980       
21981          
21982 //        'font-size'//??
21983 ];
21984
21985 // black listed style attributes.
21986 Roo.HtmlEditorCore.cblack= [
21987       //  'font-size' -- this can be set by the project 
21988 ];
21989
21990
21991 Roo.HtmlEditorCore.swapCodes   =[ 
21992     [    8211, "--" ], 
21993     [    8212, "--" ], 
21994     [    8216,  "'" ],  
21995     [    8217, "'" ],  
21996     [    8220, '"' ],  
21997     [    8221, '"' ],  
21998     [    8226, "*" ],  
21999     [    8230, "..." ]
22000 ]; 
22001
22002     /*
22003  * - LGPL
22004  *
22005  * HtmlEditor
22006  * 
22007  */
22008
22009 /**
22010  * @class Roo.bootstrap.HtmlEditor
22011  * @extends Roo.bootstrap.TextArea
22012  * Bootstrap HtmlEditor class
22013
22014  * @constructor
22015  * Create a new HtmlEditor
22016  * @param {Object} config The config object
22017  */
22018
22019 Roo.bootstrap.HtmlEditor = function(config){
22020     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22021     if (!this.toolbars) {
22022         this.toolbars = [];
22023     }
22024     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22025     this.addEvents({
22026             /**
22027              * @event initialize
22028              * Fires when the editor is fully initialized (including the iframe)
22029              * @param {HtmlEditor} this
22030              */
22031             initialize: true,
22032             /**
22033              * @event activate
22034              * Fires when the editor is first receives the focus. Any insertion must wait
22035              * until after this event.
22036              * @param {HtmlEditor} this
22037              */
22038             activate: true,
22039              /**
22040              * @event beforesync
22041              * Fires before the textarea is updated with content from the editor iframe. Return false
22042              * to cancel the sync.
22043              * @param {HtmlEditor} this
22044              * @param {String} html
22045              */
22046             beforesync: true,
22047              /**
22048              * @event beforepush
22049              * Fires before the iframe editor is updated with content from the textarea. Return false
22050              * to cancel the push.
22051              * @param {HtmlEditor} this
22052              * @param {String} html
22053              */
22054             beforepush: true,
22055              /**
22056              * @event sync
22057              * Fires when the textarea is updated with content from the editor iframe.
22058              * @param {HtmlEditor} this
22059              * @param {String} html
22060              */
22061             sync: true,
22062              /**
22063              * @event push
22064              * Fires when the iframe editor is updated with content from the textarea.
22065              * @param {HtmlEditor} this
22066              * @param {String} html
22067              */
22068             push: true,
22069              /**
22070              * @event editmodechange
22071              * Fires when the editor switches edit modes
22072              * @param {HtmlEditor} this
22073              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22074              */
22075             editmodechange: true,
22076             /**
22077              * @event editorevent
22078              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22079              * @param {HtmlEditor} this
22080              */
22081             editorevent: true,
22082             /**
22083              * @event firstfocus
22084              * Fires when on first focus - needed by toolbars..
22085              * @param {HtmlEditor} this
22086              */
22087             firstfocus: true,
22088             /**
22089              * @event autosave
22090              * Auto save the htmlEditor value as a file into Events
22091              * @param {HtmlEditor} this
22092              */
22093             autosave: true,
22094             /**
22095              * @event savedpreview
22096              * preview the saved version of htmlEditor
22097              * @param {HtmlEditor} this
22098              */
22099             savedpreview: true
22100         });
22101 };
22102
22103
22104 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22105     
22106     
22107       /**
22108      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22109      */
22110     toolbars : false,
22111    
22112      /**
22113      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22114      *                        Roo.resizable.
22115      */
22116     resizable : false,
22117      /**
22118      * @cfg {Number} height (in pixels)
22119      */   
22120     height: 300,
22121    /**
22122      * @cfg {Number} width (in pixels)
22123      */   
22124     width: false,
22125     
22126     /**
22127      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22128      * 
22129      */
22130     stylesheets: false,
22131     
22132     // id of frame..
22133     frameId: false,
22134     
22135     // private properties
22136     validationEvent : false,
22137     deferHeight: true,
22138     initialized : false,
22139     activated : false,
22140     
22141     onFocus : Roo.emptyFn,
22142     iframePad:3,
22143     hideMode:'offsets',
22144     
22145     
22146     tbContainer : false,
22147     
22148     toolbarContainer :function() {
22149         return this.wrap.select('.x-html-editor-tb',true).first();
22150     },
22151
22152     /**
22153      * Protected method that will not generally be called directly. It
22154      * is called when the editor creates its toolbar. Override this method if you need to
22155      * add custom toolbar buttons.
22156      * @param {HtmlEditor} editor
22157      */
22158     createToolbar : function(){
22159         
22160         Roo.log("create toolbars");
22161         
22162         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22163         this.toolbars[0].render(this.toolbarContainer());
22164         
22165         return;
22166         
22167 //        if (!editor.toolbars || !editor.toolbars.length) {
22168 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22169 //        }
22170 //        
22171 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22172 //            editor.toolbars[i] = Roo.factory(
22173 //                    typeof(editor.toolbars[i]) == 'string' ?
22174 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22175 //                Roo.bootstrap.HtmlEditor);
22176 //            editor.toolbars[i].init(editor);
22177 //        }
22178     },
22179
22180      
22181     // private
22182     onRender : function(ct, position)
22183     {
22184        // Roo.log("Call onRender: " + this.xtype);
22185         var _t = this;
22186         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22187       
22188         this.wrap = this.inputEl().wrap({
22189             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22190         });
22191         
22192         this.editorcore.onRender(ct, position);
22193          
22194         if (this.resizable) {
22195             this.resizeEl = new Roo.Resizable(this.wrap, {
22196                 pinned : true,
22197                 wrap: true,
22198                 dynamic : true,
22199                 minHeight : this.height,
22200                 height: this.height,
22201                 handles : this.resizable,
22202                 width: this.width,
22203                 listeners : {
22204                     resize : function(r, w, h) {
22205                         _t.onResize(w,h); // -something
22206                     }
22207                 }
22208             });
22209             
22210         }
22211         this.createToolbar(this);
22212        
22213         
22214         if(!this.width && this.resizable){
22215             this.setSize(this.wrap.getSize());
22216         }
22217         if (this.resizeEl) {
22218             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22219             // should trigger onReize..
22220         }
22221         
22222     },
22223
22224     // private
22225     onResize : function(w, h)
22226     {
22227         Roo.log('resize: ' +w + ',' + h );
22228         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22229         var ew = false;
22230         var eh = false;
22231         
22232         if(this.inputEl() ){
22233             if(typeof w == 'number'){
22234                 var aw = w - this.wrap.getFrameWidth('lr');
22235                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22236                 ew = aw;
22237             }
22238             if(typeof h == 'number'){
22239                  var tbh = -11;  // fixme it needs to tool bar size!
22240                 for (var i =0; i < this.toolbars.length;i++) {
22241                     // fixme - ask toolbars for heights?
22242                     tbh += this.toolbars[i].el.getHeight();
22243                     //if (this.toolbars[i].footer) {
22244                     //    tbh += this.toolbars[i].footer.el.getHeight();
22245                     //}
22246                 }
22247               
22248                 
22249                 
22250                 
22251                 
22252                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22253                 ah -= 5; // knock a few pixes off for look..
22254                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22255                 var eh = ah;
22256             }
22257         }
22258         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22259         this.editorcore.onResize(ew,eh);
22260         
22261     },
22262
22263     /**
22264      * Toggles the editor between standard and source edit mode.
22265      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22266      */
22267     toggleSourceEdit : function(sourceEditMode)
22268     {
22269         this.editorcore.toggleSourceEdit(sourceEditMode);
22270         
22271         if(this.editorcore.sourceEditMode){
22272             Roo.log('editor - showing textarea');
22273             
22274 //            Roo.log('in');
22275 //            Roo.log(this.syncValue());
22276             this.syncValue();
22277             this.inputEl().removeClass(['hide', 'x-hidden']);
22278             this.inputEl().dom.removeAttribute('tabIndex');
22279             this.inputEl().focus();
22280         }else{
22281             Roo.log('editor - hiding textarea');
22282 //            Roo.log('out')
22283 //            Roo.log(this.pushValue()); 
22284             this.pushValue();
22285             
22286             this.inputEl().addClass(['hide', 'x-hidden']);
22287             this.inputEl().dom.setAttribute('tabIndex', -1);
22288             //this.deferFocus();
22289         }
22290          
22291         if(this.resizable){
22292             this.setSize(this.wrap.getSize());
22293         }
22294         
22295         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22296     },
22297  
22298     // private (for BoxComponent)
22299     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22300
22301     // private (for BoxComponent)
22302     getResizeEl : function(){
22303         return this.wrap;
22304     },
22305
22306     // private (for BoxComponent)
22307     getPositionEl : function(){
22308         return this.wrap;
22309     },
22310
22311     // private
22312     initEvents : function(){
22313         this.originalValue = this.getValue();
22314     },
22315
22316 //    /**
22317 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22318 //     * @method
22319 //     */
22320 //    markInvalid : Roo.emptyFn,
22321 //    /**
22322 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22323 //     * @method
22324 //     */
22325 //    clearInvalid : Roo.emptyFn,
22326
22327     setValue : function(v){
22328         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22329         this.editorcore.pushValue();
22330     },
22331
22332      
22333     // private
22334     deferFocus : function(){
22335         this.focus.defer(10, this);
22336     },
22337
22338     // doc'ed in Field
22339     focus : function(){
22340         this.editorcore.focus();
22341         
22342     },
22343       
22344
22345     // private
22346     onDestroy : function(){
22347         
22348         
22349         
22350         if(this.rendered){
22351             
22352             for (var i =0; i < this.toolbars.length;i++) {
22353                 // fixme - ask toolbars for heights?
22354                 this.toolbars[i].onDestroy();
22355             }
22356             
22357             this.wrap.dom.innerHTML = '';
22358             this.wrap.remove();
22359         }
22360     },
22361
22362     // private
22363     onFirstFocus : function(){
22364         //Roo.log("onFirstFocus");
22365         this.editorcore.onFirstFocus();
22366          for (var i =0; i < this.toolbars.length;i++) {
22367             this.toolbars[i].onFirstFocus();
22368         }
22369         
22370     },
22371     
22372     // private
22373     syncValue : function()
22374     {   
22375         this.editorcore.syncValue();
22376     },
22377     
22378     pushValue : function()
22379     {   
22380         this.editorcore.pushValue();
22381     }
22382      
22383     
22384     // hide stuff that is not compatible
22385     /**
22386      * @event blur
22387      * @hide
22388      */
22389     /**
22390      * @event change
22391      * @hide
22392      */
22393     /**
22394      * @event focus
22395      * @hide
22396      */
22397     /**
22398      * @event specialkey
22399      * @hide
22400      */
22401     /**
22402      * @cfg {String} fieldClass @hide
22403      */
22404     /**
22405      * @cfg {String} focusClass @hide
22406      */
22407     /**
22408      * @cfg {String} autoCreate @hide
22409      */
22410     /**
22411      * @cfg {String} inputType @hide
22412      */
22413     /**
22414      * @cfg {String} invalidClass @hide
22415      */
22416     /**
22417      * @cfg {String} invalidText @hide
22418      */
22419     /**
22420      * @cfg {String} msgFx @hide
22421      */
22422     /**
22423      * @cfg {String} validateOnBlur @hide
22424      */
22425 });
22426  
22427     
22428    
22429    
22430    
22431       
22432 Roo.namespace('Roo.bootstrap.htmleditor');
22433 /**
22434  * @class Roo.bootstrap.HtmlEditorToolbar1
22435  * Basic Toolbar
22436  * 
22437  * Usage:
22438  *
22439  new Roo.bootstrap.HtmlEditor({
22440     ....
22441     toolbars : [
22442         new Roo.bootstrap.HtmlEditorToolbar1({
22443             disable : { fonts: 1 , format: 1, ..., ... , ...],
22444             btns : [ .... ]
22445         })
22446     }
22447      
22448  * 
22449  * @cfg {Object} disable List of elements to disable..
22450  * @cfg {Array} btns List of additional buttons.
22451  * 
22452  * 
22453  * NEEDS Extra CSS? 
22454  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22455  */
22456  
22457 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22458 {
22459     
22460     Roo.apply(this, config);
22461     
22462     // default disabled, based on 'good practice'..
22463     this.disable = this.disable || {};
22464     Roo.applyIf(this.disable, {
22465         fontSize : true,
22466         colors : true,
22467         specialElements : true
22468     });
22469     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22470     
22471     this.editor = config.editor;
22472     this.editorcore = config.editor.editorcore;
22473     
22474     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22475     
22476     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22477     // dont call parent... till later.
22478 }
22479 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22480      
22481     bar : true,
22482     
22483     editor : false,
22484     editorcore : false,
22485     
22486     
22487     formats : [
22488         "p" ,  
22489         "h1","h2","h3","h4","h5","h6", 
22490         "pre", "code", 
22491         "abbr", "acronym", "address", "cite", "samp", "var",
22492         'div','span'
22493     ],
22494     
22495     onRender : function(ct, position)
22496     {
22497        // Roo.log("Call onRender: " + this.xtype);
22498         
22499        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22500        Roo.log(this.el);
22501        this.el.dom.style.marginBottom = '0';
22502        var _this = this;
22503        var editorcore = this.editorcore;
22504        var editor= this.editor;
22505        
22506        var children = [];
22507        var btn = function(id,cmd , toggle, handler){
22508        
22509             var  event = toggle ? 'toggle' : 'click';
22510        
22511             var a = {
22512                 size : 'sm',
22513                 xtype: 'Button',
22514                 xns: Roo.bootstrap,
22515                 glyphicon : id,
22516                 cmd : id || cmd,
22517                 enableToggle:toggle !== false,
22518                 //html : 'submit'
22519                 pressed : toggle ? false : null,
22520                 listeners : {}
22521             };
22522             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22523                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22524             };
22525             children.push(a);
22526             return a;
22527        }
22528         
22529         var style = {
22530                 xtype: 'Button',
22531                 size : 'sm',
22532                 xns: Roo.bootstrap,
22533                 glyphicon : 'font',
22534                 //html : 'submit'
22535                 menu : {
22536                     xtype: 'Menu',
22537                     xns: Roo.bootstrap,
22538                     items:  []
22539                 }
22540         };
22541         Roo.each(this.formats, function(f) {
22542             style.menu.items.push({
22543                 xtype :'MenuItem',
22544                 xns: Roo.bootstrap,
22545                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22546                 tagname : f,
22547                 listeners : {
22548                     click : function()
22549                     {
22550                         editorcore.insertTag(this.tagname);
22551                         editor.focus();
22552                     }
22553                 }
22554                 
22555             });
22556         });
22557          children.push(style);   
22558             
22559             
22560         btn('bold',false,true);
22561         btn('italic',false,true);
22562         btn('align-left', 'justifyleft',true);
22563         btn('align-center', 'justifycenter',true);
22564         btn('align-right' , 'justifyright',true);
22565         btn('link', false, false, function(btn) {
22566             //Roo.log("create link?");
22567             var url = prompt(this.createLinkText, this.defaultLinkValue);
22568             if(url && url != 'http:/'+'/'){
22569                 this.editorcore.relayCmd('createlink', url);
22570             }
22571         }),
22572         btn('list','insertunorderedlist',true);
22573         btn('pencil', false,true, function(btn){
22574                 Roo.log(this);
22575                 
22576                 this.toggleSourceEdit(btn.pressed);
22577         });
22578         /*
22579         var cog = {
22580                 xtype: 'Button',
22581                 size : 'sm',
22582                 xns: Roo.bootstrap,
22583                 glyphicon : 'cog',
22584                 //html : 'submit'
22585                 menu : {
22586                     xtype: 'Menu',
22587                     xns: Roo.bootstrap,
22588                     items:  []
22589                 }
22590         };
22591         
22592         cog.menu.items.push({
22593             xtype :'MenuItem',
22594             xns: Roo.bootstrap,
22595             html : Clean styles,
22596             tagname : f,
22597             listeners : {
22598                 click : function()
22599                 {
22600                     editorcore.insertTag(this.tagname);
22601                     editor.focus();
22602                 }
22603             }
22604             
22605         });
22606        */
22607         
22608          
22609        this.xtype = 'NavSimplebar';
22610         
22611         for(var i=0;i< children.length;i++) {
22612             
22613             this.buttons.add(this.addxtypeChild(children[i]));
22614             
22615         }
22616         
22617         editor.on('editorevent', this.updateToolbar, this);
22618     },
22619     onBtnClick : function(id)
22620     {
22621        this.editorcore.relayCmd(id);
22622        this.editorcore.focus();
22623     },
22624     
22625     /**
22626      * Protected method that will not generally be called directly. It triggers
22627      * a toolbar update by reading the markup state of the current selection in the editor.
22628      */
22629     updateToolbar: function(){
22630
22631         if(!this.editorcore.activated){
22632             this.editor.onFirstFocus(); // is this neeed?
22633             return;
22634         }
22635
22636         var btns = this.buttons; 
22637         var doc = this.editorcore.doc;
22638         btns.get('bold').setActive(doc.queryCommandState('bold'));
22639         btns.get('italic').setActive(doc.queryCommandState('italic'));
22640         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22641         
22642         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22643         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22644         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22645         
22646         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22647         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22648          /*
22649         
22650         var ans = this.editorcore.getAllAncestors();
22651         if (this.formatCombo) {
22652             
22653             
22654             var store = this.formatCombo.store;
22655             this.formatCombo.setValue("");
22656             for (var i =0; i < ans.length;i++) {
22657                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22658                     // select it..
22659                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22660                     break;
22661                 }
22662             }
22663         }
22664         
22665         
22666         
22667         // hides menus... - so this cant be on a menu...
22668         Roo.bootstrap.MenuMgr.hideAll();
22669         */
22670         Roo.bootstrap.MenuMgr.hideAll();
22671         //this.editorsyncValue();
22672     },
22673     onFirstFocus: function() {
22674         this.buttons.each(function(item){
22675            item.enable();
22676         });
22677     },
22678     toggleSourceEdit : function(sourceEditMode){
22679         
22680           
22681         if(sourceEditMode){
22682             Roo.log("disabling buttons");
22683            this.buttons.each( function(item){
22684                 if(item.cmd != 'pencil'){
22685                     item.disable();
22686                 }
22687             });
22688           
22689         }else{
22690             Roo.log("enabling buttons");
22691             if(this.editorcore.initialized){
22692                 this.buttons.each( function(item){
22693                     item.enable();
22694                 });
22695             }
22696             
22697         }
22698         Roo.log("calling toggole on editor");
22699         // tell the editor that it's been pressed..
22700         this.editor.toggleSourceEdit(sourceEditMode);
22701        
22702     }
22703 });
22704
22705
22706
22707
22708
22709 /**
22710  * @class Roo.bootstrap.Table.AbstractSelectionModel
22711  * @extends Roo.util.Observable
22712  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22713  * implemented by descendant classes.  This class should not be directly instantiated.
22714  * @constructor
22715  */
22716 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22717     this.locked = false;
22718     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22719 };
22720
22721
22722 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22723     /** @ignore Called by the grid automatically. Do not call directly. */
22724     init : function(grid){
22725         this.grid = grid;
22726         this.initEvents();
22727     },
22728
22729     /**
22730      * Locks the selections.
22731      */
22732     lock : function(){
22733         this.locked = true;
22734     },
22735
22736     /**
22737      * Unlocks the selections.
22738      */
22739     unlock : function(){
22740         this.locked = false;
22741     },
22742
22743     /**
22744      * Returns true if the selections are locked.
22745      * @return {Boolean}
22746      */
22747     isLocked : function(){
22748         return this.locked;
22749     }
22750 });
22751 /**
22752  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22753  * @class Roo.bootstrap.Table.RowSelectionModel
22754  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22755  * It supports multiple selections and keyboard selection/navigation. 
22756  * @constructor
22757  * @param {Object} config
22758  */
22759
22760 Roo.bootstrap.Table.RowSelectionModel = function(config){
22761     Roo.apply(this, config);
22762     this.selections = new Roo.util.MixedCollection(false, function(o){
22763         return o.id;
22764     });
22765
22766     this.last = false;
22767     this.lastActive = false;
22768
22769     this.addEvents({
22770         /**
22771              * @event selectionchange
22772              * Fires when the selection changes
22773              * @param {SelectionModel} this
22774              */
22775             "selectionchange" : true,
22776         /**
22777              * @event afterselectionchange
22778              * Fires after the selection changes (eg. by key press or clicking)
22779              * @param {SelectionModel} this
22780              */
22781             "afterselectionchange" : true,
22782         /**
22783              * @event beforerowselect
22784              * Fires when a row is selected being selected, return false to cancel.
22785              * @param {SelectionModel} this
22786              * @param {Number} rowIndex The selected index
22787              * @param {Boolean} keepExisting False if other selections will be cleared
22788              */
22789             "beforerowselect" : true,
22790         /**
22791              * @event rowselect
22792              * Fires when a row is selected.
22793              * @param {SelectionModel} this
22794              * @param {Number} rowIndex The selected index
22795              * @param {Roo.data.Record} r The record
22796              */
22797             "rowselect" : true,
22798         /**
22799              * @event rowdeselect
22800              * Fires when a row is deselected.
22801              * @param {SelectionModel} this
22802              * @param {Number} rowIndex The selected index
22803              */
22804         "rowdeselect" : true
22805     });
22806     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22807     this.locked = false;
22808  };
22809
22810 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22811     /**
22812      * @cfg {Boolean} singleSelect
22813      * True to allow selection of only one row at a time (defaults to false)
22814      */
22815     singleSelect : false,
22816
22817     // private
22818     initEvents : function()
22819     {
22820
22821         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22822         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22823         //}else{ // allow click to work like normal
22824          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22825         //}
22826         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22827         this.grid.on("rowclick", this.handleMouseDown, this);
22828         
22829         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22830             "up" : function(e){
22831                 if(!e.shiftKey){
22832                     this.selectPrevious(e.shiftKey);
22833                 }else if(this.last !== false && this.lastActive !== false){
22834                     var last = this.last;
22835                     this.selectRange(this.last,  this.lastActive-1);
22836                     this.grid.getView().focusRow(this.lastActive);
22837                     if(last !== false){
22838                         this.last = last;
22839                     }
22840                 }else{
22841                     this.selectFirstRow();
22842                 }
22843                 this.fireEvent("afterselectionchange", this);
22844             },
22845             "down" : function(e){
22846                 if(!e.shiftKey){
22847                     this.selectNext(e.shiftKey);
22848                 }else if(this.last !== false && this.lastActive !== false){
22849                     var last = this.last;
22850                     this.selectRange(this.last,  this.lastActive+1);
22851                     this.grid.getView().focusRow(this.lastActive);
22852                     if(last !== false){
22853                         this.last = last;
22854                     }
22855                 }else{
22856                     this.selectFirstRow();
22857                 }
22858                 this.fireEvent("afterselectionchange", this);
22859             },
22860             scope: this
22861         });
22862         this.grid.store.on('load', function(){
22863             this.selections.clear();
22864         },this);
22865         /*
22866         var view = this.grid.view;
22867         view.on("refresh", this.onRefresh, this);
22868         view.on("rowupdated", this.onRowUpdated, this);
22869         view.on("rowremoved", this.onRemove, this);
22870         */
22871     },
22872
22873     // private
22874     onRefresh : function()
22875     {
22876         var ds = this.grid.store, i, v = this.grid.view;
22877         var s = this.selections;
22878         s.each(function(r){
22879             if((i = ds.indexOfId(r.id)) != -1){
22880                 v.onRowSelect(i);
22881             }else{
22882                 s.remove(r);
22883             }
22884         });
22885     },
22886
22887     // private
22888     onRemove : function(v, index, r){
22889         this.selections.remove(r);
22890     },
22891
22892     // private
22893     onRowUpdated : function(v, index, r){
22894         if(this.isSelected(r)){
22895             v.onRowSelect(index);
22896         }
22897     },
22898
22899     /**
22900      * Select records.
22901      * @param {Array} records The records to select
22902      * @param {Boolean} keepExisting (optional) True to keep existing selections
22903      */
22904     selectRecords : function(records, keepExisting)
22905     {
22906         if(!keepExisting){
22907             this.clearSelections();
22908         }
22909             var ds = this.grid.store;
22910         for(var i = 0, len = records.length; i < len; i++){
22911             this.selectRow(ds.indexOf(records[i]), true);
22912         }
22913     },
22914
22915     /**
22916      * Gets the number of selected rows.
22917      * @return {Number}
22918      */
22919     getCount : function(){
22920         return this.selections.length;
22921     },
22922
22923     /**
22924      * Selects the first row in the grid.
22925      */
22926     selectFirstRow : function(){
22927         this.selectRow(0);
22928     },
22929
22930     /**
22931      * Select the last row.
22932      * @param {Boolean} keepExisting (optional) True to keep existing selections
22933      */
22934     selectLastRow : function(keepExisting){
22935         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22936         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22937     },
22938
22939     /**
22940      * Selects the row immediately following the last selected row.
22941      * @param {Boolean} keepExisting (optional) True to keep existing selections
22942      */
22943     selectNext : function(keepExisting)
22944     {
22945             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22946             this.selectRow(this.last+1, keepExisting);
22947             this.grid.getView().focusRow(this.last);
22948         }
22949     },
22950
22951     /**
22952      * Selects the row that precedes the last selected row.
22953      * @param {Boolean} keepExisting (optional) True to keep existing selections
22954      */
22955     selectPrevious : function(keepExisting){
22956         if(this.last){
22957             this.selectRow(this.last-1, keepExisting);
22958             this.grid.getView().focusRow(this.last);
22959         }
22960     },
22961
22962     /**
22963      * Returns the selected records
22964      * @return {Array} Array of selected records
22965      */
22966     getSelections : function(){
22967         return [].concat(this.selections.items);
22968     },
22969
22970     /**
22971      * Returns the first selected record.
22972      * @return {Record}
22973      */
22974     getSelected : function(){
22975         return this.selections.itemAt(0);
22976     },
22977
22978
22979     /**
22980      * Clears all selections.
22981      */
22982     clearSelections : function(fast)
22983     {
22984         if(this.locked) {
22985             return;
22986         }
22987         if(fast !== true){
22988                 var ds = this.grid.store;
22989             var s = this.selections;
22990             s.each(function(r){
22991                 this.deselectRow(ds.indexOfId(r.id));
22992             }, this);
22993             s.clear();
22994         }else{
22995             this.selections.clear();
22996         }
22997         this.last = false;
22998     },
22999
23000
23001     /**
23002      * Selects all rows.
23003      */
23004     selectAll : function(){
23005         if(this.locked) {
23006             return;
23007         }
23008         this.selections.clear();
23009         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23010             this.selectRow(i, true);
23011         }
23012     },
23013
23014     /**
23015      * Returns True if there is a selection.
23016      * @return {Boolean}
23017      */
23018     hasSelection : function(){
23019         return this.selections.length > 0;
23020     },
23021
23022     /**
23023      * Returns True if the specified row is selected.
23024      * @param {Number/Record} record The record or index of the record to check
23025      * @return {Boolean}
23026      */
23027     isSelected : function(index){
23028             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23029         return (r && this.selections.key(r.id) ? true : false);
23030     },
23031
23032     /**
23033      * Returns True if the specified record id is selected.
23034      * @param {String} id The id of record to check
23035      * @return {Boolean}
23036      */
23037     isIdSelected : function(id){
23038         return (this.selections.key(id) ? true : false);
23039     },
23040
23041
23042     // private
23043     handleMouseDBClick : function(e, t){
23044         
23045     },
23046     // private
23047     handleMouseDown : function(e, t)
23048     {
23049             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23050         if(this.isLocked() || rowIndex < 0 ){
23051             return;
23052         };
23053         if(e.shiftKey && this.last !== false){
23054             var last = this.last;
23055             this.selectRange(last, rowIndex, e.ctrlKey);
23056             this.last = last; // reset the last
23057             t.focus();
23058     
23059         }else{
23060             var isSelected = this.isSelected(rowIndex);
23061             //Roo.log("select row:" + rowIndex);
23062             if(isSelected){
23063                 this.deselectRow(rowIndex);
23064             } else {
23065                         this.selectRow(rowIndex, true);
23066             }
23067     
23068             /*
23069                 if(e.button !== 0 && isSelected){
23070                 alert('rowIndex 2: ' + rowIndex);
23071                     view.focusRow(rowIndex);
23072                 }else if(e.ctrlKey && isSelected){
23073                     this.deselectRow(rowIndex);
23074                 }else if(!isSelected){
23075                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23076                     view.focusRow(rowIndex);
23077                 }
23078             */
23079         }
23080         this.fireEvent("afterselectionchange", this);
23081     },
23082     // private
23083     handleDragableRowClick :  function(grid, rowIndex, e) 
23084     {
23085         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23086             this.selectRow(rowIndex, false);
23087             grid.view.focusRow(rowIndex);
23088              this.fireEvent("afterselectionchange", this);
23089         }
23090     },
23091     
23092     /**
23093      * Selects multiple rows.
23094      * @param {Array} rows Array of the indexes of the row to select
23095      * @param {Boolean} keepExisting (optional) True to keep existing selections
23096      */
23097     selectRows : function(rows, keepExisting){
23098         if(!keepExisting){
23099             this.clearSelections();
23100         }
23101         for(var i = 0, len = rows.length; i < len; i++){
23102             this.selectRow(rows[i], true);
23103         }
23104     },
23105
23106     /**
23107      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23108      * @param {Number} startRow The index of the first row in the range
23109      * @param {Number} endRow The index of the last row in the range
23110      * @param {Boolean} keepExisting (optional) True to retain existing selections
23111      */
23112     selectRange : function(startRow, endRow, keepExisting){
23113         if(this.locked) {
23114             return;
23115         }
23116         if(!keepExisting){
23117             this.clearSelections();
23118         }
23119         if(startRow <= endRow){
23120             for(var i = startRow; i <= endRow; i++){
23121                 this.selectRow(i, true);
23122             }
23123         }else{
23124             for(var i = startRow; i >= endRow; i--){
23125                 this.selectRow(i, true);
23126             }
23127         }
23128     },
23129
23130     /**
23131      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23132      * @param {Number} startRow The index of the first row in the range
23133      * @param {Number} endRow The index of the last row in the range
23134      */
23135     deselectRange : function(startRow, endRow, preventViewNotify){
23136         if(this.locked) {
23137             return;
23138         }
23139         for(var i = startRow; i <= endRow; i++){
23140             this.deselectRow(i, preventViewNotify);
23141         }
23142     },
23143
23144     /**
23145      * Selects a row.
23146      * @param {Number} row The index of the row to select
23147      * @param {Boolean} keepExisting (optional) True to keep existing selections
23148      */
23149     selectRow : function(index, keepExisting, preventViewNotify)
23150     {
23151             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23152             return;
23153         }
23154         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23155             if(!keepExisting || this.singleSelect){
23156                 this.clearSelections();
23157             }
23158             
23159             var r = this.grid.store.getAt(index);
23160             //console.log('selectRow - record id :' + r.id);
23161             
23162             this.selections.add(r);
23163             this.last = this.lastActive = index;
23164             if(!preventViewNotify){
23165                 var proxy = new Roo.Element(
23166                                 this.grid.getRowDom(index)
23167                 );
23168                 proxy.addClass('bg-info info');
23169             }
23170             this.fireEvent("rowselect", this, index, r);
23171             this.fireEvent("selectionchange", this);
23172         }
23173     },
23174
23175     /**
23176      * Deselects a row.
23177      * @param {Number} row The index of the row to deselect
23178      */
23179     deselectRow : function(index, preventViewNotify)
23180     {
23181         if(this.locked) {
23182             return;
23183         }
23184         if(this.last == index){
23185             this.last = false;
23186         }
23187         if(this.lastActive == index){
23188             this.lastActive = false;
23189         }
23190         
23191         var r = this.grid.store.getAt(index);
23192         if (!r) {
23193             return;
23194         }
23195         
23196         this.selections.remove(r);
23197         //.console.log('deselectRow - record id :' + r.id);
23198         if(!preventViewNotify){
23199         
23200             var proxy = new Roo.Element(
23201                 this.grid.getRowDom(index)
23202             );
23203             proxy.removeClass('bg-info info');
23204         }
23205         this.fireEvent("rowdeselect", this, index);
23206         this.fireEvent("selectionchange", this);
23207     },
23208
23209     // private
23210     restoreLast : function(){
23211         if(this._last){
23212             this.last = this._last;
23213         }
23214     },
23215
23216     // private
23217     acceptsNav : function(row, col, cm){
23218         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23219     },
23220
23221     // private
23222     onEditorKey : function(field, e){
23223         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23224         if(k == e.TAB){
23225             e.stopEvent();
23226             ed.completeEdit();
23227             if(e.shiftKey){
23228                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23229             }else{
23230                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23231             }
23232         }else if(k == e.ENTER && !e.ctrlKey){
23233             e.stopEvent();
23234             ed.completeEdit();
23235             if(e.shiftKey){
23236                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23237             }else{
23238                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23239             }
23240         }else if(k == e.ESC){
23241             ed.cancelEdit();
23242         }
23243         if(newCell){
23244             g.startEditing(newCell[0], newCell[1]);
23245         }
23246     }
23247 });
23248 /*
23249  * Based on:
23250  * Ext JS Library 1.1.1
23251  * Copyright(c) 2006-2007, Ext JS, LLC.
23252  *
23253  * Originally Released Under LGPL - original licence link has changed is not relivant.
23254  *
23255  * Fork - LGPL
23256  * <script type="text/javascript">
23257  */
23258  
23259 /**
23260  * @class Roo.bootstrap.PagingToolbar
23261  * @extends Roo.bootstrap.NavSimplebar
23262  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23263  * @constructor
23264  * Create a new PagingToolbar
23265  * @param {Object} config The config object
23266  * @param {Roo.data.Store} store
23267  */
23268 Roo.bootstrap.PagingToolbar = function(config)
23269 {
23270     // old args format still supported... - xtype is prefered..
23271         // created from xtype...
23272     
23273     this.ds = config.dataSource;
23274     
23275     if (config.store && !this.ds) {
23276         this.store= Roo.factory(config.store, Roo.data);
23277         this.ds = this.store;
23278         this.ds.xmodule = this.xmodule || false;
23279     }
23280     
23281     this.toolbarItems = [];
23282     if (config.items) {
23283         this.toolbarItems = config.items;
23284     }
23285     
23286     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23287     
23288     this.cursor = 0;
23289     
23290     if (this.ds) { 
23291         this.bind(this.ds);
23292     }
23293     
23294     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23295     
23296 };
23297
23298 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23299     /**
23300      * @cfg {Roo.data.Store} dataSource
23301      * The underlying data store providing the paged data
23302      */
23303     /**
23304      * @cfg {String/HTMLElement/Element} container
23305      * container The id or element that will contain the toolbar
23306      */
23307     /**
23308      * @cfg {Boolean} displayInfo
23309      * True to display the displayMsg (defaults to false)
23310      */
23311     /**
23312      * @cfg {Number} pageSize
23313      * The number of records to display per page (defaults to 20)
23314      */
23315     pageSize: 20,
23316     /**
23317      * @cfg {String} displayMsg
23318      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23319      */
23320     displayMsg : 'Displaying {0} - {1} of {2}',
23321     /**
23322      * @cfg {String} emptyMsg
23323      * The message to display when no records are found (defaults to "No data to display")
23324      */
23325     emptyMsg : 'No data to display',
23326     /**
23327      * Customizable piece of the default paging text (defaults to "Page")
23328      * @type String
23329      */
23330     beforePageText : "Page",
23331     /**
23332      * Customizable piece of the default paging text (defaults to "of %0")
23333      * @type String
23334      */
23335     afterPageText : "of {0}",
23336     /**
23337      * Customizable piece of the default paging text (defaults to "First Page")
23338      * @type String
23339      */
23340     firstText : "First Page",
23341     /**
23342      * Customizable piece of the default paging text (defaults to "Previous Page")
23343      * @type String
23344      */
23345     prevText : "Previous Page",
23346     /**
23347      * Customizable piece of the default paging text (defaults to "Next Page")
23348      * @type String
23349      */
23350     nextText : "Next Page",
23351     /**
23352      * Customizable piece of the default paging text (defaults to "Last Page")
23353      * @type String
23354      */
23355     lastText : "Last Page",
23356     /**
23357      * Customizable piece of the default paging text (defaults to "Refresh")
23358      * @type String
23359      */
23360     refreshText : "Refresh",
23361
23362     buttons : false,
23363     // private
23364     onRender : function(ct, position) 
23365     {
23366         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23367         this.navgroup.parentId = this.id;
23368         this.navgroup.onRender(this.el, null);
23369         // add the buttons to the navgroup
23370         
23371         if(this.displayInfo){
23372             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23373             this.displayEl = this.el.select('.x-paging-info', true).first();
23374 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23375 //            this.displayEl = navel.el.select('span',true).first();
23376         }
23377         
23378         var _this = this;
23379         
23380         if(this.buttons){
23381             Roo.each(_this.buttons, function(e){ // this might need to use render????
23382                Roo.factory(e).onRender(_this.el, null);
23383             });
23384         }
23385             
23386         Roo.each(_this.toolbarItems, function(e) {
23387             _this.navgroup.addItem(e);
23388         });
23389         
23390         
23391         this.first = this.navgroup.addItem({
23392             tooltip: this.firstText,
23393             cls: "prev",
23394             icon : 'fa fa-backward',
23395             disabled: true,
23396             preventDefault: true,
23397             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23398         });
23399         
23400         this.prev =  this.navgroup.addItem({
23401             tooltip: this.prevText,
23402             cls: "prev",
23403             icon : 'fa fa-step-backward',
23404             disabled: true,
23405             preventDefault: true,
23406             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23407         });
23408     //this.addSeparator();
23409         
23410         
23411         var field = this.navgroup.addItem( {
23412             tagtype : 'span',
23413             cls : 'x-paging-position',
23414             
23415             html : this.beforePageText  +
23416                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23417                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23418          } ); //?? escaped?
23419         
23420         this.field = field.el.select('input', true).first();
23421         this.field.on("keydown", this.onPagingKeydown, this);
23422         this.field.on("focus", function(){this.dom.select();});
23423     
23424     
23425         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23426         //this.field.setHeight(18);
23427         //this.addSeparator();
23428         this.next = this.navgroup.addItem({
23429             tooltip: this.nextText,
23430             cls: "next",
23431             html : ' <i class="fa fa-step-forward">',
23432             disabled: true,
23433             preventDefault: true,
23434             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23435         });
23436         this.last = this.navgroup.addItem({
23437             tooltip: this.lastText,
23438             icon : 'fa fa-forward',
23439             cls: "next",
23440             disabled: true,
23441             preventDefault: true,
23442             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23443         });
23444     //this.addSeparator();
23445         this.loading = this.navgroup.addItem({
23446             tooltip: this.refreshText,
23447             icon: 'fa fa-refresh',
23448             preventDefault: true,
23449             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23450         });
23451         
23452     },
23453
23454     // private
23455     updateInfo : function(){
23456         if(this.displayEl){
23457             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23458             var msg = count == 0 ?
23459                 this.emptyMsg :
23460                 String.format(
23461                     this.displayMsg,
23462                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23463                 );
23464             this.displayEl.update(msg);
23465         }
23466     },
23467
23468     // private
23469     onLoad : function(ds, r, o){
23470        this.cursor = o.params ? o.params.start : 0;
23471        var d = this.getPageData(),
23472             ap = d.activePage,
23473             ps = d.pages;
23474         
23475        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23476        this.field.dom.value = ap;
23477        this.first.setDisabled(ap == 1);
23478        this.prev.setDisabled(ap == 1);
23479        this.next.setDisabled(ap == ps);
23480        this.last.setDisabled(ap == ps);
23481        this.loading.enable();
23482        this.updateInfo();
23483     },
23484
23485     // private
23486     getPageData : function(){
23487         var total = this.ds.getTotalCount();
23488         return {
23489             total : total,
23490             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23491             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23492         };
23493     },
23494
23495     // private
23496     onLoadError : function(){
23497         this.loading.enable();
23498     },
23499
23500     // private
23501     onPagingKeydown : function(e){
23502         var k = e.getKey();
23503         var d = this.getPageData();
23504         if(k == e.RETURN){
23505             var v = this.field.dom.value, pageNum;
23506             if(!v || isNaN(pageNum = parseInt(v, 10))){
23507                 this.field.dom.value = d.activePage;
23508                 return;
23509             }
23510             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23511             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23512             e.stopEvent();
23513         }
23514         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))
23515         {
23516           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23517           this.field.dom.value = pageNum;
23518           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23519           e.stopEvent();
23520         }
23521         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23522         {
23523           var v = this.field.dom.value, pageNum; 
23524           var increment = (e.shiftKey) ? 10 : 1;
23525           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23526                 increment *= -1;
23527           }
23528           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23529             this.field.dom.value = d.activePage;
23530             return;
23531           }
23532           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23533           {
23534             this.field.dom.value = parseInt(v, 10) + increment;
23535             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23536             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23537           }
23538           e.stopEvent();
23539         }
23540     },
23541
23542     // private
23543     beforeLoad : function(){
23544         if(this.loading){
23545             this.loading.disable();
23546         }
23547     },
23548
23549     // private
23550     onClick : function(which){
23551         
23552         var ds = this.ds;
23553         if (!ds) {
23554             return;
23555         }
23556         
23557         switch(which){
23558             case "first":
23559                 ds.load({params:{start: 0, limit: this.pageSize}});
23560             break;
23561             case "prev":
23562                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23563             break;
23564             case "next":
23565                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23566             break;
23567             case "last":
23568                 var total = ds.getTotalCount();
23569                 var extra = total % this.pageSize;
23570                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23571                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23572             break;
23573             case "refresh":
23574                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23575             break;
23576         }
23577     },
23578
23579     /**
23580      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23581      * @param {Roo.data.Store} store The data store to unbind
23582      */
23583     unbind : function(ds){
23584         ds.un("beforeload", this.beforeLoad, this);
23585         ds.un("load", this.onLoad, this);
23586         ds.un("loadexception", this.onLoadError, this);
23587         ds.un("remove", this.updateInfo, this);
23588         ds.un("add", this.updateInfo, this);
23589         this.ds = undefined;
23590     },
23591
23592     /**
23593      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23594      * @param {Roo.data.Store} store The data store to bind
23595      */
23596     bind : function(ds){
23597         ds.on("beforeload", this.beforeLoad, this);
23598         ds.on("load", this.onLoad, this);
23599         ds.on("loadexception", this.onLoadError, this);
23600         ds.on("remove", this.updateInfo, this);
23601         ds.on("add", this.updateInfo, this);
23602         this.ds = ds;
23603     }
23604 });/*
23605  * - LGPL
23606  *
23607  * element
23608  * 
23609  */
23610
23611 /**
23612  * @class Roo.bootstrap.MessageBar
23613  * @extends Roo.bootstrap.Component
23614  * Bootstrap MessageBar class
23615  * @cfg {String} html contents of the MessageBar
23616  * @cfg {String} weight (info | success | warning | danger) default info
23617  * @cfg {String} beforeClass insert the bar before the given class
23618  * @cfg {Boolean} closable (true | false) default false
23619  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23620  * 
23621  * @constructor
23622  * Create a new Element
23623  * @param {Object} config The config object
23624  */
23625
23626 Roo.bootstrap.MessageBar = function(config){
23627     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23628 };
23629
23630 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23631     
23632     html: '',
23633     weight: 'info',
23634     closable: false,
23635     fixed: false,
23636     beforeClass: 'bootstrap-sticky-wrap',
23637     
23638     getAutoCreate : function(){
23639         
23640         var cfg = {
23641             tag: 'div',
23642             cls: 'alert alert-dismissable alert-' + this.weight,
23643             cn: [
23644                 {
23645                     tag: 'span',
23646                     cls: 'message',
23647                     html: this.html || ''
23648                 }
23649             ]
23650         };
23651         
23652         if(this.fixed){
23653             cfg.cls += ' alert-messages-fixed';
23654         }
23655         
23656         if(this.closable){
23657             cfg.cn.push({
23658                 tag: 'button',
23659                 cls: 'close',
23660                 html: 'x'
23661             });
23662         }
23663         
23664         return cfg;
23665     },
23666     
23667     onRender : function(ct, position)
23668     {
23669         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23670         
23671         if(!this.el){
23672             var cfg = Roo.apply({},  this.getAutoCreate());
23673             cfg.id = Roo.id();
23674             
23675             if (this.cls) {
23676                 cfg.cls += ' ' + this.cls;
23677             }
23678             if (this.style) {
23679                 cfg.style = this.style;
23680             }
23681             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23682             
23683             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23684         }
23685         
23686         this.el.select('>button.close').on('click', this.hide, this);
23687         
23688     },
23689     
23690     show : function()
23691     {
23692         if (!this.rendered) {
23693             this.render();
23694         }
23695         
23696         this.el.show();
23697         
23698         this.fireEvent('show', this);
23699         
23700     },
23701     
23702     hide : function()
23703     {
23704         if (!this.rendered) {
23705             this.render();
23706         }
23707         
23708         this.el.hide();
23709         
23710         this.fireEvent('hide', this);
23711     },
23712     
23713     update : function()
23714     {
23715 //        var e = this.el.dom.firstChild;
23716 //        
23717 //        if(this.closable){
23718 //            e = e.nextSibling;
23719 //        }
23720 //        
23721 //        e.data = this.html || '';
23722
23723         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23724     }
23725    
23726 });
23727
23728  
23729
23730      /*
23731  * - LGPL
23732  *
23733  * Graph
23734  * 
23735  */
23736
23737
23738 /**
23739  * @class Roo.bootstrap.Graph
23740  * @extends Roo.bootstrap.Component
23741  * Bootstrap Graph class
23742 > Prameters
23743  -sm {number} sm 4
23744  -md {number} md 5
23745  @cfg {String} graphtype  bar | vbar | pie
23746  @cfg {number} g_x coodinator | centre x (pie)
23747  @cfg {number} g_y coodinator | centre y (pie)
23748  @cfg {number} g_r radius (pie)
23749  @cfg {number} g_height height of the chart (respected by all elements in the set)
23750  @cfg {number} g_width width of the chart (respected by all elements in the set)
23751  @cfg {Object} title The title of the chart
23752     
23753  -{Array}  values
23754  -opts (object) options for the chart 
23755      o {
23756      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23757      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23758      o vgutter (number)
23759      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.
23760      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23761      o to
23762      o stretch (boolean)
23763      o }
23764  -opts (object) options for the pie
23765      o{
23766      o cut
23767      o startAngle (number)
23768      o endAngle (number)
23769      } 
23770  *
23771  * @constructor
23772  * Create a new Input
23773  * @param {Object} config The config object
23774  */
23775
23776 Roo.bootstrap.Graph = function(config){
23777     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23778     
23779     this.addEvents({
23780         // img events
23781         /**
23782          * @event click
23783          * The img click event for the img.
23784          * @param {Roo.EventObject} e
23785          */
23786         "click" : true
23787     });
23788 };
23789
23790 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23791     
23792     sm: 4,
23793     md: 5,
23794     graphtype: 'bar',
23795     g_height: 250,
23796     g_width: 400,
23797     g_x: 50,
23798     g_y: 50,
23799     g_r: 30,
23800     opts:{
23801         //g_colors: this.colors,
23802         g_type: 'soft',
23803         g_gutter: '20%'
23804
23805     },
23806     title : false,
23807
23808     getAutoCreate : function(){
23809         
23810         var cfg = {
23811             tag: 'div',
23812             html : null
23813         };
23814         
23815         
23816         return  cfg;
23817     },
23818
23819     onRender : function(ct,position){
23820         
23821         
23822         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23823         
23824         if (typeof(Raphael) == 'undefined') {
23825             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23826             return;
23827         }
23828         
23829         this.raphael = Raphael(this.el.dom);
23830         
23831                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23832                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23833                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23834                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23835                 /*
23836                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23837                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23838                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23839                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23840                 
23841                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23842                 r.barchart(330, 10, 300, 220, data1);
23843                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23844                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23845                 */
23846                 
23847                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23848                 // r.barchart(30, 30, 560, 250,  xdata, {
23849                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23850                 //     axis : "0 0 1 1",
23851                 //     axisxlabels :  xdata
23852                 //     //yvalues : cols,
23853                    
23854                 // });
23855 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23856 //        
23857 //        this.load(null,xdata,{
23858 //                axis : "0 0 1 1",
23859 //                axisxlabels :  xdata
23860 //                });
23861
23862     },
23863
23864     load : function(graphtype,xdata,opts)
23865     {
23866         this.raphael.clear();
23867         if(!graphtype) {
23868             graphtype = this.graphtype;
23869         }
23870         if(!opts){
23871             opts = this.opts;
23872         }
23873         var r = this.raphael,
23874             fin = function () {
23875                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23876             },
23877             fout = function () {
23878                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23879             },
23880             pfin = function() {
23881                 this.sector.stop();
23882                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23883
23884                 if (this.label) {
23885                     this.label[0].stop();
23886                     this.label[0].attr({ r: 7.5 });
23887                     this.label[1].attr({ "font-weight": 800 });
23888                 }
23889             },
23890             pfout = function() {
23891                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23892
23893                 if (this.label) {
23894                     this.label[0].animate({ r: 5 }, 500, "bounce");
23895                     this.label[1].attr({ "font-weight": 400 });
23896                 }
23897             };
23898
23899         switch(graphtype){
23900             case 'bar':
23901                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23902                 break;
23903             case 'hbar':
23904                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23905                 break;
23906             case 'pie':
23907 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23908 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23909 //            
23910                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23911                 
23912                 break;
23913
23914         }
23915         
23916         if(this.title){
23917             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23918         }
23919         
23920     },
23921     
23922     setTitle: function(o)
23923     {
23924         this.title = o;
23925     },
23926     
23927     initEvents: function() {
23928         
23929         if(!this.href){
23930             this.el.on('click', this.onClick, this);
23931         }
23932     },
23933     
23934     onClick : function(e)
23935     {
23936         Roo.log('img onclick');
23937         this.fireEvent('click', this, e);
23938     }
23939    
23940 });
23941
23942  
23943 /*
23944  * - LGPL
23945  *
23946  * numberBox
23947  * 
23948  */
23949 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23950
23951 /**
23952  * @class Roo.bootstrap.dash.NumberBox
23953  * @extends Roo.bootstrap.Component
23954  * Bootstrap NumberBox class
23955  * @cfg {String} headline Box headline
23956  * @cfg {String} content Box content
23957  * @cfg {String} icon Box icon
23958  * @cfg {String} footer Footer text
23959  * @cfg {String} fhref Footer href
23960  * 
23961  * @constructor
23962  * Create a new NumberBox
23963  * @param {Object} config The config object
23964  */
23965
23966
23967 Roo.bootstrap.dash.NumberBox = function(config){
23968     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23969     
23970 };
23971
23972 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23973     
23974     headline : '',
23975     content : '',
23976     icon : '',
23977     footer : '',
23978     fhref : '',
23979     ficon : '',
23980     
23981     getAutoCreate : function(){
23982         
23983         var cfg = {
23984             tag : 'div',
23985             cls : 'small-box ',
23986             cn : [
23987                 {
23988                     tag : 'div',
23989                     cls : 'inner',
23990                     cn :[
23991                         {
23992                             tag : 'h3',
23993                             cls : 'roo-headline',
23994                             html : this.headline
23995                         },
23996                         {
23997                             tag : 'p',
23998                             cls : 'roo-content',
23999                             html : this.content
24000                         }
24001                     ]
24002                 }
24003             ]
24004         };
24005         
24006         if(this.icon){
24007             cfg.cn.push({
24008                 tag : 'div',
24009                 cls : 'icon',
24010                 cn :[
24011                     {
24012                         tag : 'i',
24013                         cls : 'ion ' + this.icon
24014                     }
24015                 ]
24016             });
24017         }
24018         
24019         if(this.footer){
24020             var footer = {
24021                 tag : 'a',
24022                 cls : 'small-box-footer',
24023                 href : this.fhref || '#',
24024                 html : this.footer
24025             };
24026             
24027             cfg.cn.push(footer);
24028             
24029         }
24030         
24031         return  cfg;
24032     },
24033
24034     onRender : function(ct,position){
24035         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24036
24037
24038        
24039                 
24040     },
24041
24042     setHeadline: function (value)
24043     {
24044         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24045     },
24046     
24047     setFooter: function (value, href)
24048     {
24049         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24050         
24051         if(href){
24052             this.el.select('a.small-box-footer',true).first().attr('href', href);
24053         }
24054         
24055     },
24056
24057     setContent: function (value)
24058     {
24059         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24060     },
24061
24062     initEvents: function() 
24063     {   
24064         
24065     }
24066     
24067 });
24068
24069  
24070 /*
24071  * - LGPL
24072  *
24073  * TabBox
24074  * 
24075  */
24076 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24077
24078 /**
24079  * @class Roo.bootstrap.dash.TabBox
24080  * @extends Roo.bootstrap.Component
24081  * Bootstrap TabBox class
24082  * @cfg {String} title Title of the TabBox
24083  * @cfg {String} icon Icon of the TabBox
24084  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24085  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24086  * 
24087  * @constructor
24088  * Create a new TabBox
24089  * @param {Object} config The config object
24090  */
24091
24092
24093 Roo.bootstrap.dash.TabBox = function(config){
24094     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24095     this.addEvents({
24096         // raw events
24097         /**
24098          * @event addpane
24099          * When a pane is added
24100          * @param {Roo.bootstrap.dash.TabPane} pane
24101          */
24102         "addpane" : true,
24103         /**
24104          * @event activatepane
24105          * When a pane is activated
24106          * @param {Roo.bootstrap.dash.TabPane} pane
24107          */
24108         "activatepane" : true
24109         
24110          
24111     });
24112     
24113     this.panes = [];
24114 };
24115
24116 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24117
24118     title : '',
24119     icon : false,
24120     showtabs : true,
24121     tabScrollable : false,
24122     
24123     getChildContainer : function()
24124     {
24125         return this.el.select('.tab-content', true).first();
24126     },
24127     
24128     getAutoCreate : function(){
24129         
24130         var header = {
24131             tag: 'li',
24132             cls: 'pull-left header',
24133             html: this.title,
24134             cn : []
24135         };
24136         
24137         if(this.icon){
24138             header.cn.push({
24139                 tag: 'i',
24140                 cls: 'fa ' + this.icon
24141             });
24142         }
24143         
24144         var h = {
24145             tag: 'ul',
24146             cls: 'nav nav-tabs pull-right',
24147             cn: [
24148                 header
24149             ]
24150         };
24151         
24152         if(this.tabScrollable){
24153             h = {
24154                 tag: 'div',
24155                 cls: 'tab-header',
24156                 cn: [
24157                     {
24158                         tag: 'ul',
24159                         cls: 'nav nav-tabs pull-right',
24160                         cn: [
24161                             header
24162                         ]
24163                     }
24164                 ]
24165             };
24166         }
24167         
24168         var cfg = {
24169             tag: 'div',
24170             cls: 'nav-tabs-custom',
24171             cn: [
24172                 h,
24173                 {
24174                     tag: 'div',
24175                     cls: 'tab-content no-padding',
24176                     cn: []
24177                 }
24178             ]
24179         };
24180
24181         return  cfg;
24182     },
24183     initEvents : function()
24184     {
24185         //Roo.log('add add pane handler');
24186         this.on('addpane', this.onAddPane, this);
24187     },
24188      /**
24189      * Updates the box title
24190      * @param {String} html to set the title to.
24191      */
24192     setTitle : function(value)
24193     {
24194         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24195     },
24196     onAddPane : function(pane)
24197     {
24198         this.panes.push(pane);
24199         //Roo.log('addpane');
24200         //Roo.log(pane);
24201         // tabs are rendere left to right..
24202         if(!this.showtabs){
24203             return;
24204         }
24205         
24206         var ctr = this.el.select('.nav-tabs', true).first();
24207          
24208          
24209         var existing = ctr.select('.nav-tab',true);
24210         var qty = existing.getCount();;
24211         
24212         
24213         var tab = ctr.createChild({
24214             tag : 'li',
24215             cls : 'nav-tab' + (qty ? '' : ' active'),
24216             cn : [
24217                 {
24218                     tag : 'a',
24219                     href:'#',
24220                     html : pane.title
24221                 }
24222             ]
24223         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24224         pane.tab = tab;
24225         
24226         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24227         if (!qty) {
24228             pane.el.addClass('active');
24229         }
24230         
24231                 
24232     },
24233     onTabClick : function(ev,un,ob,pane)
24234     {
24235         //Roo.log('tab - prev default');
24236         ev.preventDefault();
24237         
24238         
24239         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24240         pane.tab.addClass('active');
24241         //Roo.log(pane.title);
24242         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24243         // technically we should have a deactivate event.. but maybe add later.
24244         // and it should not de-activate the selected tab...
24245         this.fireEvent('activatepane', pane);
24246         pane.el.addClass('active');
24247         pane.fireEvent('activate');
24248         
24249         
24250     },
24251     
24252     getActivePane : function()
24253     {
24254         var r = false;
24255         Roo.each(this.panes, function(p) {
24256             if(p.el.hasClass('active')){
24257                 r = p;
24258                 return false;
24259             }
24260             
24261             return;
24262         });
24263         
24264         return r;
24265     }
24266     
24267     
24268 });
24269
24270  
24271 /*
24272  * - LGPL
24273  *
24274  * Tab pane
24275  * 
24276  */
24277 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24278 /**
24279  * @class Roo.bootstrap.TabPane
24280  * @extends Roo.bootstrap.Component
24281  * Bootstrap TabPane class
24282  * @cfg {Boolean} active (false | true) Default false
24283  * @cfg {String} title title of panel
24284
24285  * 
24286  * @constructor
24287  * Create a new TabPane
24288  * @param {Object} config The config object
24289  */
24290
24291 Roo.bootstrap.dash.TabPane = function(config){
24292     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24293     
24294     this.addEvents({
24295         // raw events
24296         /**
24297          * @event activate
24298          * When a pane is activated
24299          * @param {Roo.bootstrap.dash.TabPane} pane
24300          */
24301         "activate" : true
24302          
24303     });
24304 };
24305
24306 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24307     
24308     active : false,
24309     title : '',
24310     
24311     // the tabBox that this is attached to.
24312     tab : false,
24313      
24314     getAutoCreate : function() 
24315     {
24316         var cfg = {
24317             tag: 'div',
24318             cls: 'tab-pane'
24319         };
24320         
24321         if(this.active){
24322             cfg.cls += ' active';
24323         }
24324         
24325         return cfg;
24326     },
24327     initEvents  : function()
24328     {
24329         //Roo.log('trigger add pane handler');
24330         this.parent().fireEvent('addpane', this)
24331     },
24332     
24333      /**
24334      * Updates the tab title 
24335      * @param {String} html to set the title to.
24336      */
24337     setTitle: function(str)
24338     {
24339         if (!this.tab) {
24340             return;
24341         }
24342         this.title = str;
24343         this.tab.select('a', true).first().dom.innerHTML = str;
24344         
24345     }
24346     
24347     
24348     
24349 });
24350
24351  
24352
24353
24354  /*
24355  * - LGPL
24356  *
24357  * menu
24358  * 
24359  */
24360 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24361
24362 /**
24363  * @class Roo.bootstrap.menu.Menu
24364  * @extends Roo.bootstrap.Component
24365  * Bootstrap Menu class - container for Menu
24366  * @cfg {String} html Text of the menu
24367  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24368  * @cfg {String} icon Font awesome icon
24369  * @cfg {String} pos Menu align to (top | bottom) default bottom
24370  * 
24371  * 
24372  * @constructor
24373  * Create a new Menu
24374  * @param {Object} config The config object
24375  */
24376
24377
24378 Roo.bootstrap.menu.Menu = function(config){
24379     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24380     
24381     this.addEvents({
24382         /**
24383          * @event beforeshow
24384          * Fires before this menu is displayed
24385          * @param {Roo.bootstrap.menu.Menu} this
24386          */
24387         beforeshow : true,
24388         /**
24389          * @event beforehide
24390          * Fires before this menu is hidden
24391          * @param {Roo.bootstrap.menu.Menu} this
24392          */
24393         beforehide : true,
24394         /**
24395          * @event show
24396          * Fires after this menu is displayed
24397          * @param {Roo.bootstrap.menu.Menu} this
24398          */
24399         show : true,
24400         /**
24401          * @event hide
24402          * Fires after this menu is hidden
24403          * @param {Roo.bootstrap.menu.Menu} this
24404          */
24405         hide : true,
24406         /**
24407          * @event click
24408          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24409          * @param {Roo.bootstrap.menu.Menu} this
24410          * @param {Roo.EventObject} e
24411          */
24412         click : true
24413     });
24414     
24415 };
24416
24417 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24418     
24419     submenu : false,
24420     html : '',
24421     weight : 'default',
24422     icon : false,
24423     pos : 'bottom',
24424     
24425     
24426     getChildContainer : function() {
24427         if(this.isSubMenu){
24428             return this.el;
24429         }
24430         
24431         return this.el.select('ul.dropdown-menu', true).first();  
24432     },
24433     
24434     getAutoCreate : function()
24435     {
24436         var text = [
24437             {
24438                 tag : 'span',
24439                 cls : 'roo-menu-text',
24440                 html : this.html
24441             }
24442         ];
24443         
24444         if(this.icon){
24445             text.unshift({
24446                 tag : 'i',
24447                 cls : 'fa ' + this.icon
24448             })
24449         }
24450         
24451         
24452         var cfg = {
24453             tag : 'div',
24454             cls : 'btn-group',
24455             cn : [
24456                 {
24457                     tag : 'button',
24458                     cls : 'dropdown-button btn btn-' + this.weight,
24459                     cn : text
24460                 },
24461                 {
24462                     tag : 'button',
24463                     cls : 'dropdown-toggle btn btn-' + this.weight,
24464                     cn : [
24465                         {
24466                             tag : 'span',
24467                             cls : 'caret'
24468                         }
24469                     ]
24470                 },
24471                 {
24472                     tag : 'ul',
24473                     cls : 'dropdown-menu'
24474                 }
24475             ]
24476             
24477         };
24478         
24479         if(this.pos == 'top'){
24480             cfg.cls += ' dropup';
24481         }
24482         
24483         if(this.isSubMenu){
24484             cfg = {
24485                 tag : 'ul',
24486                 cls : 'dropdown-menu'
24487             }
24488         }
24489         
24490         return cfg;
24491     },
24492     
24493     onRender : function(ct, position)
24494     {
24495         this.isSubMenu = ct.hasClass('dropdown-submenu');
24496         
24497         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24498     },
24499     
24500     initEvents : function() 
24501     {
24502         if(this.isSubMenu){
24503             return;
24504         }
24505         
24506         this.hidden = true;
24507         
24508         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24509         this.triggerEl.on('click', this.onTriggerPress, this);
24510         
24511         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24512         this.buttonEl.on('click', this.onClick, this);
24513         
24514     },
24515     
24516     list : function()
24517     {
24518         if(this.isSubMenu){
24519             return this.el;
24520         }
24521         
24522         return this.el.select('ul.dropdown-menu', true).first();
24523     },
24524     
24525     onClick : function(e)
24526     {
24527         this.fireEvent("click", this, e);
24528     },
24529     
24530     onTriggerPress  : function(e)
24531     {   
24532         if (this.isVisible()) {
24533             this.hide();
24534         } else {
24535             this.show();
24536         }
24537     },
24538     
24539     isVisible : function(){
24540         return !this.hidden;
24541     },
24542     
24543     show : function()
24544     {
24545         this.fireEvent("beforeshow", this);
24546         
24547         this.hidden = false;
24548         this.el.addClass('open');
24549         
24550         Roo.get(document).on("mouseup", this.onMouseUp, this);
24551         
24552         this.fireEvent("show", this);
24553         
24554         
24555     },
24556     
24557     hide : function()
24558     {
24559         this.fireEvent("beforehide", this);
24560         
24561         this.hidden = true;
24562         this.el.removeClass('open');
24563         
24564         Roo.get(document).un("mouseup", this.onMouseUp);
24565         
24566         this.fireEvent("hide", this);
24567     },
24568     
24569     onMouseUp : function()
24570     {
24571         this.hide();
24572     }
24573     
24574 });
24575
24576  
24577  /*
24578  * - LGPL
24579  *
24580  * menu item
24581  * 
24582  */
24583 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24584
24585 /**
24586  * @class Roo.bootstrap.menu.Item
24587  * @extends Roo.bootstrap.Component
24588  * Bootstrap MenuItem class
24589  * @cfg {Boolean} submenu (true | false) default false
24590  * @cfg {String} html text of the item
24591  * @cfg {String} href the link
24592  * @cfg {Boolean} disable (true | false) default false
24593  * @cfg {Boolean} preventDefault (true | false) default true
24594  * @cfg {String} icon Font awesome icon
24595  * @cfg {String} pos Submenu align to (left | right) default right 
24596  * 
24597  * 
24598  * @constructor
24599  * Create a new Item
24600  * @param {Object} config The config object
24601  */
24602
24603
24604 Roo.bootstrap.menu.Item = function(config){
24605     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24606     this.addEvents({
24607         /**
24608          * @event mouseover
24609          * Fires when the mouse is hovering over this menu
24610          * @param {Roo.bootstrap.menu.Item} this
24611          * @param {Roo.EventObject} e
24612          */
24613         mouseover : true,
24614         /**
24615          * @event mouseout
24616          * Fires when the mouse exits this menu
24617          * @param {Roo.bootstrap.menu.Item} this
24618          * @param {Roo.EventObject} e
24619          */
24620         mouseout : true,
24621         // raw events
24622         /**
24623          * @event click
24624          * The raw click event for the entire grid.
24625          * @param {Roo.EventObject} e
24626          */
24627         click : true
24628     });
24629 };
24630
24631 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24632     
24633     submenu : false,
24634     href : '',
24635     html : '',
24636     preventDefault: true,
24637     disable : false,
24638     icon : false,
24639     pos : 'right',
24640     
24641     getAutoCreate : function()
24642     {
24643         var text = [
24644             {
24645                 tag : 'span',
24646                 cls : 'roo-menu-item-text',
24647                 html : this.html
24648             }
24649         ];
24650         
24651         if(this.icon){
24652             text.unshift({
24653                 tag : 'i',
24654                 cls : 'fa ' + this.icon
24655             })
24656         }
24657         
24658         var cfg = {
24659             tag : 'li',
24660             cn : [
24661                 {
24662                     tag : 'a',
24663                     href : this.href || '#',
24664                     cn : text
24665                 }
24666             ]
24667         };
24668         
24669         if(this.disable){
24670             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24671         }
24672         
24673         if(this.submenu){
24674             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24675             
24676             if(this.pos == 'left'){
24677                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24678             }
24679         }
24680         
24681         return cfg;
24682     },
24683     
24684     initEvents : function() 
24685     {
24686         this.el.on('mouseover', this.onMouseOver, this);
24687         this.el.on('mouseout', this.onMouseOut, this);
24688         
24689         this.el.select('a', true).first().on('click', this.onClick, this);
24690         
24691     },
24692     
24693     onClick : function(e)
24694     {
24695         if(this.preventDefault){
24696             e.preventDefault();
24697         }
24698         
24699         this.fireEvent("click", this, e);
24700     },
24701     
24702     onMouseOver : function(e)
24703     {
24704         if(this.submenu && this.pos == 'left'){
24705             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24706         }
24707         
24708         this.fireEvent("mouseover", this, e);
24709     },
24710     
24711     onMouseOut : function(e)
24712     {
24713         this.fireEvent("mouseout", this, e);
24714     }
24715 });
24716
24717  
24718
24719  /*
24720  * - LGPL
24721  *
24722  * menu separator
24723  * 
24724  */
24725 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24726
24727 /**
24728  * @class Roo.bootstrap.menu.Separator
24729  * @extends Roo.bootstrap.Component
24730  * Bootstrap Separator class
24731  * 
24732  * @constructor
24733  * Create a new Separator
24734  * @param {Object} config The config object
24735  */
24736
24737
24738 Roo.bootstrap.menu.Separator = function(config){
24739     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24740 };
24741
24742 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24743     
24744     getAutoCreate : function(){
24745         var cfg = {
24746             tag : 'li',
24747             cls: 'divider'
24748         };
24749         
24750         return cfg;
24751     }
24752    
24753 });
24754
24755  
24756
24757  /*
24758  * - LGPL
24759  *
24760  * Tooltip
24761  * 
24762  */
24763
24764 /**
24765  * @class Roo.bootstrap.Tooltip
24766  * Bootstrap Tooltip class
24767  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24768  * to determine which dom element triggers the tooltip.
24769  * 
24770  * It needs to add support for additional attributes like tooltip-position
24771  * 
24772  * @constructor
24773  * Create a new Toolti
24774  * @param {Object} config The config object
24775  */
24776
24777 Roo.bootstrap.Tooltip = function(config){
24778     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24779 };
24780
24781 Roo.apply(Roo.bootstrap.Tooltip, {
24782     /**
24783      * @function init initialize tooltip monitoring.
24784      * @static
24785      */
24786     currentEl : false,
24787     currentTip : false,
24788     currentRegion : false,
24789     
24790     //  init : delay?
24791     
24792     init : function()
24793     {
24794         Roo.get(document).on('mouseover', this.enter ,this);
24795         Roo.get(document).on('mouseout', this.leave, this);
24796          
24797         
24798         this.currentTip = new Roo.bootstrap.Tooltip();
24799     },
24800     
24801     enter : function(ev)
24802     {
24803         var dom = ev.getTarget();
24804         
24805         //Roo.log(['enter',dom]);
24806         var el = Roo.fly(dom);
24807         if (this.currentEl) {
24808             //Roo.log(dom);
24809             //Roo.log(this.currentEl);
24810             //Roo.log(this.currentEl.contains(dom));
24811             if (this.currentEl == el) {
24812                 return;
24813             }
24814             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24815                 return;
24816             }
24817
24818         }
24819         
24820         if (this.currentTip.el) {
24821             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24822         }    
24823         //Roo.log(ev);
24824         
24825         if(!el || el.dom == document){
24826             return;
24827         }
24828         
24829         var bindEl = el;
24830         
24831         // you can not look for children, as if el is the body.. then everythign is the child..
24832         if (!el.attr('tooltip')) { //
24833             if (!el.select("[tooltip]").elements.length) {
24834                 return;
24835             }
24836             // is the mouse over this child...?
24837             bindEl = el.select("[tooltip]").first();
24838             var xy = ev.getXY();
24839             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24840                 //Roo.log("not in region.");
24841                 return;
24842             }
24843             //Roo.log("child element over..");
24844             
24845         }
24846         this.currentEl = bindEl;
24847         this.currentTip.bind(bindEl);
24848         this.currentRegion = Roo.lib.Region.getRegion(dom);
24849         this.currentTip.enter();
24850         
24851     },
24852     leave : function(ev)
24853     {
24854         var dom = ev.getTarget();
24855         //Roo.log(['leave',dom]);
24856         if (!this.currentEl) {
24857             return;
24858         }
24859         
24860         
24861         if (dom != this.currentEl.dom) {
24862             return;
24863         }
24864         var xy = ev.getXY();
24865         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24866             return;
24867         }
24868         // only activate leave if mouse cursor is outside... bounding box..
24869         
24870         
24871         
24872         
24873         if (this.currentTip) {
24874             this.currentTip.leave();
24875         }
24876         //Roo.log('clear currentEl');
24877         this.currentEl = false;
24878         
24879         
24880     },
24881     alignment : {
24882         'left' : ['r-l', [-2,0], 'right'],
24883         'right' : ['l-r', [2,0], 'left'],
24884         'bottom' : ['t-b', [0,2], 'top'],
24885         'top' : [ 'b-t', [0,-2], 'bottom']
24886     }
24887     
24888 });
24889
24890
24891 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24892     
24893     
24894     bindEl : false,
24895     
24896     delay : null, // can be { show : 300 , hide: 500}
24897     
24898     timeout : null,
24899     
24900     hoverState : null, //???
24901     
24902     placement : 'bottom', 
24903     
24904     getAutoCreate : function(){
24905     
24906         var cfg = {
24907            cls : 'tooltip',
24908            role : 'tooltip',
24909            cn : [
24910                 {
24911                     cls : 'tooltip-arrow'
24912                 },
24913                 {
24914                     cls : 'tooltip-inner'
24915                 }
24916            ]
24917         };
24918         
24919         return cfg;
24920     },
24921     bind : function(el)
24922     {
24923         this.bindEl = el;
24924     },
24925       
24926     
24927     enter : function () {
24928        
24929         if (this.timeout != null) {
24930             clearTimeout(this.timeout);
24931         }
24932         
24933         this.hoverState = 'in';
24934          //Roo.log("enter - show");
24935         if (!this.delay || !this.delay.show) {
24936             this.show();
24937             return;
24938         }
24939         var _t = this;
24940         this.timeout = setTimeout(function () {
24941             if (_t.hoverState == 'in') {
24942                 _t.show();
24943             }
24944         }, this.delay.show);
24945     },
24946     leave : function()
24947     {
24948         clearTimeout(this.timeout);
24949     
24950         this.hoverState = 'out';
24951          if (!this.delay || !this.delay.hide) {
24952             this.hide();
24953             return;
24954         }
24955        
24956         var _t = this;
24957         this.timeout = setTimeout(function () {
24958             //Roo.log("leave - timeout");
24959             
24960             if (_t.hoverState == 'out') {
24961                 _t.hide();
24962                 Roo.bootstrap.Tooltip.currentEl = false;
24963             }
24964         }, delay);
24965     },
24966     
24967     show : function ()
24968     {
24969         if (!this.el) {
24970             this.render(document.body);
24971         }
24972         // set content.
24973         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24974         
24975         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24976         
24977         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24978         
24979         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24980         
24981         var placement = typeof this.placement == 'function' ?
24982             this.placement.call(this, this.el, on_el) :
24983             this.placement;
24984             
24985         var autoToken = /\s?auto?\s?/i;
24986         var autoPlace = autoToken.test(placement);
24987         if (autoPlace) {
24988             placement = placement.replace(autoToken, '') || 'top';
24989         }
24990         
24991         //this.el.detach()
24992         //this.el.setXY([0,0]);
24993         this.el.show();
24994         //this.el.dom.style.display='block';
24995         
24996         //this.el.appendTo(on_el);
24997         
24998         var p = this.getPosition();
24999         var box = this.el.getBox();
25000         
25001         if (autoPlace) {
25002             // fixme..
25003         }
25004         
25005         var align = Roo.bootstrap.Tooltip.alignment[placement];
25006         
25007         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25008         
25009         if(placement == 'top' || placement == 'bottom'){
25010             if(xy[0] < 0){
25011                 placement = 'right';
25012             }
25013             
25014             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25015                 placement = 'left';
25016             }
25017             
25018             var scroll = Roo.select('body', true).first().getScroll();
25019             
25020             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25021                 placement = 'top';
25022             }
25023             
25024         }
25025         
25026         align = Roo.bootstrap.Tooltip.alignment[placement];
25027         
25028         this.el.alignTo(this.bindEl, align[0],align[1]);
25029         //var arrow = this.el.select('.arrow',true).first();
25030         //arrow.set(align[2], 
25031         
25032         this.el.addClass(placement);
25033         
25034         this.el.addClass('in fade');
25035         
25036         this.hoverState = null;
25037         
25038         if (this.el.hasClass('fade')) {
25039             // fade it?
25040         }
25041         
25042     },
25043     hide : function()
25044     {
25045          
25046         if (!this.el) {
25047             return;
25048         }
25049         //this.el.setXY([0,0]);
25050         this.el.removeClass('in');
25051         //this.el.hide();
25052         
25053     }
25054     
25055 });
25056  
25057
25058  /*
25059  * - LGPL
25060  *
25061  * Location Picker
25062  * 
25063  */
25064
25065 /**
25066  * @class Roo.bootstrap.LocationPicker
25067  * @extends Roo.bootstrap.Component
25068  * Bootstrap LocationPicker class
25069  * @cfg {Number} latitude Position when init default 0
25070  * @cfg {Number} longitude Position when init default 0
25071  * @cfg {Number} zoom default 15
25072  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25073  * @cfg {Boolean} mapTypeControl default false
25074  * @cfg {Boolean} disableDoubleClickZoom default false
25075  * @cfg {Boolean} scrollwheel default true
25076  * @cfg {Boolean} streetViewControl default false
25077  * @cfg {Number} radius default 0
25078  * @cfg {String} locationName
25079  * @cfg {Boolean} draggable default true
25080  * @cfg {Boolean} enableAutocomplete default false
25081  * @cfg {Boolean} enableReverseGeocode default true
25082  * @cfg {String} markerTitle
25083  * 
25084  * @constructor
25085  * Create a new LocationPicker
25086  * @param {Object} config The config object
25087  */
25088
25089
25090 Roo.bootstrap.LocationPicker = function(config){
25091     
25092     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25093     
25094     this.addEvents({
25095         /**
25096          * @event initial
25097          * Fires when the picker initialized.
25098          * @param {Roo.bootstrap.LocationPicker} this
25099          * @param {Google Location} location
25100          */
25101         initial : true,
25102         /**
25103          * @event positionchanged
25104          * Fires when the picker position changed.
25105          * @param {Roo.bootstrap.LocationPicker} this
25106          * @param {Google Location} location
25107          */
25108         positionchanged : true,
25109         /**
25110          * @event resize
25111          * Fires when the map resize.
25112          * @param {Roo.bootstrap.LocationPicker} this
25113          */
25114         resize : true,
25115         /**
25116          * @event show
25117          * Fires when the map show.
25118          * @param {Roo.bootstrap.LocationPicker} this
25119          */
25120         show : true,
25121         /**
25122          * @event hide
25123          * Fires when the map hide.
25124          * @param {Roo.bootstrap.LocationPicker} this
25125          */
25126         hide : true,
25127         /**
25128          * @event mapClick
25129          * Fires when click the map.
25130          * @param {Roo.bootstrap.LocationPicker} this
25131          * @param {Map event} e
25132          */
25133         mapClick : true,
25134         /**
25135          * @event mapRightClick
25136          * Fires when right click the map.
25137          * @param {Roo.bootstrap.LocationPicker} this
25138          * @param {Map event} e
25139          */
25140         mapRightClick : true,
25141         /**
25142          * @event markerClick
25143          * Fires when click the marker.
25144          * @param {Roo.bootstrap.LocationPicker} this
25145          * @param {Map event} e
25146          */
25147         markerClick : true,
25148         /**
25149          * @event markerRightClick
25150          * Fires when right click the marker.
25151          * @param {Roo.bootstrap.LocationPicker} this
25152          * @param {Map event} e
25153          */
25154         markerRightClick : true,
25155         /**
25156          * @event OverlayViewDraw
25157          * Fires when OverlayView Draw
25158          * @param {Roo.bootstrap.LocationPicker} this
25159          */
25160         OverlayViewDraw : true,
25161         /**
25162          * @event OverlayViewOnAdd
25163          * Fires when OverlayView Draw
25164          * @param {Roo.bootstrap.LocationPicker} this
25165          */
25166         OverlayViewOnAdd : true,
25167         /**
25168          * @event OverlayViewOnRemove
25169          * Fires when OverlayView Draw
25170          * @param {Roo.bootstrap.LocationPicker} this
25171          */
25172         OverlayViewOnRemove : true,
25173         /**
25174          * @event OverlayViewShow
25175          * Fires when OverlayView Draw
25176          * @param {Roo.bootstrap.LocationPicker} this
25177          * @param {Pixel} cpx
25178          */
25179         OverlayViewShow : true,
25180         /**
25181          * @event OverlayViewHide
25182          * Fires when OverlayView Draw
25183          * @param {Roo.bootstrap.LocationPicker} this
25184          */
25185         OverlayViewHide : true,
25186         /**
25187          * @event loadexception
25188          * Fires when load google lib failed.
25189          * @param {Roo.bootstrap.LocationPicker} this
25190          */
25191         loadexception : true
25192     });
25193         
25194 };
25195
25196 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25197     
25198     gMapContext: false,
25199     
25200     latitude: 0,
25201     longitude: 0,
25202     zoom: 15,
25203     mapTypeId: false,
25204     mapTypeControl: false,
25205     disableDoubleClickZoom: false,
25206     scrollwheel: true,
25207     streetViewControl: false,
25208     radius: 0,
25209     locationName: '',
25210     draggable: true,
25211     enableAutocomplete: false,
25212     enableReverseGeocode: true,
25213     markerTitle: '',
25214     
25215     getAutoCreate: function()
25216     {
25217
25218         var cfg = {
25219             tag: 'div',
25220             cls: 'roo-location-picker'
25221         };
25222         
25223         return cfg
25224     },
25225     
25226     initEvents: function(ct, position)
25227     {       
25228         if(!this.el.getWidth() || this.isApplied()){
25229             return;
25230         }
25231         
25232         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25233         
25234         this.initial();
25235     },
25236     
25237     initial: function()
25238     {
25239         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25240             this.fireEvent('loadexception', this);
25241             return;
25242         }
25243         
25244         if(!this.mapTypeId){
25245             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25246         }
25247         
25248         this.gMapContext = this.GMapContext();
25249         
25250         this.initOverlayView();
25251         
25252         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25253         
25254         var _this = this;
25255                 
25256         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25257             _this.setPosition(_this.gMapContext.marker.position);
25258         });
25259         
25260         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25261             _this.fireEvent('mapClick', this, event);
25262             
25263         });
25264
25265         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25266             _this.fireEvent('mapRightClick', this, event);
25267             
25268         });
25269         
25270         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25271             _this.fireEvent('markerClick', this, event);
25272             
25273         });
25274
25275         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25276             _this.fireEvent('markerRightClick', this, event);
25277             
25278         });
25279         
25280         this.setPosition(this.gMapContext.location);
25281         
25282         this.fireEvent('initial', this, this.gMapContext.location);
25283     },
25284     
25285     initOverlayView: function()
25286     {
25287         var _this = this;
25288         
25289         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25290             
25291             draw: function()
25292             {
25293                 _this.fireEvent('OverlayViewDraw', _this);
25294             },
25295             
25296             onAdd: function()
25297             {
25298                 _this.fireEvent('OverlayViewOnAdd', _this);
25299             },
25300             
25301             onRemove: function()
25302             {
25303                 _this.fireEvent('OverlayViewOnRemove', _this);
25304             },
25305             
25306             show: function(cpx)
25307             {
25308                 _this.fireEvent('OverlayViewShow', _this, cpx);
25309             },
25310             
25311             hide: function()
25312             {
25313                 _this.fireEvent('OverlayViewHide', _this);
25314             }
25315             
25316         });
25317     },
25318     
25319     fromLatLngToContainerPixel: function(event)
25320     {
25321         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25322     },
25323     
25324     isApplied: function() 
25325     {
25326         return this.getGmapContext() == false ? false : true;
25327     },
25328     
25329     getGmapContext: function() 
25330     {
25331         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25332     },
25333     
25334     GMapContext: function() 
25335     {
25336         var position = new google.maps.LatLng(this.latitude, this.longitude);
25337         
25338         var _map = new google.maps.Map(this.el.dom, {
25339             center: position,
25340             zoom: this.zoom,
25341             mapTypeId: this.mapTypeId,
25342             mapTypeControl: this.mapTypeControl,
25343             disableDoubleClickZoom: this.disableDoubleClickZoom,
25344             scrollwheel: this.scrollwheel,
25345             streetViewControl: this.streetViewControl,
25346             locationName: this.locationName,
25347             draggable: this.draggable,
25348             enableAutocomplete: this.enableAutocomplete,
25349             enableReverseGeocode: this.enableReverseGeocode
25350         });
25351         
25352         var _marker = new google.maps.Marker({
25353             position: position,
25354             map: _map,
25355             title: this.markerTitle,
25356             draggable: this.draggable
25357         });
25358         
25359         return {
25360             map: _map,
25361             marker: _marker,
25362             circle: null,
25363             location: position,
25364             radius: this.radius,
25365             locationName: this.locationName,
25366             addressComponents: {
25367                 formatted_address: null,
25368                 addressLine1: null,
25369                 addressLine2: null,
25370                 streetName: null,
25371                 streetNumber: null,
25372                 city: null,
25373                 district: null,
25374                 state: null,
25375                 stateOrProvince: null
25376             },
25377             settings: this,
25378             domContainer: this.el.dom,
25379             geodecoder: new google.maps.Geocoder()
25380         };
25381     },
25382     
25383     drawCircle: function(center, radius, options) 
25384     {
25385         if (this.gMapContext.circle != null) {
25386             this.gMapContext.circle.setMap(null);
25387         }
25388         if (radius > 0) {
25389             radius *= 1;
25390             options = Roo.apply({}, options, {
25391                 strokeColor: "#0000FF",
25392                 strokeOpacity: .35,
25393                 strokeWeight: 2,
25394                 fillColor: "#0000FF",
25395                 fillOpacity: .2
25396             });
25397             
25398             options.map = this.gMapContext.map;
25399             options.radius = radius;
25400             options.center = center;
25401             this.gMapContext.circle = new google.maps.Circle(options);
25402             return this.gMapContext.circle;
25403         }
25404         
25405         return null;
25406     },
25407     
25408     setPosition: function(location) 
25409     {
25410         this.gMapContext.location = location;
25411         this.gMapContext.marker.setPosition(location);
25412         this.gMapContext.map.panTo(location);
25413         this.drawCircle(location, this.gMapContext.radius, {});
25414         
25415         var _this = this;
25416         
25417         if (this.gMapContext.settings.enableReverseGeocode) {
25418             this.gMapContext.geodecoder.geocode({
25419                 latLng: this.gMapContext.location
25420             }, function(results, status) {
25421                 
25422                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25423                     _this.gMapContext.locationName = results[0].formatted_address;
25424                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25425                     
25426                     _this.fireEvent('positionchanged', this, location);
25427                 }
25428             });
25429             
25430             return;
25431         }
25432         
25433         this.fireEvent('positionchanged', this, location);
25434     },
25435     
25436     resize: function()
25437     {
25438         google.maps.event.trigger(this.gMapContext.map, "resize");
25439         
25440         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25441         
25442         this.fireEvent('resize', this);
25443     },
25444     
25445     setPositionByLatLng: function(latitude, longitude)
25446     {
25447         this.setPosition(new google.maps.LatLng(latitude, longitude));
25448     },
25449     
25450     getCurrentPosition: function() 
25451     {
25452         return {
25453             latitude: this.gMapContext.location.lat(),
25454             longitude: this.gMapContext.location.lng()
25455         };
25456     },
25457     
25458     getAddressName: function() 
25459     {
25460         return this.gMapContext.locationName;
25461     },
25462     
25463     getAddressComponents: function() 
25464     {
25465         return this.gMapContext.addressComponents;
25466     },
25467     
25468     address_component_from_google_geocode: function(address_components) 
25469     {
25470         var result = {};
25471         
25472         for (var i = 0; i < address_components.length; i++) {
25473             var component = address_components[i];
25474             if (component.types.indexOf("postal_code") >= 0) {
25475                 result.postalCode = component.short_name;
25476             } else if (component.types.indexOf("street_number") >= 0) {
25477                 result.streetNumber = component.short_name;
25478             } else if (component.types.indexOf("route") >= 0) {
25479                 result.streetName = component.short_name;
25480             } else if (component.types.indexOf("neighborhood") >= 0) {
25481                 result.city = component.short_name;
25482             } else if (component.types.indexOf("locality") >= 0) {
25483                 result.city = component.short_name;
25484             } else if (component.types.indexOf("sublocality") >= 0) {
25485                 result.district = component.short_name;
25486             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25487                 result.stateOrProvince = component.short_name;
25488             } else if (component.types.indexOf("country") >= 0) {
25489                 result.country = component.short_name;
25490             }
25491         }
25492         
25493         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25494         result.addressLine2 = "";
25495         return result;
25496     },
25497     
25498     setZoomLevel: function(zoom)
25499     {
25500         this.gMapContext.map.setZoom(zoom);
25501     },
25502     
25503     show: function()
25504     {
25505         if(!this.el){
25506             return;
25507         }
25508         
25509         this.el.show();
25510         
25511         this.resize();
25512         
25513         this.fireEvent('show', this);
25514     },
25515     
25516     hide: function()
25517     {
25518         if(!this.el){
25519             return;
25520         }
25521         
25522         this.el.hide();
25523         
25524         this.fireEvent('hide', this);
25525     }
25526     
25527 });
25528
25529 Roo.apply(Roo.bootstrap.LocationPicker, {
25530     
25531     OverlayView : function(map, options)
25532     {
25533         options = options || {};
25534         
25535         this.setMap(map);
25536     }
25537     
25538     
25539 });/*
25540  * - LGPL
25541  *
25542  * Alert
25543  * 
25544  */
25545
25546 /**
25547  * @class Roo.bootstrap.Alert
25548  * @extends Roo.bootstrap.Component
25549  * Bootstrap Alert class
25550  * @cfg {String} title The title of alert
25551  * @cfg {String} html The content of alert
25552  * @cfg {String} weight (  success | info | warning | danger )
25553  * @cfg {String} faicon font-awesomeicon
25554  * 
25555  * @constructor
25556  * Create a new alert
25557  * @param {Object} config The config object
25558  */
25559
25560
25561 Roo.bootstrap.Alert = function(config){
25562     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25563     
25564 };
25565
25566 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25567     
25568     title: '',
25569     html: '',
25570     weight: false,
25571     faicon: false,
25572     
25573     getAutoCreate : function()
25574     {
25575         
25576         var cfg = {
25577             tag : 'div',
25578             cls : 'alert',
25579             cn : [
25580                 {
25581                     tag : 'i',
25582                     cls : 'roo-alert-icon'
25583                     
25584                 },
25585                 {
25586                     tag : 'b',
25587                     cls : 'roo-alert-title',
25588                     html : this.title
25589                 },
25590                 {
25591                     tag : 'span',
25592                     cls : 'roo-alert-text',
25593                     html : this.html
25594                 }
25595             ]
25596         };
25597         
25598         if(this.faicon){
25599             cfg.cn[0].cls += ' fa ' + this.faicon;
25600         }
25601         
25602         if(this.weight){
25603             cfg.cls += ' alert-' + this.weight;
25604         }
25605         
25606         return cfg;
25607     },
25608     
25609     initEvents: function() 
25610     {
25611         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25612     },
25613     
25614     setTitle : function(str)
25615     {
25616         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25617     },
25618     
25619     setText : function(str)
25620     {
25621         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25622     },
25623     
25624     setWeight : function(weight)
25625     {
25626         if(this.weight){
25627             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25628         }
25629         
25630         this.weight = weight;
25631         
25632         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25633     },
25634     
25635     setIcon : function(icon)
25636     {
25637         if(this.faicon){
25638             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25639         }
25640         
25641         this.faicon = icon;
25642         
25643         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25644     },
25645     
25646     hide: function() 
25647     {
25648         this.el.hide();   
25649     },
25650     
25651     show: function() 
25652     {  
25653         this.el.show();   
25654     }
25655     
25656 });
25657
25658  
25659 /*
25660 * Licence: LGPL
25661 */
25662
25663 /**
25664  * @class Roo.bootstrap.UploadCropbox
25665  * @extends Roo.bootstrap.Component
25666  * Bootstrap UploadCropbox class
25667  * @cfg {String} emptyText show when image has been loaded
25668  * @cfg {String} rotateNotify show when image too small to rotate
25669  * @cfg {Number} errorTimeout default 3000
25670  * @cfg {Number} minWidth default 300
25671  * @cfg {Number} minHeight default 300
25672  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25673  * @cfg {Boolean} isDocument (true|false) default false
25674  * @cfg {String} url action url
25675  * @cfg {String} paramName default 'imageUpload'
25676  * @cfg {String} method default POST
25677  * @cfg {Boolean} loadMask (true|false) default true
25678  * @cfg {Boolean} loadingText default 'Loading...'
25679  * 
25680  * @constructor
25681  * Create a new UploadCropbox
25682  * @param {Object} config The config object
25683  */
25684
25685 Roo.bootstrap.UploadCropbox = function(config){
25686     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25687     
25688     this.addEvents({
25689         /**
25690          * @event beforeselectfile
25691          * Fire before select file
25692          * @param {Roo.bootstrap.UploadCropbox} this
25693          */
25694         "beforeselectfile" : true,
25695         /**
25696          * @event initial
25697          * Fire after initEvent
25698          * @param {Roo.bootstrap.UploadCropbox} this
25699          */
25700         "initial" : true,
25701         /**
25702          * @event crop
25703          * Fire after initEvent
25704          * @param {Roo.bootstrap.UploadCropbox} this
25705          * @param {String} data
25706          */
25707         "crop" : true,
25708         /**
25709          * @event prepare
25710          * Fire when preparing the file data
25711          * @param {Roo.bootstrap.UploadCropbox} this
25712          * @param {Object} file
25713          */
25714         "prepare" : true,
25715         /**
25716          * @event exception
25717          * Fire when get exception
25718          * @param {Roo.bootstrap.UploadCropbox} this
25719          * @param {XMLHttpRequest} xhr
25720          */
25721         "exception" : true,
25722         /**
25723          * @event beforeloadcanvas
25724          * Fire before load the canvas
25725          * @param {Roo.bootstrap.UploadCropbox} this
25726          * @param {String} src
25727          */
25728         "beforeloadcanvas" : true,
25729         /**
25730          * @event trash
25731          * Fire when trash image
25732          * @param {Roo.bootstrap.UploadCropbox} this
25733          */
25734         "trash" : true,
25735         /**
25736          * @event download
25737          * Fire when download the image
25738          * @param {Roo.bootstrap.UploadCropbox} this
25739          */
25740         "download" : true,
25741         /**
25742          * @event footerbuttonclick
25743          * Fire when footerbuttonclick
25744          * @param {Roo.bootstrap.UploadCropbox} this
25745          * @param {String} type
25746          */
25747         "footerbuttonclick" : true,
25748         /**
25749          * @event resize
25750          * Fire when resize
25751          * @param {Roo.bootstrap.UploadCropbox} this
25752          */
25753         "resize" : true,
25754         /**
25755          * @event rotate
25756          * Fire when rotate the image
25757          * @param {Roo.bootstrap.UploadCropbox} this
25758          * @param {String} pos
25759          */
25760         "rotate" : true,
25761         /**
25762          * @event inspect
25763          * Fire when inspect the file
25764          * @param {Roo.bootstrap.UploadCropbox} this
25765          * @param {Object} file
25766          */
25767         "inspect" : true,
25768         /**
25769          * @event upload
25770          * Fire when xhr upload the file
25771          * @param {Roo.bootstrap.UploadCropbox} this
25772          * @param {Object} data
25773          */
25774         "upload" : true,
25775         /**
25776          * @event arrange
25777          * Fire when arrange the file data
25778          * @param {Roo.bootstrap.UploadCropbox} this
25779          * @param {Object} formData
25780          */
25781         "arrange" : true
25782     });
25783     
25784     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25785 };
25786
25787 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25788     
25789     emptyText : 'Click to upload image',
25790     rotateNotify : 'Image is too small to rotate',
25791     errorTimeout : 3000,
25792     scale : 0,
25793     baseScale : 1,
25794     rotate : 0,
25795     dragable : false,
25796     pinching : false,
25797     mouseX : 0,
25798     mouseY : 0,
25799     cropData : false,
25800     minWidth : 300,
25801     minHeight : 300,
25802     file : false,
25803     exif : {},
25804     baseRotate : 1,
25805     cropType : 'image/jpeg',
25806     buttons : false,
25807     canvasLoaded : false,
25808     isDocument : false,
25809     method : 'POST',
25810     paramName : 'imageUpload',
25811     loadMask : true,
25812     loadingText : 'Loading...',
25813     maskEl : false,
25814     
25815     getAutoCreate : function()
25816     {
25817         var cfg = {
25818             tag : 'div',
25819             cls : 'roo-upload-cropbox',
25820             cn : [
25821                 {
25822                     tag : 'input',
25823                     cls : 'roo-upload-cropbox-selector',
25824                     type : 'file'
25825                 },
25826                 {
25827                     tag : 'div',
25828                     cls : 'roo-upload-cropbox-body',
25829                     style : 'cursor:pointer',
25830                     cn : [
25831                         {
25832                             tag : 'div',
25833                             cls : 'roo-upload-cropbox-preview'
25834                         },
25835                         {
25836                             tag : 'div',
25837                             cls : 'roo-upload-cropbox-thumb'
25838                         },
25839                         {
25840                             tag : 'div',
25841                             cls : 'roo-upload-cropbox-empty-notify',
25842                             html : this.emptyText
25843                         },
25844                         {
25845                             tag : 'div',
25846                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25847                             html : this.rotateNotify
25848                         }
25849                     ]
25850                 },
25851                 {
25852                     tag : 'div',
25853                     cls : 'roo-upload-cropbox-footer',
25854                     cn : {
25855                         tag : 'div',
25856                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25857                         cn : []
25858                     }
25859                 }
25860             ]
25861         };
25862         
25863         return cfg;
25864     },
25865     
25866     onRender : function(ct, position)
25867     {
25868         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25869         
25870         if (this.buttons.length) {
25871             
25872             Roo.each(this.buttons, function(bb) {
25873                 
25874                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25875                 
25876                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25877                 
25878             }, this);
25879         }
25880         
25881         if(this.loadMask){
25882             this.maskEl = this.el;
25883         }
25884     },
25885     
25886     initEvents : function()
25887     {
25888         this.urlAPI = (window.createObjectURL && window) || 
25889                                 (window.URL && URL.revokeObjectURL && URL) || 
25890                                 (window.webkitURL && webkitURL);
25891                         
25892         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25893         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25894         
25895         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25896         this.selectorEl.hide();
25897         
25898         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25899         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25900         
25901         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25902         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25903         this.thumbEl.hide();
25904         
25905         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25906         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25907         
25908         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25909         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25910         this.errorEl.hide();
25911         
25912         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25913         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25914         this.footerEl.hide();
25915         
25916         this.setThumbBoxSize();
25917         
25918         this.bind();
25919         
25920         this.resize();
25921         
25922         this.fireEvent('initial', this);
25923     },
25924
25925     bind : function()
25926     {
25927         var _this = this;
25928         
25929         window.addEventListener("resize", function() { _this.resize(); } );
25930         
25931         this.bodyEl.on('click', this.beforeSelectFile, this);
25932         
25933         if(Roo.isTouch){
25934             this.bodyEl.on('touchstart', this.onTouchStart, this);
25935             this.bodyEl.on('touchmove', this.onTouchMove, this);
25936             this.bodyEl.on('touchend', this.onTouchEnd, this);
25937         }
25938         
25939         if(!Roo.isTouch){
25940             this.bodyEl.on('mousedown', this.onMouseDown, this);
25941             this.bodyEl.on('mousemove', this.onMouseMove, this);
25942             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25943             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25944             Roo.get(document).on('mouseup', this.onMouseUp, this);
25945         }
25946         
25947         this.selectorEl.on('change', this.onFileSelected, this);
25948     },
25949     
25950     reset : function()
25951     {    
25952         this.scale = 0;
25953         this.baseScale = 1;
25954         this.rotate = 0;
25955         this.baseRotate = 1;
25956         this.dragable = false;
25957         this.pinching = false;
25958         this.mouseX = 0;
25959         this.mouseY = 0;
25960         this.cropData = false;
25961         this.notifyEl.dom.innerHTML = this.emptyText;
25962         
25963         this.selectorEl.dom.value = '';
25964         
25965     },
25966     
25967     resize : function()
25968     {
25969         if(this.fireEvent('resize', this) != false){
25970             this.setThumbBoxPosition();
25971             this.setCanvasPosition();
25972         }
25973     },
25974     
25975     onFooterButtonClick : function(e, el, o, type)
25976     {
25977         switch (type) {
25978             case 'rotate-left' :
25979                 this.onRotateLeft(e);
25980                 break;
25981             case 'rotate-right' :
25982                 this.onRotateRight(e);
25983                 break;
25984             case 'picture' :
25985                 this.beforeSelectFile(e);
25986                 break;
25987             case 'trash' :
25988                 this.trash(e);
25989                 break;
25990             case 'crop' :
25991                 this.crop(e);
25992                 break;
25993             case 'download' :
25994                 this.download(e);
25995                 break;
25996             default :
25997                 break;
25998         }
25999         
26000         this.fireEvent('footerbuttonclick', this, type);
26001     },
26002     
26003     beforeSelectFile : function(e)
26004     {
26005         e.preventDefault();
26006         
26007         if(this.fireEvent('beforeselectfile', this) != false){
26008             this.selectorEl.dom.click();
26009         }
26010     },
26011     
26012     onFileSelected : function(e)
26013     {
26014         e.preventDefault();
26015         
26016         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26017             return;
26018         }
26019         
26020         var file = this.selectorEl.dom.files[0];
26021         
26022         if(this.fireEvent('inspect', this, file) != false){
26023             this.prepare(file);
26024         }
26025         
26026     },
26027     
26028     trash : function(e)
26029     {
26030         this.fireEvent('trash', this);
26031     },
26032     
26033     download : function(e)
26034     {
26035         this.fireEvent('download', this);
26036     },
26037     
26038     loadCanvas : function(src)
26039     {   
26040         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26041             
26042             this.reset();
26043             
26044             this.imageEl = document.createElement('img');
26045             
26046             var _this = this;
26047             
26048             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26049             
26050             this.imageEl.src = src;
26051         }
26052     },
26053     
26054     onLoadCanvas : function()
26055     {   
26056         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26057         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26058         
26059         this.bodyEl.un('click', this.beforeSelectFile, this);
26060         
26061         this.notifyEl.hide();
26062         this.thumbEl.show();
26063         this.footerEl.show();
26064         
26065         this.baseRotateLevel();
26066         
26067         if(this.isDocument){
26068             this.setThumbBoxSize();
26069         }
26070         
26071         this.setThumbBoxPosition();
26072         
26073         this.baseScaleLevel();
26074         
26075         this.draw();
26076         
26077         this.resize();
26078         
26079         this.canvasLoaded = true;
26080         
26081         if(this.loadMask){
26082             this.maskEl.unmask();
26083         }
26084         
26085     },
26086     
26087     setCanvasPosition : function()
26088     {   
26089         if(!this.canvasEl){
26090             return;
26091         }
26092         
26093         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26094         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26095         
26096         this.previewEl.setLeft(pw);
26097         this.previewEl.setTop(ph);
26098         
26099     },
26100     
26101     onMouseDown : function(e)
26102     {   
26103         e.stopEvent();
26104         
26105         this.dragable = true;
26106         this.pinching = false;
26107         
26108         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26109             this.dragable = false;
26110             return;
26111         }
26112         
26113         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26114         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26115         
26116     },
26117     
26118     onMouseMove : function(e)
26119     {   
26120         e.stopEvent();
26121         
26122         if(!this.canvasLoaded){
26123             return;
26124         }
26125         
26126         if (!this.dragable){
26127             return;
26128         }
26129         
26130         var minX = Math.ceil(this.thumbEl.getLeft(true));
26131         var minY = Math.ceil(this.thumbEl.getTop(true));
26132         
26133         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26134         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26135         
26136         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26137         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26138         
26139         x = x - this.mouseX;
26140         y = y - this.mouseY;
26141         
26142         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26143         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26144         
26145         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26146         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26147         
26148         this.previewEl.setLeft(bgX);
26149         this.previewEl.setTop(bgY);
26150         
26151         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26152         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26153     },
26154     
26155     onMouseUp : function(e)
26156     {   
26157         e.stopEvent();
26158         
26159         this.dragable = false;
26160     },
26161     
26162     onMouseWheel : function(e)
26163     {   
26164         e.stopEvent();
26165         
26166         this.startScale = this.scale;
26167         
26168         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26169         
26170         if(!this.zoomable()){
26171             this.scale = this.startScale;
26172             return;
26173         }
26174         
26175         this.draw();
26176         
26177         return;
26178     },
26179     
26180     zoomable : function()
26181     {
26182         var minScale = this.thumbEl.getWidth() / this.minWidth;
26183         
26184         if(this.minWidth < this.minHeight){
26185             minScale = this.thumbEl.getHeight() / this.minHeight;
26186         }
26187         
26188         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26189         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26190         
26191         if(
26192                 this.isDocument &&
26193                 (this.rotate == 0 || this.rotate == 180) && 
26194                 (
26195                     width > this.imageEl.OriginWidth || 
26196                     height > this.imageEl.OriginHeight ||
26197                     (width < this.minWidth && height < this.minHeight)
26198                 )
26199         ){
26200             return false;
26201         }
26202         
26203         if(
26204                 this.isDocument &&
26205                 (this.rotate == 90 || this.rotate == 270) && 
26206                 (
26207                     width > this.imageEl.OriginWidth || 
26208                     height > this.imageEl.OriginHeight ||
26209                     (width < this.minHeight && height < this.minWidth)
26210                 )
26211         ){
26212             return false;
26213         }
26214         
26215         if(
26216                 !this.isDocument &&
26217                 (this.rotate == 0 || this.rotate == 180) && 
26218                 (
26219                     width < this.minWidth || 
26220                     width > this.imageEl.OriginWidth || 
26221                     height < this.minHeight || 
26222                     height > this.imageEl.OriginHeight
26223                 )
26224         ){
26225             return false;
26226         }
26227         
26228         if(
26229                 !this.isDocument &&
26230                 (this.rotate == 90 || this.rotate == 270) && 
26231                 (
26232                     width < this.minHeight || 
26233                     width > this.imageEl.OriginWidth || 
26234                     height < this.minWidth || 
26235                     height > this.imageEl.OriginHeight
26236                 )
26237         ){
26238             return false;
26239         }
26240         
26241         return true;
26242         
26243     },
26244     
26245     onRotateLeft : function(e)
26246     {   
26247         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26248             
26249             var minScale = this.thumbEl.getWidth() / this.minWidth;
26250             
26251             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26252             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26253             
26254             this.startScale = this.scale;
26255             
26256             while (this.getScaleLevel() < minScale){
26257             
26258                 this.scale = this.scale + 1;
26259                 
26260                 if(!this.zoomable()){
26261                     break;
26262                 }
26263                 
26264                 if(
26265                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26266                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26267                 ){
26268                     continue;
26269                 }
26270                 
26271                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26272
26273                 this.draw();
26274                 
26275                 return;
26276             }
26277             
26278             this.scale = this.startScale;
26279             
26280             this.onRotateFail();
26281             
26282             return false;
26283         }
26284         
26285         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26286
26287         if(this.isDocument){
26288             this.setThumbBoxSize();
26289             this.setThumbBoxPosition();
26290             this.setCanvasPosition();
26291         }
26292         
26293         this.draw();
26294         
26295         this.fireEvent('rotate', this, 'left');
26296         
26297     },
26298     
26299     onRotateRight : function(e)
26300     {
26301         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26302             
26303             var minScale = this.thumbEl.getWidth() / this.minWidth;
26304         
26305             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26306             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26307             
26308             this.startScale = this.scale;
26309             
26310             while (this.getScaleLevel() < minScale){
26311             
26312                 this.scale = this.scale + 1;
26313                 
26314                 if(!this.zoomable()){
26315                     break;
26316                 }
26317                 
26318                 if(
26319                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26320                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26321                 ){
26322                     continue;
26323                 }
26324                 
26325                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26326
26327                 this.draw();
26328                 
26329                 return;
26330             }
26331             
26332             this.scale = this.startScale;
26333             
26334             this.onRotateFail();
26335             
26336             return false;
26337         }
26338         
26339         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26340
26341         if(this.isDocument){
26342             this.setThumbBoxSize();
26343             this.setThumbBoxPosition();
26344             this.setCanvasPosition();
26345         }
26346         
26347         this.draw();
26348         
26349         this.fireEvent('rotate', this, 'right');
26350     },
26351     
26352     onRotateFail : function()
26353     {
26354         this.errorEl.show(true);
26355         
26356         var _this = this;
26357         
26358         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26359     },
26360     
26361     draw : function()
26362     {
26363         this.previewEl.dom.innerHTML = '';
26364         
26365         var canvasEl = document.createElement("canvas");
26366         
26367         var contextEl = canvasEl.getContext("2d");
26368         
26369         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26370         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26371         var center = this.imageEl.OriginWidth / 2;
26372         
26373         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26374             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26375             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26376             center = this.imageEl.OriginHeight / 2;
26377         }
26378         
26379         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26380         
26381         contextEl.translate(center, center);
26382         contextEl.rotate(this.rotate * Math.PI / 180);
26383
26384         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26385         
26386         this.canvasEl = document.createElement("canvas");
26387         
26388         this.contextEl = this.canvasEl.getContext("2d");
26389         
26390         switch (this.rotate) {
26391             case 0 :
26392                 
26393                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26394                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26395                 
26396                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26397                 
26398                 break;
26399             case 90 : 
26400                 
26401                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26402                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26403                 
26404                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26405                     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);
26406                     break;
26407                 }
26408                 
26409                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26410                 
26411                 break;
26412             case 180 :
26413                 
26414                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26415                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26416                 
26417                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26418                     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);
26419                     break;
26420                 }
26421                 
26422                 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);
26423                 
26424                 break;
26425             case 270 :
26426                 
26427                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26428                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26429         
26430                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26431                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26432                     break;
26433                 }
26434                 
26435                 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);
26436                 
26437                 break;
26438             default : 
26439                 break;
26440         }
26441         
26442         this.previewEl.appendChild(this.canvasEl);
26443         
26444         this.setCanvasPosition();
26445     },
26446     
26447     crop : function()
26448     {
26449         if(!this.canvasLoaded){
26450             return;
26451         }
26452         
26453         var imageCanvas = document.createElement("canvas");
26454         
26455         var imageContext = imageCanvas.getContext("2d");
26456         
26457         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26458         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26459         
26460         var center = imageCanvas.width / 2;
26461         
26462         imageContext.translate(center, center);
26463         
26464         imageContext.rotate(this.rotate * Math.PI / 180);
26465         
26466         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26467         
26468         var canvas = document.createElement("canvas");
26469         
26470         var context = canvas.getContext("2d");
26471                 
26472         canvas.width = this.minWidth;
26473         canvas.height = this.minHeight;
26474
26475         switch (this.rotate) {
26476             case 0 :
26477                 
26478                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26479                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26480                 
26481                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26482                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26483                 
26484                 var targetWidth = this.minWidth - 2 * x;
26485                 var targetHeight = this.minHeight - 2 * y;
26486                 
26487                 var scale = 1;
26488                 
26489                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26490                     scale = targetWidth / width;
26491                 }
26492                 
26493                 if(x > 0 && y == 0){
26494                     scale = targetHeight / height;
26495                 }
26496                 
26497                 if(x > 0 && y > 0){
26498                     scale = targetWidth / width;
26499                     
26500                     if(width < height){
26501                         scale = targetHeight / height;
26502                     }
26503                 }
26504                 
26505                 context.scale(scale, scale);
26506                 
26507                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26508                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26509
26510                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26511                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26512
26513                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26514                 
26515                 break;
26516             case 90 : 
26517                 
26518                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26519                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26520                 
26521                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26522                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26523                 
26524                 var targetWidth = this.minWidth - 2 * x;
26525                 var targetHeight = this.minHeight - 2 * y;
26526                 
26527                 var scale = 1;
26528                 
26529                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26530                     scale = targetWidth / width;
26531                 }
26532                 
26533                 if(x > 0 && y == 0){
26534                     scale = targetHeight / height;
26535                 }
26536                 
26537                 if(x > 0 && y > 0){
26538                     scale = targetWidth / width;
26539                     
26540                     if(width < height){
26541                         scale = targetHeight / height;
26542                     }
26543                 }
26544                 
26545                 context.scale(scale, scale);
26546                 
26547                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26548                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26549
26550                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26551                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26552                 
26553                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26554                 
26555                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26556                 
26557                 break;
26558             case 180 :
26559                 
26560                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26561                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26562                 
26563                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26564                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26565                 
26566                 var targetWidth = this.minWidth - 2 * x;
26567                 var targetHeight = this.minHeight - 2 * y;
26568                 
26569                 var scale = 1;
26570                 
26571                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26572                     scale = targetWidth / width;
26573                 }
26574                 
26575                 if(x > 0 && y == 0){
26576                     scale = targetHeight / height;
26577                 }
26578                 
26579                 if(x > 0 && y > 0){
26580                     scale = targetWidth / width;
26581                     
26582                     if(width < height){
26583                         scale = targetHeight / height;
26584                     }
26585                 }
26586                 
26587                 context.scale(scale, scale);
26588                 
26589                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26590                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26591
26592                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26593                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26594
26595                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26596                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26597                 
26598                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26599                 
26600                 break;
26601             case 270 :
26602                 
26603                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26604                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26605                 
26606                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26607                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26608                 
26609                 var targetWidth = this.minWidth - 2 * x;
26610                 var targetHeight = this.minHeight - 2 * y;
26611                 
26612                 var scale = 1;
26613                 
26614                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26615                     scale = targetWidth / width;
26616                 }
26617                 
26618                 if(x > 0 && y == 0){
26619                     scale = targetHeight / height;
26620                 }
26621                 
26622                 if(x > 0 && y > 0){
26623                     scale = targetWidth / width;
26624                     
26625                     if(width < height){
26626                         scale = targetHeight / height;
26627                     }
26628                 }
26629                 
26630                 context.scale(scale, scale);
26631                 
26632                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26633                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26634
26635                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26636                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26637                 
26638                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26639                 
26640                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26641                 
26642                 break;
26643             default : 
26644                 break;
26645         }
26646         
26647         this.cropData = canvas.toDataURL(this.cropType);
26648         
26649         if(this.fireEvent('crop', this, this.cropData) !== false){
26650             this.process(this.file, this.cropData);
26651         }
26652         
26653         return;
26654         
26655     },
26656     
26657     setThumbBoxSize : function()
26658     {
26659         var width, height;
26660         
26661         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26662             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26663             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26664             
26665             this.minWidth = width;
26666             this.minHeight = height;
26667             
26668             if(this.rotate == 90 || this.rotate == 270){
26669                 this.minWidth = height;
26670                 this.minHeight = width;
26671             }
26672         }
26673         
26674         height = 300;
26675         width = Math.ceil(this.minWidth * height / this.minHeight);
26676         
26677         if(this.minWidth > this.minHeight){
26678             width = 300;
26679             height = Math.ceil(this.minHeight * width / this.minWidth);
26680         }
26681         
26682         this.thumbEl.setStyle({
26683             width : width + 'px',
26684             height : height + 'px'
26685         });
26686
26687         return;
26688             
26689     },
26690     
26691     setThumbBoxPosition : function()
26692     {
26693         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26694         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26695         
26696         this.thumbEl.setLeft(x);
26697         this.thumbEl.setTop(y);
26698         
26699     },
26700     
26701     baseRotateLevel : function()
26702     {
26703         this.baseRotate = 1;
26704         
26705         if(
26706                 typeof(this.exif) != 'undefined' &&
26707                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26708                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26709         ){
26710             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26711         }
26712         
26713         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26714         
26715     },
26716     
26717     baseScaleLevel : function()
26718     {
26719         var width, height;
26720         
26721         if(this.isDocument){
26722             
26723             if(this.baseRotate == 6 || this.baseRotate == 8){
26724             
26725                 height = this.thumbEl.getHeight();
26726                 this.baseScale = height / this.imageEl.OriginWidth;
26727
26728                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26729                     width = this.thumbEl.getWidth();
26730                     this.baseScale = width / this.imageEl.OriginHeight;
26731                 }
26732
26733                 return;
26734             }
26735
26736             height = this.thumbEl.getHeight();
26737             this.baseScale = height / this.imageEl.OriginHeight;
26738
26739             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26740                 width = this.thumbEl.getWidth();
26741                 this.baseScale = width / this.imageEl.OriginWidth;
26742             }
26743
26744             return;
26745         }
26746         
26747         if(this.baseRotate == 6 || this.baseRotate == 8){
26748             
26749             width = this.thumbEl.getHeight();
26750             this.baseScale = width / this.imageEl.OriginHeight;
26751             
26752             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26753                 height = this.thumbEl.getWidth();
26754                 this.baseScale = height / this.imageEl.OriginHeight;
26755             }
26756             
26757             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26758                 height = this.thumbEl.getWidth();
26759                 this.baseScale = height / this.imageEl.OriginHeight;
26760                 
26761                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26762                     width = this.thumbEl.getHeight();
26763                     this.baseScale = width / this.imageEl.OriginWidth;
26764                 }
26765             }
26766             
26767             return;
26768         }
26769         
26770         width = this.thumbEl.getWidth();
26771         this.baseScale = width / this.imageEl.OriginWidth;
26772         
26773         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26774             height = this.thumbEl.getHeight();
26775             this.baseScale = height / this.imageEl.OriginHeight;
26776         }
26777         
26778         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26779             
26780             height = this.thumbEl.getHeight();
26781             this.baseScale = height / this.imageEl.OriginHeight;
26782             
26783             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26784                 width = this.thumbEl.getWidth();
26785                 this.baseScale = width / this.imageEl.OriginWidth;
26786             }
26787             
26788         }
26789         
26790         return;
26791     },
26792     
26793     getScaleLevel : function()
26794     {
26795         return this.baseScale * Math.pow(1.1, this.scale);
26796     },
26797     
26798     onTouchStart : function(e)
26799     {
26800         if(!this.canvasLoaded){
26801             this.beforeSelectFile(e);
26802             return;
26803         }
26804         
26805         var touches = e.browserEvent.touches;
26806         
26807         if(!touches){
26808             return;
26809         }
26810         
26811         if(touches.length == 1){
26812             this.onMouseDown(e);
26813             return;
26814         }
26815         
26816         if(touches.length != 2){
26817             return;
26818         }
26819         
26820         var coords = [];
26821         
26822         for(var i = 0, finger; finger = touches[i]; i++){
26823             coords.push(finger.pageX, finger.pageY);
26824         }
26825         
26826         var x = Math.pow(coords[0] - coords[2], 2);
26827         var y = Math.pow(coords[1] - coords[3], 2);
26828         
26829         this.startDistance = Math.sqrt(x + y);
26830         
26831         this.startScale = this.scale;
26832         
26833         this.pinching = true;
26834         this.dragable = false;
26835         
26836     },
26837     
26838     onTouchMove : function(e)
26839     {
26840         if(!this.pinching && !this.dragable){
26841             return;
26842         }
26843         
26844         var touches = e.browserEvent.touches;
26845         
26846         if(!touches){
26847             return;
26848         }
26849         
26850         if(this.dragable){
26851             this.onMouseMove(e);
26852             return;
26853         }
26854         
26855         var coords = [];
26856         
26857         for(var i = 0, finger; finger = touches[i]; i++){
26858             coords.push(finger.pageX, finger.pageY);
26859         }
26860         
26861         var x = Math.pow(coords[0] - coords[2], 2);
26862         var y = Math.pow(coords[1] - coords[3], 2);
26863         
26864         this.endDistance = Math.sqrt(x + y);
26865         
26866         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26867         
26868         if(!this.zoomable()){
26869             this.scale = this.startScale;
26870             return;
26871         }
26872         
26873         this.draw();
26874         
26875     },
26876     
26877     onTouchEnd : function(e)
26878     {
26879         this.pinching = false;
26880         this.dragable = false;
26881         
26882     },
26883     
26884     process : function(file, crop)
26885     {
26886         if(this.loadMask){
26887             this.maskEl.mask(this.loadingText);
26888         }
26889         
26890         this.xhr = new XMLHttpRequest();
26891         
26892         file.xhr = this.xhr;
26893
26894         this.xhr.open(this.method, this.url, true);
26895         
26896         var headers = {
26897             "Accept": "application/json",
26898             "Cache-Control": "no-cache",
26899             "X-Requested-With": "XMLHttpRequest"
26900         };
26901         
26902         for (var headerName in headers) {
26903             var headerValue = headers[headerName];
26904             if (headerValue) {
26905                 this.xhr.setRequestHeader(headerName, headerValue);
26906             }
26907         }
26908         
26909         var _this = this;
26910         
26911         this.xhr.onload = function()
26912         {
26913             _this.xhrOnLoad(_this.xhr);
26914         }
26915         
26916         this.xhr.onerror = function()
26917         {
26918             _this.xhrOnError(_this.xhr);
26919         }
26920         
26921         var formData = new FormData();
26922
26923         formData.append('returnHTML', 'NO');
26924         
26925         if(crop){
26926             formData.append('crop', crop);
26927         }
26928         
26929         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26930             formData.append(this.paramName, file, file.name);
26931         }
26932         
26933         if(typeof(file.filename) != 'undefined'){
26934             formData.append('filename', file.filename);
26935         }
26936         
26937         if(typeof(file.mimetype) != 'undefined'){
26938             formData.append('mimetype', file.mimetype);
26939         }
26940         
26941         if(this.fireEvent('arrange', this, formData) != false){
26942             this.xhr.send(formData);
26943         };
26944     },
26945     
26946     xhrOnLoad : function(xhr)
26947     {
26948         if(this.loadMask){
26949             this.maskEl.unmask();
26950         }
26951         
26952         if (xhr.readyState !== 4) {
26953             this.fireEvent('exception', this, xhr);
26954             return;
26955         }
26956
26957         var response = Roo.decode(xhr.responseText);
26958         
26959         if(!response.success){
26960             this.fireEvent('exception', this, xhr);
26961             return;
26962         }
26963         
26964         var response = Roo.decode(xhr.responseText);
26965         
26966         this.fireEvent('upload', this, response);
26967         
26968     },
26969     
26970     xhrOnError : function()
26971     {
26972         if(this.loadMask){
26973             this.maskEl.unmask();
26974         }
26975         
26976         Roo.log('xhr on error');
26977         
26978         var response = Roo.decode(xhr.responseText);
26979           
26980         Roo.log(response);
26981         
26982     },
26983     
26984     prepare : function(file)
26985     {   
26986         if(this.loadMask){
26987             this.maskEl.mask(this.loadingText);
26988         }
26989         
26990         this.file = false;
26991         this.exif = {};
26992         
26993         if(typeof(file) === 'string'){
26994             this.loadCanvas(file);
26995             return;
26996         }
26997         
26998         if(!file || !this.urlAPI){
26999             return;
27000         }
27001         
27002         this.file = file;
27003         this.cropType = file.type;
27004         
27005         var _this = this;
27006         
27007         if(this.fireEvent('prepare', this, this.file) != false){
27008             
27009             var reader = new FileReader();
27010             
27011             reader.onload = function (e) {
27012                 if (e.target.error) {
27013                     Roo.log(e.target.error);
27014                     return;
27015                 }
27016                 
27017                 var buffer = e.target.result,
27018                     dataView = new DataView(buffer),
27019                     offset = 2,
27020                     maxOffset = dataView.byteLength - 4,
27021                     markerBytes,
27022                     markerLength;
27023                 
27024                 if (dataView.getUint16(0) === 0xffd8) {
27025                     while (offset < maxOffset) {
27026                         markerBytes = dataView.getUint16(offset);
27027                         
27028                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27029                             markerLength = dataView.getUint16(offset + 2) + 2;
27030                             if (offset + markerLength > dataView.byteLength) {
27031                                 Roo.log('Invalid meta data: Invalid segment size.');
27032                                 break;
27033                             }
27034                             
27035                             if(markerBytes == 0xffe1){
27036                                 _this.parseExifData(
27037                                     dataView,
27038                                     offset,
27039                                     markerLength
27040                                 );
27041                             }
27042                             
27043                             offset += markerLength;
27044                             
27045                             continue;
27046                         }
27047                         
27048                         break;
27049                     }
27050                     
27051                 }
27052                 
27053                 var url = _this.urlAPI.createObjectURL(_this.file);
27054                 
27055                 _this.loadCanvas(url);
27056                 
27057                 return;
27058             }
27059             
27060             reader.readAsArrayBuffer(this.file);
27061             
27062         }
27063         
27064     },
27065     
27066     parseExifData : function(dataView, offset, length)
27067     {
27068         var tiffOffset = offset + 10,
27069             littleEndian,
27070             dirOffset;
27071     
27072         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27073             // No Exif data, might be XMP data instead
27074             return;
27075         }
27076         
27077         // Check for the ASCII code for "Exif" (0x45786966):
27078         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27079             // No Exif data, might be XMP data instead
27080             return;
27081         }
27082         if (tiffOffset + 8 > dataView.byteLength) {
27083             Roo.log('Invalid Exif data: Invalid segment size.');
27084             return;
27085         }
27086         // Check for the two null bytes:
27087         if (dataView.getUint16(offset + 8) !== 0x0000) {
27088             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27089             return;
27090         }
27091         // Check the byte alignment:
27092         switch (dataView.getUint16(tiffOffset)) {
27093         case 0x4949:
27094             littleEndian = true;
27095             break;
27096         case 0x4D4D:
27097             littleEndian = false;
27098             break;
27099         default:
27100             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27101             return;
27102         }
27103         // Check for the TIFF tag marker (0x002A):
27104         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27105             Roo.log('Invalid Exif data: Missing TIFF marker.');
27106             return;
27107         }
27108         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27109         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27110         
27111         this.parseExifTags(
27112             dataView,
27113             tiffOffset,
27114             tiffOffset + dirOffset,
27115             littleEndian
27116         );
27117     },
27118     
27119     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27120     {
27121         var tagsNumber,
27122             dirEndOffset,
27123             i;
27124         if (dirOffset + 6 > dataView.byteLength) {
27125             Roo.log('Invalid Exif data: Invalid directory offset.');
27126             return;
27127         }
27128         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27129         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27130         if (dirEndOffset + 4 > dataView.byteLength) {
27131             Roo.log('Invalid Exif data: Invalid directory size.');
27132             return;
27133         }
27134         for (i = 0; i < tagsNumber; i += 1) {
27135             this.parseExifTag(
27136                 dataView,
27137                 tiffOffset,
27138                 dirOffset + 2 + 12 * i, // tag offset
27139                 littleEndian
27140             );
27141         }
27142         // Return the offset to the next directory:
27143         return dataView.getUint32(dirEndOffset, littleEndian);
27144     },
27145     
27146     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27147     {
27148         var tag = dataView.getUint16(offset, littleEndian);
27149         
27150         this.exif[tag] = this.getExifValue(
27151             dataView,
27152             tiffOffset,
27153             offset,
27154             dataView.getUint16(offset + 2, littleEndian), // tag type
27155             dataView.getUint32(offset + 4, littleEndian), // tag length
27156             littleEndian
27157         );
27158     },
27159     
27160     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27161     {
27162         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27163             tagSize,
27164             dataOffset,
27165             values,
27166             i,
27167             str,
27168             c;
27169     
27170         if (!tagType) {
27171             Roo.log('Invalid Exif data: Invalid tag type.');
27172             return;
27173         }
27174         
27175         tagSize = tagType.size * length;
27176         // Determine if the value is contained in the dataOffset bytes,
27177         // or if the value at the dataOffset is a pointer to the actual data:
27178         dataOffset = tagSize > 4 ?
27179                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27180         if (dataOffset + tagSize > dataView.byteLength) {
27181             Roo.log('Invalid Exif data: Invalid data offset.');
27182             return;
27183         }
27184         if (length === 1) {
27185             return tagType.getValue(dataView, dataOffset, littleEndian);
27186         }
27187         values = [];
27188         for (i = 0; i < length; i += 1) {
27189             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27190         }
27191         
27192         if (tagType.ascii) {
27193             str = '';
27194             // Concatenate the chars:
27195             for (i = 0; i < values.length; i += 1) {
27196                 c = values[i];
27197                 // Ignore the terminating NULL byte(s):
27198                 if (c === '\u0000') {
27199                     break;
27200                 }
27201                 str += c;
27202             }
27203             return str;
27204         }
27205         return values;
27206     }
27207     
27208 });
27209
27210 Roo.apply(Roo.bootstrap.UploadCropbox, {
27211     tags : {
27212         'Orientation': 0x0112
27213     },
27214     
27215     Orientation: {
27216             1: 0, //'top-left',
27217 //            2: 'top-right',
27218             3: 180, //'bottom-right',
27219 //            4: 'bottom-left',
27220 //            5: 'left-top',
27221             6: 90, //'right-top',
27222 //            7: 'right-bottom',
27223             8: 270 //'left-bottom'
27224     },
27225     
27226     exifTagTypes : {
27227         // byte, 8-bit unsigned int:
27228         1: {
27229             getValue: function (dataView, dataOffset) {
27230                 return dataView.getUint8(dataOffset);
27231             },
27232             size: 1
27233         },
27234         // ascii, 8-bit byte:
27235         2: {
27236             getValue: function (dataView, dataOffset) {
27237                 return String.fromCharCode(dataView.getUint8(dataOffset));
27238             },
27239             size: 1,
27240             ascii: true
27241         },
27242         // short, 16 bit int:
27243         3: {
27244             getValue: function (dataView, dataOffset, littleEndian) {
27245                 return dataView.getUint16(dataOffset, littleEndian);
27246             },
27247             size: 2
27248         },
27249         // long, 32 bit int:
27250         4: {
27251             getValue: function (dataView, dataOffset, littleEndian) {
27252                 return dataView.getUint32(dataOffset, littleEndian);
27253             },
27254             size: 4
27255         },
27256         // rational = two long values, first is numerator, second is denominator:
27257         5: {
27258             getValue: function (dataView, dataOffset, littleEndian) {
27259                 return dataView.getUint32(dataOffset, littleEndian) /
27260                     dataView.getUint32(dataOffset + 4, littleEndian);
27261             },
27262             size: 8
27263         },
27264         // slong, 32 bit signed int:
27265         9: {
27266             getValue: function (dataView, dataOffset, littleEndian) {
27267                 return dataView.getInt32(dataOffset, littleEndian);
27268             },
27269             size: 4
27270         },
27271         // srational, two slongs, first is numerator, second is denominator:
27272         10: {
27273             getValue: function (dataView, dataOffset, littleEndian) {
27274                 return dataView.getInt32(dataOffset, littleEndian) /
27275                     dataView.getInt32(dataOffset + 4, littleEndian);
27276             },
27277             size: 8
27278         }
27279     },
27280     
27281     footer : {
27282         STANDARD : [
27283             {
27284                 tag : 'div',
27285                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27286                 action : 'rotate-left',
27287                 cn : [
27288                     {
27289                         tag : 'button',
27290                         cls : 'btn btn-default',
27291                         html : '<i class="fa fa-undo"></i>'
27292                     }
27293                 ]
27294             },
27295             {
27296                 tag : 'div',
27297                 cls : 'btn-group roo-upload-cropbox-picture',
27298                 action : 'picture',
27299                 cn : [
27300                     {
27301                         tag : 'button',
27302                         cls : 'btn btn-default',
27303                         html : '<i class="fa fa-picture-o"></i>'
27304                     }
27305                 ]
27306             },
27307             {
27308                 tag : 'div',
27309                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27310                 action : 'rotate-right',
27311                 cn : [
27312                     {
27313                         tag : 'button',
27314                         cls : 'btn btn-default',
27315                         html : '<i class="fa fa-repeat"></i>'
27316                     }
27317                 ]
27318             }
27319         ],
27320         DOCUMENT : [
27321             {
27322                 tag : 'div',
27323                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27324                 action : 'rotate-left',
27325                 cn : [
27326                     {
27327                         tag : 'button',
27328                         cls : 'btn btn-default',
27329                         html : '<i class="fa fa-undo"></i>'
27330                     }
27331                 ]
27332             },
27333             {
27334                 tag : 'div',
27335                 cls : 'btn-group roo-upload-cropbox-download',
27336                 action : 'download',
27337                 cn : [
27338                     {
27339                         tag : 'button',
27340                         cls : 'btn btn-default',
27341                         html : '<i class="fa fa-download"></i>'
27342                     }
27343                 ]
27344             },
27345             {
27346                 tag : 'div',
27347                 cls : 'btn-group roo-upload-cropbox-crop',
27348                 action : 'crop',
27349                 cn : [
27350                     {
27351                         tag : 'button',
27352                         cls : 'btn btn-default',
27353                         html : '<i class="fa fa-crop"></i>'
27354                     }
27355                 ]
27356             },
27357             {
27358                 tag : 'div',
27359                 cls : 'btn-group roo-upload-cropbox-trash',
27360                 action : 'trash',
27361                 cn : [
27362                     {
27363                         tag : 'button',
27364                         cls : 'btn btn-default',
27365                         html : '<i class="fa fa-trash"></i>'
27366                     }
27367                 ]
27368             },
27369             {
27370                 tag : 'div',
27371                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27372                 action : 'rotate-right',
27373                 cn : [
27374                     {
27375                         tag : 'button',
27376                         cls : 'btn btn-default',
27377                         html : '<i class="fa fa-repeat"></i>'
27378                     }
27379                 ]
27380             }
27381         ],
27382         ROTATOR : [
27383             {
27384                 tag : 'div',
27385                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27386                 action : 'rotate-left',
27387                 cn : [
27388                     {
27389                         tag : 'button',
27390                         cls : 'btn btn-default',
27391                         html : '<i class="fa fa-undo"></i>'
27392                     }
27393                 ]
27394             },
27395             {
27396                 tag : 'div',
27397                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27398                 action : 'rotate-right',
27399                 cn : [
27400                     {
27401                         tag : 'button',
27402                         cls : 'btn btn-default',
27403                         html : '<i class="fa fa-repeat"></i>'
27404                     }
27405                 ]
27406             }
27407         ]
27408     }
27409 });
27410
27411 /*
27412 * Licence: LGPL
27413 */
27414
27415 /**
27416  * @class Roo.bootstrap.DocumentManager
27417  * @extends Roo.bootstrap.Component
27418  * Bootstrap DocumentManager class
27419  * @cfg {String} paramName default 'imageUpload'
27420  * @cfg {String} toolTipName default 'filename'
27421  * @cfg {String} method default POST
27422  * @cfg {String} url action url
27423  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27424  * @cfg {Boolean} multiple multiple upload default true
27425  * @cfg {Number} thumbSize default 300
27426  * @cfg {String} fieldLabel
27427  * @cfg {Number} labelWidth default 4
27428  * @cfg {String} labelAlign (left|top) default left
27429  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27430  * 
27431  * @constructor
27432  * Create a new DocumentManager
27433  * @param {Object} config The config object
27434  */
27435
27436 Roo.bootstrap.DocumentManager = function(config){
27437     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27438     
27439     this.files = [];
27440     this.delegates = [];
27441     
27442     this.addEvents({
27443         /**
27444          * @event initial
27445          * Fire when initial the DocumentManager
27446          * @param {Roo.bootstrap.DocumentManager} this
27447          */
27448         "initial" : true,
27449         /**
27450          * @event inspect
27451          * inspect selected file
27452          * @param {Roo.bootstrap.DocumentManager} this
27453          * @param {File} file
27454          */
27455         "inspect" : true,
27456         /**
27457          * @event exception
27458          * Fire when xhr load exception
27459          * @param {Roo.bootstrap.DocumentManager} this
27460          * @param {XMLHttpRequest} xhr
27461          */
27462         "exception" : true,
27463         /**
27464          * @event afterupload
27465          * Fire when xhr load exception
27466          * @param {Roo.bootstrap.DocumentManager} this
27467          * @param {XMLHttpRequest} xhr
27468          */
27469         "afterupload" : true,
27470         /**
27471          * @event prepare
27472          * prepare the form data
27473          * @param {Roo.bootstrap.DocumentManager} this
27474          * @param {Object} formData
27475          */
27476         "prepare" : true,
27477         /**
27478          * @event remove
27479          * Fire when remove the file
27480          * @param {Roo.bootstrap.DocumentManager} this
27481          * @param {Object} file
27482          */
27483         "remove" : true,
27484         /**
27485          * @event refresh
27486          * Fire after refresh the file
27487          * @param {Roo.bootstrap.DocumentManager} this
27488          */
27489         "refresh" : true,
27490         /**
27491          * @event click
27492          * Fire after click the image
27493          * @param {Roo.bootstrap.DocumentManager} this
27494          * @param {Object} file
27495          */
27496         "click" : true,
27497         /**
27498          * @event edit
27499          * Fire when upload a image and editable set to true
27500          * @param {Roo.bootstrap.DocumentManager} this
27501          * @param {Object} file
27502          */
27503         "edit" : true,
27504         /**
27505          * @event beforeselectfile
27506          * Fire before select file
27507          * @param {Roo.bootstrap.DocumentManager} this
27508          */
27509         "beforeselectfile" : true,
27510         /**
27511          * @event process
27512          * Fire before process file
27513          * @param {Roo.bootstrap.DocumentManager} this
27514          * @param {Object} file
27515          */
27516         "process" : true
27517         
27518     });
27519 };
27520
27521 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27522     
27523     boxes : 0,
27524     inputName : '',
27525     thumbSize : 300,
27526     multiple : true,
27527     files : false,
27528     method : 'POST',
27529     url : '',
27530     paramName : 'imageUpload',
27531     toolTipName : 'filename',
27532     fieldLabel : '',
27533     labelWidth : 4,
27534     labelAlign : 'left',
27535     editable : true,
27536     delegates : false,
27537     xhr : false, 
27538     
27539     getAutoCreate : function()
27540     {   
27541         var managerWidget = {
27542             tag : 'div',
27543             cls : 'roo-document-manager',
27544             cn : [
27545                 {
27546                     tag : 'input',
27547                     cls : 'roo-document-manager-selector',
27548                     type : 'file'
27549                 },
27550                 {
27551                     tag : 'div',
27552                     cls : 'roo-document-manager-uploader',
27553                     cn : [
27554                         {
27555                             tag : 'div',
27556                             cls : 'roo-document-manager-upload-btn',
27557                             html : '<i class="fa fa-plus"></i>'
27558                         }
27559                     ]
27560                     
27561                 }
27562             ]
27563         };
27564         
27565         var content = [
27566             {
27567                 tag : 'div',
27568                 cls : 'column col-md-12',
27569                 cn : managerWidget
27570             }
27571         ];
27572         
27573         if(this.fieldLabel.length){
27574             
27575             content = [
27576                 {
27577                     tag : 'div',
27578                     cls : 'column col-md-12',
27579                     html : this.fieldLabel
27580                 },
27581                 {
27582                     tag : 'div',
27583                     cls : 'column col-md-12',
27584                     cn : managerWidget
27585                 }
27586             ];
27587
27588             if(this.labelAlign == 'left'){
27589                 content = [
27590                     {
27591                         tag : 'div',
27592                         cls : 'column col-md-' + this.labelWidth,
27593                         html : this.fieldLabel
27594                     },
27595                     {
27596                         tag : 'div',
27597                         cls : 'column col-md-' + (12 - this.labelWidth),
27598                         cn : managerWidget
27599                     }
27600                 ];
27601                 
27602             }
27603         }
27604         
27605         var cfg = {
27606             tag : 'div',
27607             cls : 'row clearfix',
27608             cn : content
27609         };
27610         
27611         return cfg;
27612         
27613     },
27614     
27615     initEvents : function()
27616     {
27617         this.managerEl = this.el.select('.roo-document-manager', true).first();
27618         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27619         
27620         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27621         this.selectorEl.hide();
27622         
27623         if(this.multiple){
27624             this.selectorEl.attr('multiple', 'multiple');
27625         }
27626         
27627         this.selectorEl.on('change', this.onFileSelected, this);
27628         
27629         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27630         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27631         
27632         this.uploader.on('click', this.onUploaderClick, this);
27633         
27634         this.renderProgressDialog();
27635         
27636         var _this = this;
27637         
27638         window.addEventListener("resize", function() { _this.refresh(); } );
27639         
27640         this.fireEvent('initial', this);
27641     },
27642     
27643     renderProgressDialog : function()
27644     {
27645         var _this = this;
27646         
27647         this.progressDialog = new Roo.bootstrap.Modal({
27648             cls : 'roo-document-manager-progress-dialog',
27649             allow_close : false,
27650             title : '',
27651             buttons : [
27652                 {
27653                     name  :'cancel',
27654                     weight : 'danger',
27655                     html : 'Cancel'
27656                 }
27657             ], 
27658             listeners : { 
27659                 btnclick : function() {
27660                     _this.uploadCancel();
27661                     this.hide();
27662                 }
27663             }
27664         });
27665          
27666         this.progressDialog.render(Roo.get(document.body));
27667          
27668         this.progress = new Roo.bootstrap.Progress({
27669             cls : 'roo-document-manager-progress',
27670             active : true,
27671             striped : true
27672         });
27673         
27674         this.progress.render(this.progressDialog.getChildContainer());
27675         
27676         this.progressBar = new Roo.bootstrap.ProgressBar({
27677             cls : 'roo-document-manager-progress-bar',
27678             aria_valuenow : 0,
27679             aria_valuemin : 0,
27680             aria_valuemax : 12,
27681             panel : 'success'
27682         });
27683         
27684         this.progressBar.render(this.progress.getChildContainer());
27685     },
27686     
27687     onUploaderClick : function(e)
27688     {
27689         e.preventDefault();
27690      
27691         if(this.fireEvent('beforeselectfile', this) != false){
27692             this.selectorEl.dom.click();
27693         }
27694         
27695     },
27696     
27697     onFileSelected : function(e)
27698     {
27699         e.preventDefault();
27700         
27701         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27702             return;
27703         }
27704         
27705         Roo.each(this.selectorEl.dom.files, function(file){
27706             if(this.fireEvent('inspect', this, file) != false){
27707                 this.files.push(file);
27708             }
27709         }, this);
27710         
27711         this.queue();
27712         
27713     },
27714     
27715     queue : function()
27716     {
27717         this.selectorEl.dom.value = '';
27718         
27719         if(!this.files.length){
27720             return;
27721         }
27722         
27723         if(this.boxes > 0 && this.files.length > this.boxes){
27724             this.files = this.files.slice(0, this.boxes);
27725         }
27726         
27727         this.uploader.show();
27728         
27729         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27730             this.uploader.hide();
27731         }
27732         
27733         var _this = this;
27734         
27735         var files = [];
27736         
27737         var docs = [];
27738         
27739         Roo.each(this.files, function(file){
27740             
27741             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27742                 var f = this.renderPreview(file);
27743                 files.push(f);
27744                 return;
27745             }
27746             
27747             if(file.type.indexOf('image') != -1){
27748                 this.delegates.push(
27749                     (function(){
27750                         _this.process(file);
27751                     }).createDelegate(this)
27752                 );
27753         
27754                 return;
27755             }
27756             
27757             docs.push(
27758                 (function(){
27759                     _this.process(file);
27760                 }).createDelegate(this)
27761             );
27762             
27763         }, this);
27764         
27765         this.files = files;
27766         
27767         this.delegates = this.delegates.concat(docs);
27768         
27769         if(!this.delegates.length){
27770             this.refresh();
27771             return;
27772         }
27773         
27774         this.progressBar.aria_valuemax = this.delegates.length;
27775         
27776         this.arrange();
27777         
27778         return;
27779     },
27780     
27781     arrange : function()
27782     {
27783         if(!this.delegates.length){
27784             this.progressDialog.hide();
27785             this.refresh();
27786             return;
27787         }
27788         
27789         var delegate = this.delegates.shift();
27790         
27791         this.progressDialog.show();
27792         
27793         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27794         
27795         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27796         
27797         delegate();
27798     },
27799     
27800     refresh : function()
27801     {
27802         this.uploader.show();
27803         
27804         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27805             this.uploader.hide();
27806         }
27807         
27808         Roo.isTouch ? this.closable(false) : this.closable(true);
27809         
27810         this.fireEvent('refresh', this);
27811     },
27812     
27813     onRemove : function(e, el, o)
27814     {
27815         e.preventDefault();
27816         
27817         this.fireEvent('remove', this, o);
27818         
27819     },
27820     
27821     remove : function(o)
27822     {
27823         var files = [];
27824         
27825         Roo.each(this.files, function(file){
27826             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27827                 files.push(file);
27828                 return;
27829             }
27830
27831             o.target.remove();
27832
27833         }, this);
27834         
27835         this.files = files;
27836         
27837         this.refresh();
27838     },
27839     
27840     clear : function()
27841     {
27842         Roo.each(this.files, function(file){
27843             if(!file.target){
27844                 return;
27845             }
27846             
27847             file.target.remove();
27848
27849         }, this);
27850         
27851         this.files = [];
27852         
27853         this.refresh();
27854     },
27855     
27856     onClick : function(e, el, o)
27857     {
27858         e.preventDefault();
27859         
27860         this.fireEvent('click', this, o);
27861         
27862     },
27863     
27864     closable : function(closable)
27865     {
27866         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27867             
27868             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27869             
27870             if(closable){
27871                 el.show();
27872                 return;
27873             }
27874             
27875             el.hide();
27876             
27877         }, this);
27878     },
27879     
27880     xhrOnLoad : function(xhr)
27881     {
27882         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27883             el.remove();
27884         }, this);
27885         
27886         if (xhr.readyState !== 4) {
27887             this.arrange();
27888             this.fireEvent('exception', this, xhr);
27889             return;
27890         }
27891
27892         var response = Roo.decode(xhr.responseText);
27893         
27894         if(!response.success){
27895             this.arrange();
27896             this.fireEvent('exception', this, xhr);
27897             return;
27898         }
27899         
27900         var file = this.renderPreview(response.data);
27901         
27902         this.files.push(file);
27903         
27904         this.arrange();
27905         
27906         this.fireEvent('afterupload', this, xhr);
27907         
27908     },
27909     
27910     xhrOnError : function(xhr)
27911     {
27912         Roo.log('xhr on error');
27913         
27914         var response = Roo.decode(xhr.responseText);
27915           
27916         Roo.log(response);
27917         
27918         this.arrange();
27919     },
27920     
27921     process : function(file)
27922     {
27923         if(this.fireEvent('process', this, file) !== false){
27924             if(this.editable && file.type.indexOf('image') != -1){
27925                 this.fireEvent('edit', this, file);
27926                 return;
27927             }
27928
27929             this.uploadStart(file, false);
27930
27931             return;
27932         }
27933         
27934     },
27935     
27936     uploadStart : function(file, crop)
27937     {
27938         this.xhr = new XMLHttpRequest();
27939         
27940         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27941             this.arrange();
27942             return;
27943         }
27944         
27945         file.xhr = this.xhr;
27946             
27947         this.managerEl.createChild({
27948             tag : 'div',
27949             cls : 'roo-document-manager-loading',
27950             cn : [
27951                 {
27952                     tag : 'div',
27953                     tooltip : file.name,
27954                     cls : 'roo-document-manager-thumb',
27955                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27956                 }
27957             ]
27958
27959         });
27960
27961         this.xhr.open(this.method, this.url, true);
27962         
27963         var headers = {
27964             "Accept": "application/json",
27965             "Cache-Control": "no-cache",
27966             "X-Requested-With": "XMLHttpRequest"
27967         };
27968         
27969         for (var headerName in headers) {
27970             var headerValue = headers[headerName];
27971             if (headerValue) {
27972                 this.xhr.setRequestHeader(headerName, headerValue);
27973             }
27974         }
27975         
27976         var _this = this;
27977         
27978         this.xhr.onload = function()
27979         {
27980             _this.xhrOnLoad(_this.xhr);
27981         }
27982         
27983         this.xhr.onerror = function()
27984         {
27985             _this.xhrOnError(_this.xhr);
27986         }
27987         
27988         var formData = new FormData();
27989
27990         formData.append('returnHTML', 'NO');
27991         
27992         if(crop){
27993             formData.append('crop', crop);
27994         }
27995         
27996         formData.append(this.paramName, file, file.name);
27997         
27998         var options = {
27999             file : file, 
28000             manually : false
28001         };
28002         
28003         if(this.fireEvent('prepare', this, formData, options) != false){
28004             
28005             if(options.manually){
28006                 return;
28007             }
28008             
28009             this.xhr.send(formData);
28010             return;
28011         };
28012         
28013         this.uploadCancel();
28014     },
28015     
28016     uploadCancel : function()
28017     {
28018         if (this.xhr) {
28019             this.xhr.abort();
28020         }
28021         
28022         this.delegates = [];
28023         
28024         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28025             el.remove();
28026         }, this);
28027         
28028         this.arrange();
28029     },
28030     
28031     renderPreview : function(file)
28032     {
28033         if(typeof(file.target) != 'undefined' && file.target){
28034             return file;
28035         }
28036         
28037         var previewEl = this.managerEl.createChild({
28038             tag : 'div',
28039             cls : 'roo-document-manager-preview',
28040             cn : [
28041                 {
28042                     tag : 'div',
28043                     tooltip : file[this.toolTipName],
28044                     cls : 'roo-document-manager-thumb',
28045                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28046                 },
28047                 {
28048                     tag : 'button',
28049                     cls : 'close',
28050                     html : '<i class="fa fa-times-circle"></i>'
28051                 }
28052             ]
28053         });
28054
28055         var close = previewEl.select('button.close', true).first();
28056
28057         close.on('click', this.onRemove, this, file);
28058
28059         file.target = previewEl;
28060
28061         var image = previewEl.select('img', true).first();
28062         
28063         var _this = this;
28064         
28065         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28066         
28067         image.on('click', this.onClick, this, file);
28068         
28069         return file;
28070         
28071     },
28072     
28073     onPreviewLoad : function(file, image)
28074     {
28075         if(typeof(file.target) == 'undefined' || !file.target){
28076             return;
28077         }
28078         
28079         var width = image.dom.naturalWidth || image.dom.width;
28080         var height = image.dom.naturalHeight || image.dom.height;
28081         
28082         if(width > height){
28083             file.target.addClass('wide');
28084             return;
28085         }
28086         
28087         file.target.addClass('tall');
28088         return;
28089         
28090     },
28091     
28092     uploadFromSource : function(file, crop)
28093     {
28094         this.xhr = new XMLHttpRequest();
28095         
28096         this.managerEl.createChild({
28097             tag : 'div',
28098             cls : 'roo-document-manager-loading',
28099             cn : [
28100                 {
28101                     tag : 'div',
28102                     tooltip : file.name,
28103                     cls : 'roo-document-manager-thumb',
28104                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28105                 }
28106             ]
28107
28108         });
28109
28110         this.xhr.open(this.method, this.url, true);
28111         
28112         var headers = {
28113             "Accept": "application/json",
28114             "Cache-Control": "no-cache",
28115             "X-Requested-With": "XMLHttpRequest"
28116         };
28117         
28118         for (var headerName in headers) {
28119             var headerValue = headers[headerName];
28120             if (headerValue) {
28121                 this.xhr.setRequestHeader(headerName, headerValue);
28122             }
28123         }
28124         
28125         var _this = this;
28126         
28127         this.xhr.onload = function()
28128         {
28129             _this.xhrOnLoad(_this.xhr);
28130         }
28131         
28132         this.xhr.onerror = function()
28133         {
28134             _this.xhrOnError(_this.xhr);
28135         }
28136         
28137         var formData = new FormData();
28138
28139         formData.append('returnHTML', 'NO');
28140         
28141         formData.append('crop', crop);
28142         
28143         if(typeof(file.filename) != 'undefined'){
28144             formData.append('filename', file.filename);
28145         }
28146         
28147         if(typeof(file.mimetype) != 'undefined'){
28148             formData.append('mimetype', file.mimetype);
28149         }
28150         
28151         if(this.fireEvent('prepare', this, formData) != false){
28152             this.xhr.send(formData);
28153         };
28154     }
28155 });
28156
28157 /*
28158 * Licence: LGPL
28159 */
28160
28161 /**
28162  * @class Roo.bootstrap.DocumentViewer
28163  * @extends Roo.bootstrap.Component
28164  * Bootstrap DocumentViewer class
28165  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28166  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28167  * 
28168  * @constructor
28169  * Create a new DocumentViewer
28170  * @param {Object} config The config object
28171  */
28172
28173 Roo.bootstrap.DocumentViewer = function(config){
28174     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28175     
28176     this.addEvents({
28177         /**
28178          * @event initial
28179          * Fire after initEvent
28180          * @param {Roo.bootstrap.DocumentViewer} this
28181          */
28182         "initial" : true,
28183         /**
28184          * @event click
28185          * Fire after click
28186          * @param {Roo.bootstrap.DocumentViewer} this
28187          */
28188         "click" : true,
28189         /**
28190          * @event download
28191          * Fire after download button
28192          * @param {Roo.bootstrap.DocumentViewer} this
28193          */
28194         "download" : true,
28195         /**
28196          * @event trash
28197          * Fire after trash button
28198          * @param {Roo.bootstrap.DocumentViewer} this
28199          */
28200         "trash" : true
28201         
28202     });
28203 };
28204
28205 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28206     
28207     showDownload : true,
28208     
28209     showTrash : true,
28210     
28211     getAutoCreate : function()
28212     {
28213         var cfg = {
28214             tag : 'div',
28215             cls : 'roo-document-viewer',
28216             cn : [
28217                 {
28218                     tag : 'div',
28219                     cls : 'roo-document-viewer-body',
28220                     cn : [
28221                         {
28222                             tag : 'div',
28223                             cls : 'roo-document-viewer-thumb',
28224                             cn : [
28225                                 {
28226                                     tag : 'img',
28227                                     cls : 'roo-document-viewer-image'
28228                                 }
28229                             ]
28230                         }
28231                     ]
28232                 },
28233                 {
28234                     tag : 'div',
28235                     cls : 'roo-document-viewer-footer',
28236                     cn : {
28237                         tag : 'div',
28238                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28239                         cn : [
28240                             {
28241                                 tag : 'div',
28242                                 cls : 'btn-group roo-document-viewer-download',
28243                                 cn : [
28244                                     {
28245                                         tag : 'button',
28246                                         cls : 'btn btn-default',
28247                                         html : '<i class="fa fa-download"></i>'
28248                                     }
28249                                 ]
28250                             },
28251                             {
28252                                 tag : 'div',
28253                                 cls : 'btn-group roo-document-viewer-trash',
28254                                 cn : [
28255                                     {
28256                                         tag : 'button',
28257                                         cls : 'btn btn-default',
28258                                         html : '<i class="fa fa-trash"></i>'
28259                                     }
28260                                 ]
28261                             }
28262                         ]
28263                     }
28264                 }
28265             ]
28266         };
28267         
28268         return cfg;
28269     },
28270     
28271     initEvents : function()
28272     {
28273         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28274         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28275         
28276         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28277         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28278         
28279         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28280         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28281         
28282         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28283         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28284         
28285         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28286         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28287         
28288         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28289         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28290         
28291         this.bodyEl.on('click', this.onClick, this);
28292         this.downloadBtn.on('click', this.onDownload, this);
28293         this.trashBtn.on('click', this.onTrash, this);
28294         
28295         this.downloadBtn.hide();
28296         this.trashBtn.hide();
28297         
28298         if(this.showDownload){
28299             this.downloadBtn.show();
28300         }
28301         
28302         if(this.showTrash){
28303             this.trashBtn.show();
28304         }
28305         
28306         if(!this.showDownload && !this.showTrash) {
28307             this.footerEl.hide();
28308         }
28309         
28310     },
28311     
28312     initial : function()
28313     {
28314         this.fireEvent('initial', this);
28315         
28316     },
28317     
28318     onClick : function(e)
28319     {
28320         e.preventDefault();
28321         
28322         this.fireEvent('click', this);
28323     },
28324     
28325     onDownload : function(e)
28326     {
28327         e.preventDefault();
28328         
28329         this.fireEvent('download', this);
28330     },
28331     
28332     onTrash : function(e)
28333     {
28334         e.preventDefault();
28335         
28336         this.fireEvent('trash', this);
28337     }
28338     
28339 });
28340 /*
28341  * - LGPL
28342  *
28343  * nav progress bar
28344  * 
28345  */
28346
28347 /**
28348  * @class Roo.bootstrap.NavProgressBar
28349  * @extends Roo.bootstrap.Component
28350  * Bootstrap NavProgressBar class
28351  * 
28352  * @constructor
28353  * Create a new nav progress bar
28354  * @param {Object} config The config object
28355  */
28356
28357 Roo.bootstrap.NavProgressBar = function(config){
28358     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28359
28360     this.bullets = this.bullets || [];
28361    
28362 //    Roo.bootstrap.NavProgressBar.register(this);
28363      this.addEvents({
28364         /**
28365              * @event changed
28366              * Fires when the active item changes
28367              * @param {Roo.bootstrap.NavProgressBar} this
28368              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28369              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28370          */
28371         'changed': true
28372      });
28373     
28374 };
28375
28376 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28377     
28378     bullets : [],
28379     barItems : [],
28380     
28381     getAutoCreate : function()
28382     {
28383         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28384         
28385         cfg = {
28386             tag : 'div',
28387             cls : 'roo-navigation-bar-group',
28388             cn : [
28389                 {
28390                     tag : 'div',
28391                     cls : 'roo-navigation-top-bar'
28392                 },
28393                 {
28394                     tag : 'div',
28395                     cls : 'roo-navigation-bullets-bar',
28396                     cn : [
28397                         {
28398                             tag : 'ul',
28399                             cls : 'roo-navigation-bar'
28400                         }
28401                     ]
28402                 },
28403                 
28404                 {
28405                     tag : 'div',
28406                     cls : 'roo-navigation-bottom-bar'
28407                 }
28408             ]
28409             
28410         };
28411         
28412         return cfg;
28413         
28414     },
28415     
28416     initEvents: function() 
28417     {
28418         
28419     },
28420     
28421     onRender : function(ct, position) 
28422     {
28423         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28424         
28425         if(this.bullets.length){
28426             Roo.each(this.bullets, function(b){
28427                this.addItem(b);
28428             }, this);
28429         }
28430         
28431         this.format();
28432         
28433     },
28434     
28435     addItem : function(cfg)
28436     {
28437         var item = new Roo.bootstrap.NavProgressItem(cfg);
28438         
28439         item.parentId = this.id;
28440         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28441         
28442         if(cfg.html){
28443             var top = new Roo.bootstrap.Element({
28444                 tag : 'div',
28445                 cls : 'roo-navigation-bar-text'
28446             });
28447             
28448             var bottom = new Roo.bootstrap.Element({
28449                 tag : 'div',
28450                 cls : 'roo-navigation-bar-text'
28451             });
28452             
28453             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28454             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28455             
28456             var topText = new Roo.bootstrap.Element({
28457                 tag : 'span',
28458                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28459             });
28460             
28461             var bottomText = new Roo.bootstrap.Element({
28462                 tag : 'span',
28463                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28464             });
28465             
28466             topText.onRender(top.el, null);
28467             bottomText.onRender(bottom.el, null);
28468             
28469             item.topEl = top;
28470             item.bottomEl = bottom;
28471         }
28472         
28473         this.barItems.push(item);
28474         
28475         return item;
28476     },
28477     
28478     getActive : function()
28479     {
28480         var active = false;
28481         
28482         Roo.each(this.barItems, function(v){
28483             
28484             if (!v.isActive()) {
28485                 return;
28486             }
28487             
28488             active = v;
28489             return false;
28490             
28491         });
28492         
28493         return active;
28494     },
28495     
28496     setActiveItem : function(item)
28497     {
28498         var prev = false;
28499         
28500         Roo.each(this.barItems, function(v){
28501             if (v.rid == item.rid) {
28502                 return ;
28503             }
28504             
28505             if (v.isActive()) {
28506                 v.setActive(false);
28507                 prev = v;
28508             }
28509         });
28510
28511         item.setActive(true);
28512         
28513         this.fireEvent('changed', this, item, prev);
28514     },
28515     
28516     getBarItem: function(rid)
28517     {
28518         var ret = false;
28519         
28520         Roo.each(this.barItems, function(e) {
28521             if (e.rid != rid) {
28522                 return;
28523             }
28524             
28525             ret =  e;
28526             return false;
28527         });
28528         
28529         return ret;
28530     },
28531     
28532     indexOfItem : function(item)
28533     {
28534         var index = false;
28535         
28536         Roo.each(this.barItems, function(v, i){
28537             
28538             if (v.rid != item.rid) {
28539                 return;
28540             }
28541             
28542             index = i;
28543             return false
28544         });
28545         
28546         return index;
28547     },
28548     
28549     setActiveNext : function()
28550     {
28551         var i = this.indexOfItem(this.getActive());
28552         
28553         if (i > this.barItems.length) {
28554             return;
28555         }
28556         
28557         this.setActiveItem(this.barItems[i+1]);
28558     },
28559     
28560     setActivePrev : function()
28561     {
28562         var i = this.indexOfItem(this.getActive());
28563         
28564         if (i  < 1) {
28565             return;
28566         }
28567         
28568         this.setActiveItem(this.barItems[i-1]);
28569     },
28570     
28571     format : function()
28572     {
28573         if(!this.barItems.length){
28574             return;
28575         }
28576      
28577         var width = 100 / this.barItems.length;
28578         
28579         Roo.each(this.barItems, function(i){
28580             i.el.setStyle('width', width + '%');
28581             i.topEl.el.setStyle('width', width + '%');
28582             i.bottomEl.el.setStyle('width', width + '%');
28583         }, this);
28584         
28585     }
28586     
28587 });
28588 /*
28589  * - LGPL
28590  *
28591  * Nav Progress Item
28592  * 
28593  */
28594
28595 /**
28596  * @class Roo.bootstrap.NavProgressItem
28597  * @extends Roo.bootstrap.Component
28598  * Bootstrap NavProgressItem class
28599  * @cfg {String} rid the reference id
28600  * @cfg {Boolean} active (true|false) Is item active default false
28601  * @cfg {Boolean} disabled (true|false) Is item active default false
28602  * @cfg {String} html
28603  * @cfg {String} position (top|bottom) text position default bottom
28604  * @cfg {String} icon show icon instead of number
28605  * 
28606  * @constructor
28607  * Create a new NavProgressItem
28608  * @param {Object} config The config object
28609  */
28610 Roo.bootstrap.NavProgressItem = function(config){
28611     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28612     this.addEvents({
28613         // raw events
28614         /**
28615          * @event click
28616          * The raw click event for the entire grid.
28617          * @param {Roo.bootstrap.NavProgressItem} this
28618          * @param {Roo.EventObject} e
28619          */
28620         "click" : true
28621     });
28622    
28623 };
28624
28625 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28626     
28627     rid : '',
28628     active : false,
28629     disabled : false,
28630     html : '',
28631     position : 'bottom',
28632     icon : false,
28633     
28634     getAutoCreate : function()
28635     {
28636         var iconCls = 'roo-navigation-bar-item-icon';
28637         
28638         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28639         
28640         var cfg = {
28641             tag: 'li',
28642             cls: 'roo-navigation-bar-item',
28643             cn : [
28644                 {
28645                     tag : 'i',
28646                     cls : iconCls
28647                 }
28648             ]
28649         };
28650         
28651         if(this.active){
28652             cfg.cls += ' active';
28653         }
28654         if(this.disabled){
28655             cfg.cls += ' disabled';
28656         }
28657         
28658         return cfg;
28659     },
28660     
28661     disable : function()
28662     {
28663         this.setDisabled(true);
28664     },
28665     
28666     enable : function()
28667     {
28668         this.setDisabled(false);
28669     },
28670     
28671     initEvents: function() 
28672     {
28673         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28674         
28675         this.iconEl.on('click', this.onClick, this);
28676     },
28677     
28678     onClick : function(e)
28679     {
28680         e.preventDefault();
28681         
28682         if(this.disabled){
28683             return;
28684         }
28685         
28686         if(this.fireEvent('click', this, e) === false){
28687             return;
28688         };
28689         
28690         this.parent().setActiveItem(this);
28691     },
28692     
28693     isActive: function () 
28694     {
28695         return this.active;
28696     },
28697     
28698     setActive : function(state)
28699     {
28700         if(this.active == state){
28701             return;
28702         }
28703         
28704         this.active = state;
28705         
28706         if (state) {
28707             this.el.addClass('active');
28708             return;
28709         }
28710         
28711         this.el.removeClass('active');
28712         
28713         return;
28714     },
28715     
28716     setDisabled : function(state)
28717     {
28718         if(this.disabled == state){
28719             return;
28720         }
28721         
28722         this.disabled = state;
28723         
28724         if (state) {
28725             this.el.addClass('disabled');
28726             return;
28727         }
28728         
28729         this.el.removeClass('disabled');
28730     },
28731     
28732     tooltipEl : function()
28733     {
28734         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28735     }
28736 });
28737  
28738
28739  /*
28740  * - LGPL
28741  *
28742  * FieldLabel
28743  * 
28744  */
28745
28746 /**
28747  * @class Roo.bootstrap.FieldLabel
28748  * @extends Roo.bootstrap.Component
28749  * Bootstrap FieldLabel class
28750  * @cfg {String} html contents of the element
28751  * @cfg {String} tag tag of the element default label
28752  * @cfg {String} cls class of the element
28753  * @cfg {String} target label target 
28754  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28755  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28756  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28757  * @cfg {String} iconTooltip default "This field is required"
28758  * 
28759  * @constructor
28760  * Create a new FieldLabel
28761  * @param {Object} config The config object
28762  */
28763
28764 Roo.bootstrap.FieldLabel = function(config){
28765     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28766     
28767     this.addEvents({
28768             /**
28769              * @event invalid
28770              * Fires after the field has been marked as invalid.
28771              * @param {Roo.form.FieldLabel} this
28772              * @param {String} msg The validation message
28773              */
28774             invalid : true,
28775             /**
28776              * @event valid
28777              * Fires after the field has been validated with no errors.
28778              * @param {Roo.form.FieldLabel} this
28779              */
28780             valid : true
28781         });
28782 };
28783
28784 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28785     
28786     tag: 'label',
28787     cls: '',
28788     html: '',
28789     target: '',
28790     allowBlank : true,
28791     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28792     validClass : 'text-success fa fa-lg fa-check',
28793     iconTooltip : 'This field is required',
28794     
28795     getAutoCreate : function(){
28796         
28797         var cfg = {
28798             tag : this.tag,
28799             cls : 'roo-bootstrap-field-label ' + this.cls,
28800             for : this.target,
28801             cn : [
28802                 {
28803                     tag : 'i',
28804                     cls : '',
28805                     tooltip : this.iconTooltip
28806                 },
28807                 {
28808                     tag : 'span',
28809                     html : this.html
28810                 }
28811             ] 
28812         };
28813         
28814         return cfg;
28815     },
28816     
28817     initEvents: function() 
28818     {
28819         Roo.bootstrap.Element.superclass.initEvents.call(this);
28820         
28821         this.iconEl = this.el.select('i', true).first();
28822         
28823         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28824         
28825         Roo.bootstrap.FieldLabel.register(this);
28826     },
28827     
28828     /**
28829      * Mark this field as valid
28830      */
28831     markValid : function()
28832     {
28833         this.iconEl.show();
28834         
28835         this.iconEl.removeClass(this.invalidClass);
28836         
28837         this.iconEl.addClass(this.validClass);
28838         
28839         this.fireEvent('valid', this);
28840     },
28841     
28842     /**
28843      * Mark this field as invalid
28844      * @param {String} msg The validation message
28845      */
28846     markInvalid : function(msg)
28847     {
28848         this.iconEl.show();
28849         
28850         this.iconEl.removeClass(this.validClass);
28851         
28852         this.iconEl.addClass(this.invalidClass);
28853         
28854         this.fireEvent('invalid', this, msg);
28855     }
28856     
28857    
28858 });
28859
28860 Roo.apply(Roo.bootstrap.FieldLabel, {
28861     
28862     groups: {},
28863     
28864      /**
28865     * register a FieldLabel Group
28866     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28867     */
28868     register : function(label)
28869     {
28870         if(this.groups.hasOwnProperty(label.target)){
28871             return;
28872         }
28873      
28874         this.groups[label.target] = label;
28875         
28876     },
28877     /**
28878     * fetch a FieldLabel Group based on the target
28879     * @param {string} target
28880     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28881     */
28882     get: function(target) {
28883         if (typeof(this.groups[target]) == 'undefined') {
28884             return false;
28885         }
28886         
28887         return this.groups[target] ;
28888     }
28889 });
28890
28891  
28892
28893  /*
28894  * - LGPL
28895  *
28896  * page DateSplitField.
28897  * 
28898  */
28899
28900
28901 /**
28902  * @class Roo.bootstrap.DateSplitField
28903  * @extends Roo.bootstrap.Component
28904  * Bootstrap DateSplitField class
28905  * @cfg {string} fieldLabel - the label associated
28906  * @cfg {Number} labelWidth set the width of label (0-12)
28907  * @cfg {String} labelAlign (top|left)
28908  * @cfg {Boolean} dayAllowBlank (true|false) default false
28909  * @cfg {Boolean} monthAllowBlank (true|false) default false
28910  * @cfg {Boolean} yearAllowBlank (true|false) default false
28911  * @cfg {string} dayPlaceholder 
28912  * @cfg {string} monthPlaceholder
28913  * @cfg {string} yearPlaceholder
28914  * @cfg {string} dayFormat default 'd'
28915  * @cfg {string} monthFormat default 'm'
28916  * @cfg {string} yearFormat default 'Y'
28917
28918  *     
28919  * @constructor
28920  * Create a new DateSplitField
28921  * @param {Object} config The config object
28922  */
28923
28924 Roo.bootstrap.DateSplitField = function(config){
28925     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28926     
28927     this.addEvents({
28928         // raw events
28929          /**
28930          * @event years
28931          * getting the data of years
28932          * @param {Roo.bootstrap.DateSplitField} this
28933          * @param {Object} years
28934          */
28935         "years" : true,
28936         /**
28937          * @event days
28938          * getting the data of days
28939          * @param {Roo.bootstrap.DateSplitField} this
28940          * @param {Object} days
28941          */
28942         "days" : true,
28943         /**
28944          * @event invalid
28945          * Fires after the field has been marked as invalid.
28946          * @param {Roo.form.Field} this
28947          * @param {String} msg The validation message
28948          */
28949         invalid : true,
28950        /**
28951          * @event valid
28952          * Fires after the field has been validated with no errors.
28953          * @param {Roo.form.Field} this
28954          */
28955         valid : true
28956     });
28957 };
28958
28959 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28960     
28961     fieldLabel : '',
28962     labelAlign : 'top',
28963     labelWidth : 3,
28964     dayAllowBlank : false,
28965     monthAllowBlank : false,
28966     yearAllowBlank : false,
28967     dayPlaceholder : '',
28968     monthPlaceholder : '',
28969     yearPlaceholder : '',
28970     dayFormat : 'd',
28971     monthFormat : 'm',
28972     yearFormat : 'Y',
28973     isFormField : true,
28974     
28975     getAutoCreate : function()
28976     {
28977         var cfg = {
28978             tag : 'div',
28979             cls : 'row roo-date-split-field-group',
28980             cn : [
28981                 {
28982                     tag : 'input',
28983                     type : 'hidden',
28984                     cls : 'form-hidden-field roo-date-split-field-group-value',
28985                     name : this.name
28986                 }
28987             ]
28988         };
28989         
28990         if(this.fieldLabel){
28991             cfg.cn.push({
28992                 tag : 'div',
28993                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28994                 cn : [
28995                     {
28996                         tag : 'label',
28997                         html : this.fieldLabel
28998                     }
28999                 ]
29000             });
29001         }
29002         
29003         Roo.each(['day', 'month', 'year'], function(t){
29004             cfg.cn.push({
29005                 tag : 'div',
29006                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29007             });
29008         }, this);
29009         
29010         return cfg;
29011     },
29012     
29013     inputEl: function ()
29014     {
29015         return this.el.select('.roo-date-split-field-group-value', true).first();
29016     },
29017     
29018     onRender : function(ct, position) 
29019     {
29020         var _this = this;
29021         
29022         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29023         
29024         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29025         
29026         this.dayField = new Roo.bootstrap.ComboBox({
29027             allowBlank : this.dayAllowBlank,
29028             alwaysQuery : true,
29029             displayField : 'value',
29030             editable : false,
29031             fieldLabel : '',
29032             forceSelection : true,
29033             mode : 'local',
29034             placeholder : this.dayPlaceholder,
29035             selectOnFocus : true,
29036             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29037             triggerAction : 'all',
29038             typeAhead : true,
29039             valueField : 'value',
29040             store : new Roo.data.SimpleStore({
29041                 data : (function() {    
29042                     var days = [];
29043                     _this.fireEvent('days', _this, days);
29044                     return days;
29045                 })(),
29046                 fields : [ 'value' ]
29047             }),
29048             listeners : {
29049                 select : function (_self, record, index)
29050                 {
29051                     _this.setValue(_this.getValue());
29052                 }
29053             }
29054         });
29055
29056         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29057         
29058         this.monthField = new Roo.bootstrap.MonthField({
29059             after : '<i class=\"fa fa-calendar\"></i>',
29060             allowBlank : this.monthAllowBlank,
29061             placeholder : this.monthPlaceholder,
29062             readOnly : true,
29063             listeners : {
29064                 render : function (_self)
29065                 {
29066                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29067                         e.preventDefault();
29068                         _self.focus();
29069                     });
29070                 },
29071                 select : function (_self, oldvalue, newvalue)
29072                 {
29073                     _this.setValue(_this.getValue());
29074                 }
29075             }
29076         });
29077         
29078         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29079         
29080         this.yearField = new Roo.bootstrap.ComboBox({
29081             allowBlank : this.yearAllowBlank,
29082             alwaysQuery : true,
29083             displayField : 'value',
29084             editable : false,
29085             fieldLabel : '',
29086             forceSelection : true,
29087             mode : 'local',
29088             placeholder : this.yearPlaceholder,
29089             selectOnFocus : true,
29090             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29091             triggerAction : 'all',
29092             typeAhead : true,
29093             valueField : 'value',
29094             store : new Roo.data.SimpleStore({
29095                 data : (function() {
29096                     var years = [];
29097                     _this.fireEvent('years', _this, years);
29098                     return years;
29099                 })(),
29100                 fields : [ 'value' ]
29101             }),
29102             listeners : {
29103                 select : function (_self, record, index)
29104                 {
29105                     _this.setValue(_this.getValue());
29106                 }
29107             }
29108         });
29109
29110         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29111     },
29112     
29113     setValue : function(v, format)
29114     {
29115         this.inputEl.dom.value = v;
29116         
29117         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29118         
29119         var d = Date.parseDate(v, f);
29120         
29121         if(!d){
29122             this.validate();
29123             return;
29124         }
29125         
29126         this.setDay(d.format(this.dayFormat));
29127         this.setMonth(d.format(this.monthFormat));
29128         this.setYear(d.format(this.yearFormat));
29129         
29130         this.validate();
29131         
29132         return;
29133     },
29134     
29135     setDay : function(v)
29136     {
29137         this.dayField.setValue(v);
29138         this.inputEl.dom.value = this.getValue();
29139         this.validate();
29140         return;
29141     },
29142     
29143     setMonth : function(v)
29144     {
29145         this.monthField.setValue(v, true);
29146         this.inputEl.dom.value = this.getValue();
29147         this.validate();
29148         return;
29149     },
29150     
29151     setYear : function(v)
29152     {
29153         this.yearField.setValue(v);
29154         this.inputEl.dom.value = this.getValue();
29155         this.validate();
29156         return;
29157     },
29158     
29159     getDay : function()
29160     {
29161         return this.dayField.getValue();
29162     },
29163     
29164     getMonth : function()
29165     {
29166         return this.monthField.getValue();
29167     },
29168     
29169     getYear : function()
29170     {
29171         return this.yearField.getValue();
29172     },
29173     
29174     getValue : function()
29175     {
29176         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29177         
29178         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29179         
29180         return date;
29181     },
29182     
29183     reset : function()
29184     {
29185         this.setDay('');
29186         this.setMonth('');
29187         this.setYear('');
29188         this.inputEl.dom.value = '';
29189         this.validate();
29190         return;
29191     },
29192     
29193     validate : function()
29194     {
29195         var d = this.dayField.validate();
29196         var m = this.monthField.validate();
29197         var y = this.yearField.validate();
29198         
29199         var valid = true;
29200         
29201         if(
29202                 (!this.dayAllowBlank && !d) ||
29203                 (!this.monthAllowBlank && !m) ||
29204                 (!this.yearAllowBlank && !y)
29205         ){
29206             valid = false;
29207         }
29208         
29209         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29210             return valid;
29211         }
29212         
29213         if(valid){
29214             this.markValid();
29215             return valid;
29216         }
29217         
29218         this.markInvalid();
29219         
29220         return valid;
29221     },
29222     
29223     markValid : function()
29224     {
29225         
29226         var label = this.el.select('label', true).first();
29227         var icon = this.el.select('i.fa-star', true).first();
29228
29229         if(label && icon){
29230             icon.remove();
29231         }
29232         
29233         this.fireEvent('valid', this);
29234     },
29235     
29236      /**
29237      * Mark this field as invalid
29238      * @param {String} msg The validation message
29239      */
29240     markInvalid : function(msg)
29241     {
29242         
29243         var label = this.el.select('label', true).first();
29244         var icon = this.el.select('i.fa-star', true).first();
29245
29246         if(label && !icon){
29247             this.el.select('.roo-date-split-field-label', true).createChild({
29248                 tag : 'i',
29249                 cls : 'text-danger fa fa-lg fa-star',
29250                 tooltip : 'This field is required',
29251                 style : 'margin-right:5px;'
29252             }, label, true);
29253         }
29254         
29255         this.fireEvent('invalid', this, msg);
29256     },
29257     
29258     clearInvalid : function()
29259     {
29260         var label = this.el.select('label', true).first();
29261         var icon = this.el.select('i.fa-star', true).first();
29262
29263         if(label && icon){
29264             icon.remove();
29265         }
29266         
29267         this.fireEvent('valid', this);
29268     },
29269     
29270     getName: function()
29271     {
29272         return this.name;
29273     }
29274     
29275 });
29276
29277  /**
29278  *
29279  * This is based on 
29280  * http://masonry.desandro.com
29281  *
29282  * The idea is to render all the bricks based on vertical width...
29283  *
29284  * The original code extends 'outlayer' - we might need to use that....
29285  * 
29286  */
29287
29288
29289 /**
29290  * @class Roo.bootstrap.LayoutMasonry
29291  * @extends Roo.bootstrap.Component
29292  * Bootstrap Layout Masonry class
29293  * 
29294  * @constructor
29295  * Create a new Element
29296  * @param {Object} config The config object
29297  */
29298
29299 Roo.bootstrap.LayoutMasonry = function(config){
29300     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29301     
29302     this.bricks = [];
29303     
29304 };
29305
29306 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29307     
29308     /**
29309      * @cfg {Boolean} isLayoutInstant = no animation?
29310      */   
29311     isLayoutInstant : false, // needed?
29312    
29313     /**
29314      * @cfg {Number} boxWidth  width of the columns
29315      */   
29316     boxWidth : 450,
29317     
29318       /**
29319      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29320      */   
29321     boxHeight : 0,
29322     
29323     /**
29324      * @cfg {Number} padWidth padding below box..
29325      */   
29326     padWidth : 10, 
29327     
29328     /**
29329      * @cfg {Number} gutter gutter width..
29330      */   
29331     gutter : 10,
29332     
29333      /**
29334      * @cfg {Number} maxCols maximum number of columns
29335      */   
29336     
29337     maxCols: 0,
29338     
29339     /**
29340      * @cfg {Boolean} isAutoInitial defalut true
29341      */   
29342     isAutoInitial : true, 
29343     
29344     containerWidth: 0,
29345     
29346     /**
29347      * @cfg {Boolean} isHorizontal defalut false
29348      */   
29349     isHorizontal : false, 
29350
29351     currentSize : null,
29352     
29353     tag: 'div',
29354     
29355     cls: '',
29356     
29357     bricks: null, //CompositeElement
29358     
29359     cols : 1,
29360     
29361     _isLayoutInited : false,
29362     
29363 //    isAlternative : false, // only use for vertical layout...
29364     
29365     /**
29366      * @cfg {Number} alternativePadWidth padding below box..
29367      */   
29368     alternativePadWidth : 50, 
29369     
29370     getAutoCreate : function(){
29371         
29372         var cfg = {
29373             tag: this.tag,
29374             cls: 'blog-masonary-wrapper ' + this.cls,
29375             cn : {
29376                 cls : 'mas-boxes masonary'
29377             }
29378         };
29379         
29380         return cfg;
29381     },
29382     
29383     getChildContainer: function( )
29384     {
29385         if (this.boxesEl) {
29386             return this.boxesEl;
29387         }
29388         
29389         this.boxesEl = this.el.select('.mas-boxes').first();
29390         
29391         return this.boxesEl;
29392     },
29393     
29394     
29395     initEvents : function()
29396     {
29397         var _this = this;
29398         
29399         if(this.isAutoInitial){
29400             Roo.log('hook children rendered');
29401             this.on('childrenrendered', function() {
29402                 Roo.log('children rendered');
29403                 _this.initial();
29404             } ,this);
29405         }
29406     },
29407     
29408     initial : function()
29409     {
29410         this.currentSize = this.el.getBox(true);
29411         
29412         Roo.EventManager.onWindowResize(this.resize, this); 
29413
29414         if(!this.isAutoInitial){
29415             this.layout();
29416             return;
29417         }
29418         
29419         this.layout();
29420         
29421         return;
29422         //this.layout.defer(500,this);
29423         
29424     },
29425     
29426     resize : function()
29427     {
29428         Roo.log('resize');
29429         
29430         var cs = this.el.getBox(true);
29431         
29432         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29433             Roo.log("no change in with or X");
29434             return;
29435         }
29436         
29437         this.currentSize = cs;
29438         
29439         this.layout();
29440         
29441     },
29442     
29443     layout : function()
29444     {   
29445         this._resetLayout();
29446         
29447         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29448         
29449         this.layoutItems( isInstant );
29450       
29451         this._isLayoutInited = true;
29452         
29453     },
29454     
29455     _resetLayout : function()
29456     {
29457         if(this.isHorizontal){
29458             this.horizontalMeasureColumns();
29459             return;
29460         }
29461         
29462         this.verticalMeasureColumns();
29463         
29464     },
29465     
29466     verticalMeasureColumns : function()
29467     {
29468         this.getContainerWidth();
29469         
29470 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29471 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29472 //            return;
29473 //        }
29474         
29475         var boxWidth = this.boxWidth + this.padWidth;
29476         
29477         if(this.containerWidth < this.boxWidth){
29478             boxWidth = this.containerWidth
29479         }
29480         
29481         var containerWidth = this.containerWidth;
29482         
29483         var cols = Math.floor(containerWidth / boxWidth);
29484         
29485         this.cols = Math.max( cols, 1 );
29486         
29487         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29488         
29489         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29490         
29491         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29492         
29493         this.colWidth = boxWidth + avail - this.padWidth;
29494         
29495         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29496         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29497     },
29498     
29499     horizontalMeasureColumns : function()
29500     {
29501         this.getContainerWidth();
29502         
29503         var boxWidth = this.boxWidth;
29504         
29505         if(this.containerWidth < boxWidth){
29506             boxWidth = this.containerWidth;
29507         }
29508         
29509         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29510         
29511         this.el.setHeight(boxWidth);
29512         
29513     },
29514     
29515     getContainerWidth : function()
29516     {
29517         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29518     },
29519     
29520     layoutItems : function( isInstant )
29521     {
29522         var items = Roo.apply([], this.bricks);
29523         
29524         if(this.isHorizontal){
29525             this._horizontalLayoutItems( items , isInstant );
29526             return;
29527         }
29528         
29529 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29530 //            this._verticalAlternativeLayoutItems( items , isInstant );
29531 //            return;
29532 //        }
29533         
29534         this._verticalLayoutItems( items , isInstant );
29535         
29536     },
29537     
29538     _verticalLayoutItems : function ( items , isInstant)
29539     {
29540         if ( !items || !items.length ) {
29541             return;
29542         }
29543         
29544         var standard = [
29545             ['xs', 'xs', 'xs', 'tall'],
29546             ['xs', 'xs', 'tall'],
29547             ['xs', 'xs', 'sm'],
29548             ['xs', 'xs', 'xs'],
29549             ['xs', 'tall'],
29550             ['xs', 'sm'],
29551             ['xs', 'xs'],
29552             ['xs'],
29553             
29554             ['sm', 'xs', 'xs'],
29555             ['sm', 'xs'],
29556             ['sm'],
29557             
29558             ['tall', 'xs', 'xs', 'xs'],
29559             ['tall', 'xs', 'xs'],
29560             ['tall', 'xs'],
29561             ['tall']
29562             
29563         ];
29564         
29565         var queue = [];
29566         
29567         var boxes = [];
29568         
29569         var box = [];
29570         
29571         Roo.each(items, function(item, k){
29572             
29573             switch (item.size) {
29574                 // these layouts take up a full box,
29575                 case 'md' :
29576                 case 'md-left' :
29577                 case 'md-right' :
29578                 case 'wide' :
29579                     
29580                     if(box.length){
29581                         boxes.push(box);
29582                         box = [];
29583                     }
29584                     
29585                     boxes.push([item]);
29586                     
29587                     break;
29588                     
29589                 case 'xs' :
29590                 case 'sm' :
29591                 case 'tall' :
29592                     
29593                     box.push(item);
29594                     
29595                     break;
29596                 default :
29597                     break;
29598                     
29599             }
29600             
29601         }, this);
29602         
29603         if(box.length){
29604             boxes.push(box);
29605             box = [];
29606         }
29607         
29608         var filterPattern = function(box, length)
29609         {
29610             if(!box.length){
29611                 return;
29612             }
29613             
29614             var match = false;
29615             
29616             var pattern = box.slice(0, length);
29617             
29618             var format = [];
29619             
29620             Roo.each(pattern, function(i){
29621                 format.push(i.size);
29622             }, this);
29623             
29624             Roo.each(standard, function(s){
29625                 
29626                 if(String(s) != String(format)){
29627                     return;
29628                 }
29629                 
29630                 match = true;
29631                 return false;
29632                 
29633             }, this);
29634             
29635             if(!match && length == 1){
29636                 return;
29637             }
29638             
29639             if(!match){
29640                 filterPattern(box, length - 1);
29641                 return;
29642             }
29643                 
29644             queue.push(pattern);
29645
29646             box = box.slice(length, box.length);
29647
29648             filterPattern(box, 4);
29649
29650             return;
29651             
29652         }
29653         
29654         Roo.each(boxes, function(box, k){
29655             
29656             if(!box.length){
29657                 return;
29658             }
29659             
29660             if(box.length == 1){
29661                 queue.push(box);
29662                 return;
29663             }
29664             
29665             filterPattern(box, 4);
29666             
29667         }, this);
29668         
29669         this._processVerticalLayoutQueue( queue, isInstant );
29670         
29671     },
29672     
29673 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29674 //    {
29675 //        if ( !items || !items.length ) {
29676 //            return;
29677 //        }
29678 //
29679 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29680 //        
29681 //    },
29682     
29683     _horizontalLayoutItems : function ( items , isInstant)
29684     {
29685         if ( !items || !items.length || items.length < 3) {
29686             return;
29687         }
29688         
29689         items.reverse();
29690         
29691         var eItems = items.slice(0, 3);
29692         
29693         items = items.slice(3, items.length);
29694         
29695         var standard = [
29696             ['xs', 'xs', 'xs', 'wide'],
29697             ['xs', 'xs', 'wide'],
29698             ['xs', 'xs', 'sm'],
29699             ['xs', 'xs', 'xs'],
29700             ['xs', 'wide'],
29701             ['xs', 'sm'],
29702             ['xs', 'xs'],
29703             ['xs'],
29704             
29705             ['sm', 'xs', 'xs'],
29706             ['sm', 'xs'],
29707             ['sm'],
29708             
29709             ['wide', 'xs', 'xs', 'xs'],
29710             ['wide', 'xs', 'xs'],
29711             ['wide', 'xs'],
29712             ['wide'],
29713             
29714             ['wide-thin']
29715         ];
29716         
29717         var queue = [];
29718         
29719         var boxes = [];
29720         
29721         var box = [];
29722         
29723         Roo.each(items, function(item, k){
29724             
29725             switch (item.size) {
29726                 case 'md' :
29727                 case 'md-left' :
29728                 case 'md-right' :
29729                 case 'tall' :
29730                     
29731                     if(box.length){
29732                         boxes.push(box);
29733                         box = [];
29734                     }
29735                     
29736                     boxes.push([item]);
29737                     
29738                     break;
29739                     
29740                 case 'xs' :
29741                 case 'sm' :
29742                 case 'wide' :
29743                 case 'wide-thin' :
29744                     
29745                     box.push(item);
29746                     
29747                     break;
29748                 default :
29749                     break;
29750                     
29751             }
29752             
29753         }, this);
29754         
29755         if(box.length){
29756             boxes.push(box);
29757             box = [];
29758         }
29759         
29760         var filterPattern = function(box, length)
29761         {
29762             if(!box.length){
29763                 return;
29764             }
29765             
29766             var match = false;
29767             
29768             var pattern = box.slice(0, length);
29769             
29770             var format = [];
29771             
29772             Roo.each(pattern, function(i){
29773                 format.push(i.size);
29774             }, this);
29775             
29776             Roo.each(standard, function(s){
29777                 
29778                 if(String(s) != String(format)){
29779                     return;
29780                 }
29781                 
29782                 match = true;
29783                 return false;
29784                 
29785             }, this);
29786             
29787             if(!match && length == 1){
29788                 return;
29789             }
29790             
29791             if(!match){
29792                 filterPattern(box, length - 1);
29793                 return;
29794             }
29795                 
29796             queue.push(pattern);
29797
29798             box = box.slice(length, box.length);
29799
29800             filterPattern(box, 4);
29801
29802             return;
29803             
29804         }
29805         
29806         Roo.each(boxes, function(box, k){
29807             
29808             if(!box.length){
29809                 return;
29810             }
29811             
29812             if(box.length == 1){
29813                 queue.push(box);
29814                 return;
29815             }
29816             
29817             filterPattern(box, 4);
29818             
29819         }, this);
29820         
29821         
29822         var prune = [];
29823         
29824         var pos = this.el.getBox(true);
29825         
29826         var minX = pos.x;
29827         
29828         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29829         
29830         var hit_end = false;
29831         
29832         Roo.each(queue, function(box){
29833             
29834             if(hit_end){
29835                 
29836                 Roo.each(box, function(b){
29837                 
29838                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29839                     b.el.hide();
29840
29841                 }, this);
29842
29843                 return;
29844             }
29845             
29846             var mx = 0;
29847             
29848             Roo.each(box, function(b){
29849                 
29850                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29851                 b.el.show();
29852
29853                 mx = Math.max(mx, b.x);
29854                 
29855             }, this);
29856             
29857             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29858             
29859             if(maxX < minX){
29860                 
29861                 Roo.each(box, function(b){
29862                 
29863                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29864                     b.el.hide();
29865                     
29866                 }, this);
29867                 
29868                 hit_end = true;
29869                 
29870                 return;
29871             }
29872             
29873             prune.push(box);
29874             
29875         }, this);
29876         
29877         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29878     },
29879     
29880     /** Sets position of item in DOM
29881     * @param {Element} item
29882     * @param {Number} x - horizontal position
29883     * @param {Number} y - vertical position
29884     * @param {Boolean} isInstant - disables transitions
29885     */
29886     _processVerticalLayoutQueue : function( queue, isInstant )
29887     {
29888         var pos = this.el.getBox(true);
29889         var x = pos.x;
29890         var y = pos.y;
29891         var maxY = [];
29892         
29893         for (var i = 0; i < this.cols; i++){
29894             maxY[i] = pos.y;
29895         }
29896         
29897         Roo.each(queue, function(box, k){
29898             
29899             var col = k % this.cols;
29900             
29901             Roo.each(box, function(b,kk){
29902                 
29903                 b.el.position('absolute');
29904                 
29905                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29906                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29907                 
29908                 if(b.size == 'md-left' || b.size == 'md-right'){
29909                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29910                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29911                 }
29912                 
29913                 b.el.setWidth(width);
29914                 b.el.setHeight(height);
29915                 // iframe?
29916                 b.el.select('iframe',true).setSize(width,height);
29917                 
29918             }, this);
29919             
29920             for (var i = 0; i < this.cols; i++){
29921                 
29922                 if(maxY[i] < maxY[col]){
29923                     col = i;
29924                     continue;
29925                 }
29926                 
29927                 col = Math.min(col, i);
29928                 
29929             }
29930             
29931             x = pos.x + col * (this.colWidth + this.padWidth);
29932             
29933             y = maxY[col];
29934             
29935             var positions = [];
29936             
29937             switch (box.length){
29938                 case 1 :
29939                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29940                     break;
29941                 case 2 :
29942                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29943                     break;
29944                 case 3 :
29945                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29946                     break;
29947                 case 4 :
29948                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29949                     break;
29950                 default :
29951                     break;
29952             }
29953             
29954             Roo.each(box, function(b,kk){
29955                 
29956                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29957                 
29958                 var sz = b.el.getSize();
29959                 
29960                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29961                 
29962             }, this);
29963             
29964         }, this);
29965         
29966         var mY = 0;
29967         
29968         for (var i = 0; i < this.cols; i++){
29969             mY = Math.max(mY, maxY[i]);
29970         }
29971         
29972         this.el.setHeight(mY - pos.y);
29973         
29974     },
29975     
29976 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29977 //    {
29978 //        var pos = this.el.getBox(true);
29979 //        var x = pos.x;
29980 //        var y = pos.y;
29981 //        var maxX = pos.right;
29982 //        
29983 //        var maxHeight = 0;
29984 //        
29985 //        Roo.each(items, function(item, k){
29986 //            
29987 //            var c = k % 2;
29988 //            
29989 //            item.el.position('absolute');
29990 //                
29991 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29992 //
29993 //            item.el.setWidth(width);
29994 //
29995 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29996 //
29997 //            item.el.setHeight(height);
29998 //            
29999 //            if(c == 0){
30000 //                item.el.setXY([x, y], isInstant ? false : true);
30001 //            } else {
30002 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30003 //            }
30004 //            
30005 //            y = y + height + this.alternativePadWidth;
30006 //            
30007 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30008 //            
30009 //        }, this);
30010 //        
30011 //        this.el.setHeight(maxHeight);
30012 //        
30013 //    },
30014     
30015     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30016     {
30017         var pos = this.el.getBox(true);
30018         
30019         var minX = pos.x;
30020         var minY = pos.y;
30021         
30022         var maxX = pos.right;
30023         
30024         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30025         
30026         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30027         
30028         Roo.each(queue, function(box, k){
30029             
30030             Roo.each(box, function(b, kk){
30031                 
30032                 b.el.position('absolute');
30033                 
30034                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30035                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30036                 
30037                 if(b.size == 'md-left' || b.size == 'md-right'){
30038                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30039                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30040                 }
30041                 
30042                 b.el.setWidth(width);
30043                 b.el.setHeight(height);
30044                 
30045             }, this);
30046             
30047             if(!box.length){
30048                 return;
30049             }
30050             
30051             var positions = [];
30052             
30053             switch (box.length){
30054                 case 1 :
30055                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30056                     break;
30057                 case 2 :
30058                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30059                     break;
30060                 case 3 :
30061                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30062                     break;
30063                 case 4 :
30064                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30065                     break;
30066                 default :
30067                     break;
30068             }
30069             
30070             Roo.each(box, function(b,kk){
30071                 
30072                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30073                 
30074                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30075                 
30076             }, this);
30077             
30078         }, this);
30079         
30080     },
30081     
30082     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30083     {
30084         Roo.each(eItems, function(b,k){
30085             
30086             b.size = (k == 0) ? 'sm' : 'xs';
30087             b.x = (k == 0) ? 2 : 1;
30088             b.y = (k == 0) ? 2 : 1;
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                 
30094             b.el.setWidth(width);
30095             
30096             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30097             
30098             b.el.setHeight(height);
30099             
30100         }, this);
30101
30102         var positions = [];
30103         
30104         positions.push({
30105             x : maxX - this.unitWidth * 2 - this.gutter,
30106             y : minY
30107         });
30108         
30109         positions.push({
30110             x : maxX - this.unitWidth,
30111             y : minY + (this.unitWidth + this.gutter) * 2
30112         });
30113         
30114         positions.push({
30115             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30116             y : minY
30117         });
30118         
30119         Roo.each(eItems, function(b,k){
30120             
30121             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30122
30123         }, this);
30124         
30125     },
30126     
30127     getVerticalOneBoxColPositions : function(x, y, box)
30128     {
30129         var pos = [];
30130         
30131         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30132         
30133         if(box[0].size == 'md-left'){
30134             rand = 0;
30135         }
30136         
30137         if(box[0].size == 'md-right'){
30138             rand = 1;
30139         }
30140         
30141         pos.push({
30142             x : x + (this.unitWidth + this.gutter) * rand,
30143             y : y
30144         });
30145         
30146         return pos;
30147     },
30148     
30149     getVerticalTwoBoxColPositions : function(x, y, box)
30150     {
30151         var pos = [];
30152         
30153         if(box[0].size == 'xs'){
30154             
30155             pos.push({
30156                 x : x,
30157                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30158             });
30159
30160             pos.push({
30161                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30162                 y : y
30163             });
30164             
30165             return pos;
30166             
30167         }
30168         
30169         pos.push({
30170             x : x,
30171             y : y
30172         });
30173
30174         pos.push({
30175             x : x + (this.unitWidth + this.gutter) * 2,
30176             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30177         });
30178         
30179         return pos;
30180         
30181     },
30182     
30183     getVerticalThreeBoxColPositions : function(x, y, box)
30184     {
30185         var pos = [];
30186         
30187         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30188             
30189             pos.push({
30190                 x : x,
30191                 y : y
30192             });
30193
30194             pos.push({
30195                 x : x + (this.unitWidth + this.gutter) * 1,
30196                 y : y
30197             });
30198             
30199             pos.push({
30200                 x : x + (this.unitWidth + this.gutter) * 2,
30201                 y : y
30202             });
30203             
30204             return pos;
30205             
30206         }
30207         
30208         if(box[0].size == 'xs' && box[1].size == 'xs'){
30209             
30210             pos.push({
30211                 x : x,
30212                 y : y
30213             });
30214
30215             pos.push({
30216                 x : x,
30217                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30218             });
30219             
30220             pos.push({
30221                 x : x + (this.unitWidth + this.gutter) * 1,
30222                 y : y
30223             });
30224             
30225             return pos;
30226             
30227         }
30228         
30229         pos.push({
30230             x : x,
30231             y : y
30232         });
30233
30234         pos.push({
30235             x : x + (this.unitWidth + this.gutter) * 2,
30236             y : y
30237         });
30238
30239         pos.push({
30240             x : x + (this.unitWidth + this.gutter) * 2,
30241             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30242         });
30243             
30244         return pos;
30245         
30246     },
30247     
30248     getVerticalFourBoxColPositions : function(x, y, box)
30249     {
30250         var pos = [];
30251         
30252         if(box[0].size == 'xs'){
30253             
30254             pos.push({
30255                 x : x,
30256                 y : y
30257             });
30258
30259             pos.push({
30260                 x : x,
30261                 y : y + (this.unitHeight + this.gutter) * 1
30262             });
30263             
30264             pos.push({
30265                 x : x,
30266                 y : y + (this.unitHeight + this.gutter) * 2
30267             });
30268             
30269             pos.push({
30270                 x : x + (this.unitWidth + this.gutter) * 1,
30271                 y : y
30272             });
30273             
30274             return pos;
30275             
30276         }
30277         
30278         pos.push({
30279             x : x,
30280             y : y
30281         });
30282
30283         pos.push({
30284             x : x + (this.unitWidth + this.gutter) * 2,
30285             y : y
30286         });
30287
30288         pos.push({
30289             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30290             y : y + (this.unitHeight + this.gutter) * 1
30291         });
30292
30293         pos.push({
30294             x : x + (this.unitWidth + this.gutter) * 2,
30295             y : y + (this.unitWidth + this.gutter) * 2
30296         });
30297
30298         return pos;
30299         
30300     },
30301     
30302     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30303     {
30304         var pos = [];
30305         
30306         if(box[0].size == 'md-left'){
30307             pos.push({
30308                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30309                 y : minY
30310             });
30311             
30312             return pos;
30313         }
30314         
30315         if(box[0].size == 'md-right'){
30316             pos.push({
30317                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30318                 y : minY + (this.unitWidth + this.gutter) * 1
30319             });
30320             
30321             return pos;
30322         }
30323         
30324         var rand = Math.floor(Math.random() * (4 - box[0].y));
30325         
30326         pos.push({
30327             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30328             y : minY + (this.unitWidth + this.gutter) * rand
30329         });
30330         
30331         return pos;
30332         
30333     },
30334     
30335     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30336     {
30337         var pos = [];
30338         
30339         if(box[0].size == 'xs'){
30340             
30341             pos.push({
30342                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30343                 y : minY
30344             });
30345
30346             pos.push({
30347                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30348                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30349             });
30350             
30351             return pos;
30352             
30353         }
30354         
30355         pos.push({
30356             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30357             y : minY
30358         });
30359
30360         pos.push({
30361             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30362             y : minY + (this.unitWidth + this.gutter) * 2
30363         });
30364         
30365         return pos;
30366         
30367     },
30368     
30369     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30370     {
30371         var pos = [];
30372         
30373         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30374             
30375             pos.push({
30376                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30377                 y : minY
30378             });
30379
30380             pos.push({
30381                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30382                 y : minY + (this.unitWidth + this.gutter) * 1
30383             });
30384             
30385             pos.push({
30386                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30387                 y : minY + (this.unitWidth + this.gutter) * 2
30388             });
30389             
30390             return pos;
30391             
30392         }
30393         
30394         if(box[0].size == 'xs' && box[1].size == 'xs'){
30395             
30396             pos.push({
30397                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30398                 y : minY
30399             });
30400
30401             pos.push({
30402                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30403                 y : minY
30404             });
30405             
30406             pos.push({
30407                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30408                 y : minY + (this.unitWidth + this.gutter) * 1
30409             });
30410             
30411             return pos;
30412             
30413         }
30414         
30415         pos.push({
30416             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30417             y : minY
30418         });
30419
30420         pos.push({
30421             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30422             y : minY + (this.unitWidth + this.gutter) * 2
30423         });
30424
30425         pos.push({
30426             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30427             y : minY + (this.unitWidth + this.gutter) * 2
30428         });
30429             
30430         return pos;
30431         
30432     },
30433     
30434     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30435     {
30436         var pos = [];
30437         
30438         if(box[0].size == 'xs'){
30439             
30440             pos.push({
30441                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30442                 y : minY
30443             });
30444
30445             pos.push({
30446                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30447                 y : minY
30448             });
30449             
30450             pos.push({
30451                 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),
30452                 y : minY
30453             });
30454             
30455             pos.push({
30456                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30457                 y : minY + (this.unitWidth + this.gutter) * 1
30458             });
30459             
30460             return pos;
30461             
30462         }
30463         
30464         pos.push({
30465             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30466             y : minY
30467         });
30468         
30469         pos.push({
30470             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30471             y : minY + (this.unitWidth + this.gutter) * 2
30472         });
30473         
30474         pos.push({
30475             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30476             y : minY + (this.unitWidth + this.gutter) * 2
30477         });
30478         
30479         pos.push({
30480             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),
30481             y : minY + (this.unitWidth + this.gutter) * 2
30482         });
30483
30484         return pos;
30485         
30486     }
30487     
30488 });
30489
30490  
30491
30492  /**
30493  *
30494  * This is based on 
30495  * http://masonry.desandro.com
30496  *
30497  * The idea is to render all the bricks based on vertical width...
30498  *
30499  * The original code extends 'outlayer' - we might need to use that....
30500  * 
30501  */
30502
30503
30504 /**
30505  * @class Roo.bootstrap.LayoutMasonryAuto
30506  * @extends Roo.bootstrap.Component
30507  * Bootstrap Layout Masonry class
30508  * 
30509  * @constructor
30510  * Create a new Element
30511  * @param {Object} config The config object
30512  */
30513
30514 Roo.bootstrap.LayoutMasonryAuto = function(config){
30515     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30516 };
30517
30518 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30519     
30520       /**
30521      * @cfg {Boolean} isFitWidth  - resize the width..
30522      */   
30523     isFitWidth : false,  // options..
30524     /**
30525      * @cfg {Boolean} isOriginLeft = left align?
30526      */   
30527     isOriginLeft : true,
30528     /**
30529      * @cfg {Boolean} isOriginTop = top align?
30530      */   
30531     isOriginTop : false,
30532     /**
30533      * @cfg {Boolean} isLayoutInstant = no animation?
30534      */   
30535     isLayoutInstant : false, // needed?
30536     /**
30537      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30538      */   
30539     isResizingContainer : true,
30540     /**
30541      * @cfg {Number} columnWidth  width of the columns 
30542      */   
30543     
30544     columnWidth : 0,
30545     
30546     /**
30547      * @cfg {Number} maxCols maximum number of columns
30548      */   
30549     
30550     maxCols: 0,
30551     /**
30552      * @cfg {Number} padHeight padding below box..
30553      */   
30554     
30555     padHeight : 10, 
30556     
30557     /**
30558      * @cfg {Boolean} isAutoInitial defalut true
30559      */   
30560     
30561     isAutoInitial : true, 
30562     
30563     // private?
30564     gutter : 0,
30565     
30566     containerWidth: 0,
30567     initialColumnWidth : 0,
30568     currentSize : null,
30569     
30570     colYs : null, // array.
30571     maxY : 0,
30572     padWidth: 10,
30573     
30574     
30575     tag: 'div',
30576     cls: '',
30577     bricks: null, //CompositeElement
30578     cols : 0, // array?
30579     // element : null, // wrapped now this.el
30580     _isLayoutInited : null, 
30581     
30582     
30583     getAutoCreate : function(){
30584         
30585         var cfg = {
30586             tag: this.tag,
30587             cls: 'blog-masonary-wrapper ' + this.cls,
30588             cn : {
30589                 cls : 'mas-boxes masonary'
30590             }
30591         };
30592         
30593         return cfg;
30594     },
30595     
30596     getChildContainer: function( )
30597     {
30598         if (this.boxesEl) {
30599             return this.boxesEl;
30600         }
30601         
30602         this.boxesEl = this.el.select('.mas-boxes').first();
30603         
30604         return this.boxesEl;
30605     },
30606     
30607     
30608     initEvents : function()
30609     {
30610         var _this = this;
30611         
30612         if(this.isAutoInitial){
30613             Roo.log('hook children rendered');
30614             this.on('childrenrendered', function() {
30615                 Roo.log('children rendered');
30616                 _this.initial();
30617             } ,this);
30618         }
30619         
30620     },
30621     
30622     initial : function()
30623     {
30624         this.reloadItems();
30625
30626         this.currentSize = this.el.getBox(true);
30627
30628         /// was window resize... - let's see if this works..
30629         Roo.EventManager.onWindowResize(this.resize, this); 
30630
30631         if(!this.isAutoInitial){
30632             this.layout();
30633             return;
30634         }
30635         
30636         this.layout.defer(500,this);
30637     },
30638     
30639     reloadItems: function()
30640     {
30641         this.bricks = this.el.select('.masonry-brick', true);
30642         
30643         this.bricks.each(function(b) {
30644             //Roo.log(b.getSize());
30645             if (!b.attr('originalwidth')) {
30646                 b.attr('originalwidth',  b.getSize().width);
30647             }
30648             
30649         });
30650         
30651         Roo.log(this.bricks.elements.length);
30652     },
30653     
30654     resize : function()
30655     {
30656         Roo.log('resize');
30657         var cs = this.el.getBox(true);
30658         
30659         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30660             Roo.log("no change in with or X");
30661             return;
30662         }
30663         this.currentSize = cs;
30664         this.layout();
30665     },
30666     
30667     layout : function()
30668     {
30669          Roo.log('layout');
30670         this._resetLayout();
30671         //this._manageStamps();
30672       
30673         // don't animate first layout
30674         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30675         this.layoutItems( isInstant );
30676       
30677         // flag for initalized
30678         this._isLayoutInited = true;
30679     },
30680     
30681     layoutItems : function( isInstant )
30682     {
30683         //var items = this._getItemsForLayout( this.items );
30684         // original code supports filtering layout items.. we just ignore it..
30685         
30686         this._layoutItems( this.bricks , isInstant );
30687       
30688         this._postLayout();
30689     },
30690     _layoutItems : function ( items , isInstant)
30691     {
30692        //this.fireEvent( 'layout', this, items );
30693     
30694
30695         if ( !items || !items.elements.length ) {
30696           // no items, emit event with empty array
30697             return;
30698         }
30699
30700         var queue = [];
30701         items.each(function(item) {
30702             Roo.log("layout item");
30703             Roo.log(item);
30704             // get x/y object from method
30705             var position = this._getItemLayoutPosition( item );
30706             // enqueue
30707             position.item = item;
30708             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30709             queue.push( position );
30710         }, this);
30711       
30712         this._processLayoutQueue( queue );
30713     },
30714     /** Sets position of item in DOM
30715     * @param {Element} item
30716     * @param {Number} x - horizontal position
30717     * @param {Number} y - vertical position
30718     * @param {Boolean} isInstant - disables transitions
30719     */
30720     _processLayoutQueue : function( queue )
30721     {
30722         for ( var i=0, len = queue.length; i < len; i++ ) {
30723             var obj = queue[i];
30724             obj.item.position('absolute');
30725             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30726         }
30727     },
30728       
30729     
30730     /**
30731     * Any logic you want to do after each layout,
30732     * i.e. size the container
30733     */
30734     _postLayout : function()
30735     {
30736         this.resizeContainer();
30737     },
30738     
30739     resizeContainer : function()
30740     {
30741         if ( !this.isResizingContainer ) {
30742             return;
30743         }
30744         var size = this._getContainerSize();
30745         if ( size ) {
30746             this.el.setSize(size.width,size.height);
30747             this.boxesEl.setSize(size.width,size.height);
30748         }
30749     },
30750     
30751     
30752     
30753     _resetLayout : function()
30754     {
30755         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30756         this.colWidth = this.el.getWidth();
30757         //this.gutter = this.el.getWidth(); 
30758         
30759         this.measureColumns();
30760
30761         // reset column Y
30762         var i = this.cols;
30763         this.colYs = [];
30764         while (i--) {
30765             this.colYs.push( 0 );
30766         }
30767     
30768         this.maxY = 0;
30769     },
30770
30771     measureColumns : function()
30772     {
30773         this.getContainerWidth();
30774       // if columnWidth is 0, default to outerWidth of first item
30775         if ( !this.columnWidth ) {
30776             var firstItem = this.bricks.first();
30777             Roo.log(firstItem);
30778             this.columnWidth  = this.containerWidth;
30779             if (firstItem && firstItem.attr('originalwidth') ) {
30780                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30781             }
30782             // columnWidth fall back to item of first element
30783             Roo.log("set column width?");
30784                         this.initialColumnWidth = this.columnWidth  ;
30785
30786             // if first elem has no width, default to size of container
30787             
30788         }
30789         
30790         
30791         if (this.initialColumnWidth) {
30792             this.columnWidth = this.initialColumnWidth;
30793         }
30794         
30795         
30796             
30797         // column width is fixed at the top - however if container width get's smaller we should
30798         // reduce it...
30799         
30800         // this bit calcs how man columns..
30801             
30802         var columnWidth = this.columnWidth += this.gutter;
30803       
30804         // calculate columns
30805         var containerWidth = this.containerWidth + this.gutter;
30806         
30807         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30808         // fix rounding errors, typically with gutters
30809         var excess = columnWidth - containerWidth % columnWidth;
30810         
30811         
30812         // if overshoot is less than a pixel, round up, otherwise floor it
30813         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30814         cols = Math[ mathMethod ]( cols );
30815         this.cols = Math.max( cols, 1 );
30816         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30817         
30818          // padding positioning..
30819         var totalColWidth = this.cols * this.columnWidth;
30820         var padavail = this.containerWidth - totalColWidth;
30821         // so for 2 columns - we need 3 'pads'
30822         
30823         var padNeeded = (1+this.cols) * this.padWidth;
30824         
30825         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30826         
30827         this.columnWidth += padExtra
30828         //this.padWidth = Math.floor(padavail /  ( this.cols));
30829         
30830         // adjust colum width so that padding is fixed??
30831         
30832         // we have 3 columns ... total = width * 3
30833         // we have X left over... that should be used by 
30834         
30835         //if (this.expandC) {
30836             
30837         //}
30838         
30839         
30840         
30841     },
30842     
30843     getContainerWidth : function()
30844     {
30845        /* // container is parent if fit width
30846         var container = this.isFitWidth ? this.element.parentNode : this.element;
30847         // check that this.size and size are there
30848         // IE8 triggers resize on body size change, so they might not be
30849         
30850         var size = getSize( container );  //FIXME
30851         this.containerWidth = size && size.innerWidth; //FIXME
30852         */
30853          
30854         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30855         
30856     },
30857     
30858     _getItemLayoutPosition : function( item )  // what is item?
30859     {
30860         // we resize the item to our columnWidth..
30861       
30862         item.setWidth(this.columnWidth);
30863         item.autoBoxAdjust  = false;
30864         
30865         var sz = item.getSize();
30866  
30867         // how many columns does this brick span
30868         var remainder = this.containerWidth % this.columnWidth;
30869         
30870         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30871         // round if off by 1 pixel, otherwise use ceil
30872         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30873         colSpan = Math.min( colSpan, this.cols );
30874         
30875         // normally this should be '1' as we dont' currently allow multi width columns..
30876         
30877         var colGroup = this._getColGroup( colSpan );
30878         // get the minimum Y value from the columns
30879         var minimumY = Math.min.apply( Math, colGroup );
30880         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30881         
30882         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30883          
30884         // position the brick
30885         var position = {
30886             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30887             y: this.currentSize.y + minimumY + this.padHeight
30888         };
30889         
30890         Roo.log(position);
30891         // apply setHeight to necessary columns
30892         var setHeight = minimumY + sz.height + this.padHeight;
30893         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30894         
30895         var setSpan = this.cols + 1 - colGroup.length;
30896         for ( var i = 0; i < setSpan; i++ ) {
30897           this.colYs[ shortColIndex + i ] = setHeight ;
30898         }
30899       
30900         return position;
30901     },
30902     
30903     /**
30904      * @param {Number} colSpan - number of columns the element spans
30905      * @returns {Array} colGroup
30906      */
30907     _getColGroup : function( colSpan )
30908     {
30909         if ( colSpan < 2 ) {
30910           // if brick spans only one column, use all the column Ys
30911           return this.colYs;
30912         }
30913       
30914         var colGroup = [];
30915         // how many different places could this brick fit horizontally
30916         var groupCount = this.cols + 1 - colSpan;
30917         // for each group potential horizontal position
30918         for ( var i = 0; i < groupCount; i++ ) {
30919           // make an array of colY values for that one group
30920           var groupColYs = this.colYs.slice( i, i + colSpan );
30921           // and get the max value of the array
30922           colGroup[i] = Math.max.apply( Math, groupColYs );
30923         }
30924         return colGroup;
30925     },
30926     /*
30927     _manageStamp : function( stamp )
30928     {
30929         var stampSize =  stamp.getSize();
30930         var offset = stamp.getBox();
30931         // get the columns that this stamp affects
30932         var firstX = this.isOriginLeft ? offset.x : offset.right;
30933         var lastX = firstX + stampSize.width;
30934         var firstCol = Math.floor( firstX / this.columnWidth );
30935         firstCol = Math.max( 0, firstCol );
30936         
30937         var lastCol = Math.floor( lastX / this.columnWidth );
30938         // lastCol should not go over if multiple of columnWidth #425
30939         lastCol -= lastX % this.columnWidth ? 0 : 1;
30940         lastCol = Math.min( this.cols - 1, lastCol );
30941         
30942         // set colYs to bottom of the stamp
30943         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30944             stampSize.height;
30945             
30946         for ( var i = firstCol; i <= lastCol; i++ ) {
30947           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30948         }
30949     },
30950     */
30951     
30952     _getContainerSize : function()
30953     {
30954         this.maxY = Math.max.apply( Math, this.colYs );
30955         var size = {
30956             height: this.maxY
30957         };
30958       
30959         if ( this.isFitWidth ) {
30960             size.width = this._getContainerFitWidth();
30961         }
30962       
30963         return size;
30964     },
30965     
30966     _getContainerFitWidth : function()
30967     {
30968         var unusedCols = 0;
30969         // count unused columns
30970         var i = this.cols;
30971         while ( --i ) {
30972           if ( this.colYs[i] !== 0 ) {
30973             break;
30974           }
30975           unusedCols++;
30976         }
30977         // fit container to columns that have been used
30978         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30979     },
30980     
30981     needsResizeLayout : function()
30982     {
30983         var previousWidth = this.containerWidth;
30984         this.getContainerWidth();
30985         return previousWidth !== this.containerWidth;
30986     }
30987  
30988 });
30989
30990  
30991
30992  /*
30993  * - LGPL
30994  *
30995  * element
30996  * 
30997  */
30998
30999 /**
31000  * @class Roo.bootstrap.MasonryBrick
31001  * @extends Roo.bootstrap.Component
31002  * Bootstrap MasonryBrick class
31003  * 
31004  * @constructor
31005  * Create a new MasonryBrick
31006  * @param {Object} config The config object
31007  */
31008
31009 Roo.bootstrap.MasonryBrick = function(config){
31010     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31011     
31012     this.addEvents({
31013         // raw events
31014         /**
31015          * @event click
31016          * When a MasonryBrick is clcik
31017          * @param {Roo.bootstrap.MasonryBrick} this
31018          * @param {Roo.EventObject} e
31019          */
31020         "click" : true
31021     });
31022 };
31023
31024 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31025     
31026     /**
31027      * @cfg {String} title
31028      */   
31029     title : '',
31030     /**
31031      * @cfg {String} html
31032      */   
31033     html : '',
31034     /**
31035      * @cfg {String} bgimage
31036      */   
31037     bgimage : '',
31038     /**
31039      * @cfg {String} videourl
31040      */   
31041     videourl : '',
31042     /**
31043      * @cfg {String} cls
31044      */   
31045     cls : '',
31046     /**
31047      * @cfg {String} href
31048      */   
31049     href : '',
31050     /**
31051      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31052      */   
31053     size : 'xs',
31054     
31055     /**
31056      * @cfg {String} (center|bottom) placetitle
31057      */   
31058     placetitle : '',
31059     
31060     /**
31061      * @cfg {Boolean} isFitContainer defalut true
31062      */   
31063     isFitContainer : true, 
31064     
31065     /**
31066      * @cfg {Boolean} preventDefault defalut false
31067      */   
31068     preventDefault : false, 
31069     
31070     getAutoCreate : function()
31071     {
31072         if(!this.isFitContainer){
31073             return this.getSplitAutoCreate();
31074         }
31075         
31076         var cls = 'masonry-brick masonry-brick-full';
31077         
31078         if(this.href.length){
31079             cls += ' masonry-brick-link';
31080         }
31081         
31082         if(this.bgimage.length){
31083             cls += ' masonry-brick-image';
31084         }
31085         
31086         if(!this.html.length){
31087             cls += ' enable-mask';
31088         }
31089         
31090         if(this.size){
31091             cls += ' masonry-' + this.size + '-brick';
31092         }
31093         
31094         if(this.placetitle.length){
31095             
31096             switch (this.placetitle) {
31097                 case 'center' :
31098                     cls += ' masonry-center-title';
31099                     break;
31100                 case 'bottom' :
31101                     cls += ' masonry-bottom-title';
31102                     break;
31103                 default:
31104                     break;
31105             }
31106             
31107         } else {
31108             if(!this.html.length && !this.bgimage.length){
31109                 cls += ' masonry-center-title';
31110             }
31111
31112             if(!this.html.length && this.bgimage.length){
31113                 cls += ' masonry-bottom-title';
31114             }
31115         }
31116         
31117         if(this.cls){
31118             cls += ' ' + this.cls;
31119         }
31120         
31121         var cfg = {
31122             tag: (this.href.length) ? 'a' : 'div',
31123             cls: cls,
31124             cn: [
31125                 {
31126                     tag: 'div',
31127                     cls: 'masonry-brick-paragraph',
31128                     cn: []
31129                 }
31130             ]
31131         };
31132         
31133         if(this.href.length){
31134             cfg.href = this.href;
31135         }
31136         
31137         var cn = cfg.cn[0].cn;
31138         
31139         if(this.title.length){
31140             cn.push({
31141                 tag: 'h4',
31142                 cls: 'masonry-brick-title',
31143                 html: this.title
31144             });
31145         }
31146         
31147         if(this.html.length){
31148             cn.push({
31149                 tag: 'p',
31150                 cls: 'masonry-brick-text',
31151                 html: this.html
31152             });
31153         }  
31154         if (!this.title.length && !this.html.length) {
31155             cfg.cn[0].cls += ' hide';
31156         }
31157         
31158         if(this.bgimage.length){
31159             cfg.cn.push({
31160                 tag: 'img',
31161                 cls: 'masonry-brick-image-view',
31162                 src: this.bgimage
31163             });
31164         }
31165         
31166         if(this.videourl.length){
31167             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31168             // youtube support only?
31169             cfg.cn.push({
31170                 tag: 'iframe',
31171                 cls: 'masonry-brick-image-view',
31172                 src: vurl,
31173                 frameborder : 0,
31174                 allowfullscreen : true
31175             });
31176             
31177             
31178         }
31179         
31180         cfg.cn.push({
31181             tag: 'div',
31182             cls: 'masonry-brick-mask'
31183         });
31184         
31185         return cfg;
31186         
31187     },
31188     
31189     getSplitAutoCreate : function()
31190     {
31191         var cls = 'masonry-brick masonry-brick-split';
31192         
31193         if(this.href.length){
31194             cls += ' masonry-brick-link';
31195         }
31196         
31197         if(this.bgimage.length){
31198             cls += ' masonry-brick-image';
31199         }
31200         
31201         if(this.size){
31202             cls += ' masonry-' + this.size + '-brick';
31203         }
31204         
31205         switch (this.placetitle) {
31206             case 'center' :
31207                 cls += ' masonry-center-title';
31208                 break;
31209             case 'bottom' :
31210                 cls += ' masonry-bottom-title';
31211                 break;
31212             default:
31213                 if(!this.bgimage.length){
31214                     cls += ' masonry-center-title';
31215                 }
31216
31217                 if(this.bgimage.length){
31218                     cls += ' masonry-bottom-title';
31219                 }
31220                 break;
31221         }
31222         
31223         if(this.cls){
31224             cls += ' ' + this.cls;
31225         }
31226         
31227         var cfg = {
31228             tag: (this.href.length) ? 'a' : 'div',
31229             cls: cls,
31230             cn: [
31231                 {
31232                     tag: 'div',
31233                     cls: 'masonry-brick-split-head',
31234                     cn: [
31235                         {
31236                             tag: 'div',
31237                             cls: 'masonry-brick-paragraph',
31238                             cn: []
31239                         }
31240                     ]
31241                 },
31242                 {
31243                     tag: 'div',
31244                     cls: 'masonry-brick-split-body',
31245                     cn: []
31246                 }
31247             ]
31248         };
31249         
31250         if(this.href.length){
31251             cfg.href = this.href;
31252         }
31253         
31254         if(this.title.length){
31255             cfg.cn[0].cn[0].cn.push({
31256                 tag: 'h4',
31257                 cls: 'masonry-brick-title',
31258                 html: this.title
31259             });
31260         }
31261         
31262         if(this.html.length){
31263             cfg.cn[1].cn.push({
31264                 tag: 'p',
31265                 cls: 'masonry-brick-text',
31266                 html: this.html
31267             });
31268         }
31269
31270         if(this.bgimage.length){
31271             cfg.cn[0].cn.push({
31272                 tag: 'img',
31273                 cls: 'masonry-brick-image-view',
31274                 src: this.bgimage
31275             });
31276         }
31277         
31278         if(this.videourl.length){
31279             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31280             // youtube support only?
31281             cfg.cn[0].cn.cn.push({
31282                 tag: 'iframe',
31283                 cls: 'masonry-brick-image-view',
31284                 src: vurl,
31285                 frameborder : 0,
31286                 allowfullscreen : true
31287             });
31288         }
31289         
31290         return cfg;
31291     },
31292     
31293     initEvents: function() 
31294     {
31295         switch (this.size) {
31296             case 'xs' :
31297                 this.x = 1;
31298                 this.y = 1;
31299                 break;
31300             case 'sm' :
31301                 this.x = 2;
31302                 this.y = 2;
31303                 break;
31304             case 'md' :
31305             case 'md-left' :
31306             case 'md-right' :
31307                 this.x = 3;
31308                 this.y = 3;
31309                 break;
31310             case 'tall' :
31311                 this.x = 2;
31312                 this.y = 3;
31313                 break;
31314             case 'wide' :
31315                 this.x = 3;
31316                 this.y = 2;
31317                 break;
31318             case 'wide-thin' :
31319                 this.x = 3;
31320                 this.y = 1;
31321                 break;
31322                         
31323             default :
31324                 break;
31325         }
31326         
31327         if(Roo.isTouch){
31328             this.el.on('touchstart', this.onTouchStart, this);
31329             this.el.on('touchmove', this.onTouchMove, this);
31330             this.el.on('touchend', this.onTouchEnd, this);
31331             this.el.on('contextmenu', this.onContextMenu, this);
31332         } else {
31333             this.el.on('mouseenter'  ,this.enter, this);
31334             this.el.on('mouseleave', this.leave, this);
31335             this.el.on('click', this.onClick, this);
31336         }
31337         
31338         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31339             this.parent().bricks.push(this);   
31340         }
31341         
31342     },
31343     
31344     onClick: function(e, el)
31345     {
31346         var time = this.endTimer - this.startTimer;
31347         
31348         if(Roo.isTouch){
31349             if(time > 1000){
31350                 e.preventDefault();
31351                 return;
31352             }
31353         }
31354         
31355         if(!this.preventDefault){
31356             return;
31357         }
31358         
31359         e.preventDefault();
31360         this.fireEvent('click', this);
31361     },
31362     
31363     enter: function(e, el)
31364     {
31365         e.preventDefault();
31366         
31367         if(!this.isFitContainer){
31368             return;
31369         }
31370         
31371         if(this.bgimage.length && this.html.length){
31372             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31373         }
31374     },
31375     
31376     leave: function(e, el)
31377     {
31378         e.preventDefault();
31379         
31380         if(!this.isFitContainer){
31381             return;
31382         }
31383         
31384         if(this.bgimage.length && this.html.length){
31385             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31386         }
31387     },
31388     
31389     onTouchStart: function(e, el)
31390     {
31391 //        e.preventDefault();
31392         
31393         this.touchmoved = false;
31394         
31395         if(!this.isFitContainer){
31396             return;
31397         }
31398         
31399         if(!this.bgimage.length || !this.html.length){
31400             return;
31401         }
31402         
31403         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31404         
31405         this.timer = new Date().getTime();
31406         
31407     },
31408     
31409     onTouchMove: function(e, el)
31410     {
31411         this.touchmoved = true;
31412     },
31413     
31414     onContextMenu : function(e,el)
31415     {
31416         e.preventDefault();
31417         e.stopPropagation();
31418         return false;
31419     },
31420     
31421     onTouchEnd: function(e, el)
31422     {
31423 //        e.preventDefault();
31424         
31425         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31426         
31427             this.leave(e,el);
31428             
31429             return;
31430         }
31431         
31432         if(!this.bgimage.length || !this.html.length){
31433             
31434             if(this.href.length){
31435                 window.location.href = this.href;
31436             }
31437             
31438             return;
31439         }
31440         
31441         if(!this.isFitContainer){
31442             return;
31443         }
31444         
31445         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31446         
31447         window.location.href = this.href;
31448     }
31449     
31450 });
31451
31452  
31453
31454  /*
31455  * - LGPL
31456  *
31457  * element
31458  * 
31459  */
31460
31461 /**
31462  * @class Roo.bootstrap.Brick
31463  * @extends Roo.bootstrap.Component
31464  * Bootstrap Brick class
31465  * 
31466  * @constructor
31467  * Create a new Brick
31468  * @param {Object} config The config object
31469  */
31470
31471 Roo.bootstrap.Brick = function(config){
31472     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31473     
31474     this.addEvents({
31475         // raw events
31476         /**
31477          * @event click
31478          * When a Brick is click
31479          * @param {Roo.bootstrap.Brick} this
31480          * @param {Roo.EventObject} e
31481          */
31482         "click" : true
31483     });
31484 };
31485
31486 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31487     
31488     /**
31489      * @cfg {String} title
31490      */   
31491     title : '',
31492     /**
31493      * @cfg {String} html
31494      */   
31495     html : '',
31496     /**
31497      * @cfg {String} bgimage
31498      */   
31499     bgimage : '',
31500     /**
31501      * @cfg {String} cls
31502      */   
31503     cls : '',
31504     /**
31505      * @cfg {String} href
31506      */   
31507     href : '',
31508     /**
31509      * @cfg {String} video
31510      */   
31511     video : '',
31512     /**
31513      * @cfg {Boolean} square
31514      */   
31515     square : true,
31516     
31517     getAutoCreate : function()
31518     {
31519         var cls = 'roo-brick';
31520         
31521         if(this.href.length){
31522             cls += ' roo-brick-link';
31523         }
31524         
31525         if(this.bgimage.length){
31526             cls += ' roo-brick-image';
31527         }
31528         
31529         if(!this.html.length && !this.bgimage.length){
31530             cls += ' roo-brick-center-title';
31531         }
31532         
31533         if(!this.html.length && this.bgimage.length){
31534             cls += ' roo-brick-bottom-title';
31535         }
31536         
31537         if(this.cls){
31538             cls += ' ' + this.cls;
31539         }
31540         
31541         var cfg = {
31542             tag: (this.href.length) ? 'a' : 'div',
31543             cls: cls,
31544             cn: [
31545                 {
31546                     tag: 'div',
31547                     cls: 'roo-brick-paragraph',
31548                     cn: []
31549                 }
31550             ]
31551         };
31552         
31553         if(this.href.length){
31554             cfg.href = this.href;
31555         }
31556         
31557         var cn = cfg.cn[0].cn;
31558         
31559         if(this.title.length){
31560             cn.push({
31561                 tag: 'h4',
31562                 cls: 'roo-brick-title',
31563                 html: this.title
31564             });
31565         }
31566         
31567         if(this.html.length){
31568             cn.push({
31569                 tag: 'p',
31570                 cls: 'roo-brick-text',
31571                 html: this.html
31572             });
31573         } else {
31574             cn.cls += ' hide';
31575         }
31576         
31577         if(this.bgimage.length){
31578             cfg.cn.push({
31579                 tag: 'img',
31580                 cls: 'roo-brick-image-view',
31581                 src: this.bgimage
31582             });
31583         }
31584         
31585         return cfg;
31586     },
31587     
31588     initEvents: function() 
31589     {
31590         if(this.title.length || this.html.length){
31591             this.el.on('mouseenter'  ,this.enter, this);
31592             this.el.on('mouseleave', this.leave, this);
31593         }
31594         
31595         
31596         Roo.EventManager.onWindowResize(this.resize, this); 
31597         
31598         this.resize();
31599     },
31600     
31601     resize : function()
31602     {
31603         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31604         
31605         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31606         
31607         if(this.bgimage.length){
31608             var image = this.el.select('.roo-brick-image-view', true).first();
31609             image.setWidth(paragraph.getWidth());
31610             image.setHeight(paragraph.getWidth());
31611             
31612             this.el.setHeight(paragraph.getWidth());
31613             
31614         }
31615         
31616     },
31617     
31618     enter: function(e, el)
31619     {
31620         e.preventDefault();
31621         
31622         if(this.bgimage.length){
31623             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31624             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31625         }
31626     },
31627     
31628     leave: function(e, el)
31629     {
31630         e.preventDefault();
31631         
31632         if(this.bgimage.length){
31633             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31634             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31635         }
31636     }
31637     
31638 });
31639
31640  
31641
31642  /*
31643  * - LGPL
31644  *
31645  * Input
31646  * 
31647  */
31648
31649 /**
31650  * @class Roo.bootstrap.NumberField
31651  * @extends Roo.bootstrap.Input
31652  * Bootstrap NumberField class
31653  * 
31654  * 
31655  * 
31656  * 
31657  * @constructor
31658  * Create a new NumberField
31659  * @param {Object} config The config object
31660  */
31661
31662 Roo.bootstrap.NumberField = function(config){
31663     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31664 };
31665
31666 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31667     
31668     /**
31669      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31670      */
31671     allowDecimals : true,
31672     /**
31673      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31674      */
31675     decimalSeparator : ".",
31676     /**
31677      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31678      */
31679     decimalPrecision : 2,
31680     /**
31681      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31682      */
31683     allowNegative : true,
31684     /**
31685      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31686      */
31687     minValue : Number.NEGATIVE_INFINITY,
31688     /**
31689      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31690      */
31691     maxValue : Number.MAX_VALUE,
31692     /**
31693      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31694      */
31695     minText : "The minimum value for this field is {0}",
31696     /**
31697      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31698      */
31699     maxText : "The maximum value for this field is {0}",
31700     /**
31701      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
31702      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31703      */
31704     nanText : "{0} is not a valid number",
31705     /**
31706      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31707      */
31708     castInt : true,
31709
31710     // private
31711     initEvents : function()
31712     {   
31713         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31714         
31715         var allowed = "0123456789";
31716         
31717         if(this.allowDecimals){
31718             allowed += this.decimalSeparator;
31719         }
31720         
31721         if(this.allowNegative){
31722             allowed += "-";
31723         }
31724         
31725         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31726         
31727         var keyPress = function(e){
31728             
31729             var k = e.getKey();
31730             
31731             var c = e.getCharCode();
31732             
31733             if(
31734                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31735                     allowed.indexOf(String.fromCharCode(c)) === -1
31736             ){
31737                 e.stopEvent();
31738                 return;
31739             }
31740             
31741             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31742                 return;
31743             }
31744             
31745             if(allowed.indexOf(String.fromCharCode(c)) === -1){
31746                 e.stopEvent();
31747             }
31748         };
31749         
31750         this.el.on("keypress", keyPress, this);
31751     },
31752     
31753     validateValue : function(value)
31754     {
31755         
31756         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31757             return false;
31758         }
31759         
31760         var num = this.parseValue(value);
31761         
31762         if(isNaN(num)){
31763             this.markInvalid(String.format(this.nanText, value));
31764             return false;
31765         }
31766         
31767         if(num < this.minValue){
31768             this.markInvalid(String.format(this.minText, this.minValue));
31769             return false;
31770         }
31771         
31772         if(num > this.maxValue){
31773             this.markInvalid(String.format(this.maxText, this.maxValue));
31774             return false;
31775         }
31776         
31777         return true;
31778     },
31779
31780     getValue : function()
31781     {
31782         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31783     },
31784
31785     parseValue : function(value)
31786     {
31787         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31788         return isNaN(value) ? '' : value;
31789     },
31790
31791     fixPrecision : function(value)
31792     {
31793         var nan = isNaN(value);
31794         
31795         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31796             return nan ? '' : value;
31797         }
31798         return parseFloat(value).toFixed(this.decimalPrecision);
31799     },
31800
31801     setValue : function(v)
31802     {
31803         v = this.fixPrecision(v);
31804         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31805     },
31806
31807     decimalPrecisionFcn : function(v)
31808     {
31809         return Math.floor(v);
31810     },
31811
31812     beforeBlur : function()
31813     {
31814         if(!this.castInt){
31815             return;
31816         }
31817         
31818         var v = this.parseValue(this.getRawValue());
31819         if(v){
31820             this.setValue(v);
31821         }
31822     }
31823     
31824 });
31825
31826  
31827
31828 /*
31829 * Licence: LGPL
31830 */
31831
31832 /**
31833  * @class Roo.bootstrap.DocumentSlider
31834  * @extends Roo.bootstrap.Component
31835  * Bootstrap DocumentSlider class
31836  * 
31837  * @constructor
31838  * Create a new DocumentViewer
31839  * @param {Object} config The config object
31840  */
31841
31842 Roo.bootstrap.DocumentSlider = function(config){
31843     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31844     
31845     this.files = [];
31846     
31847     this.addEvents({
31848         /**
31849          * @event initial
31850          * Fire after initEvent
31851          * @param {Roo.bootstrap.DocumentSlider} this
31852          */
31853         "initial" : true,
31854         /**
31855          * @event update
31856          * Fire after update
31857          * @param {Roo.bootstrap.DocumentSlider} this
31858          */
31859         "update" : true,
31860         /**
31861          * @event click
31862          * Fire after click
31863          * @param {Roo.bootstrap.DocumentSlider} this
31864          */
31865         "click" : true
31866     });
31867 };
31868
31869 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
31870     
31871     files : false,
31872     
31873     indicator : 0,
31874     
31875     getAutoCreate : function()
31876     {
31877         var cfg = {
31878             tag : 'div',
31879             cls : 'roo-document-slider',
31880             cn : [
31881                 {
31882                     tag : 'div',
31883                     cls : 'roo-document-slider-header',
31884                     cn : [
31885                         {
31886                             tag : 'div',
31887                             cls : 'roo-document-slider-header-title'
31888                         }
31889                     ]
31890                 },
31891                 {
31892                     tag : 'div',
31893                     cls : 'roo-document-slider-body',
31894                     cn : [
31895                         {
31896                             tag : 'div',
31897                             cls : 'roo-document-slider-prev',
31898                             cn : [
31899                                 {
31900                                     tag : 'i',
31901                                     cls : 'fa fa-chevron-left'
31902                                 }
31903                             ]
31904                         },
31905                         {
31906                             tag : 'div',
31907                             cls : 'roo-document-slider-thumb',
31908                             cn : [
31909                                 {
31910                                     tag : 'img',
31911                                     cls : 'roo-document-slider-image'
31912                                 }
31913                             ]
31914                         },
31915                         {
31916                             tag : 'div',
31917                             cls : 'roo-document-slider-next',
31918                             cn : [
31919                                 {
31920                                     tag : 'i',
31921                                     cls : 'fa fa-chevron-right'
31922                                 }
31923                             ]
31924                         }
31925                     ]
31926                 }
31927             ]
31928         };
31929         
31930         return cfg;
31931     },
31932     
31933     initEvents : function()
31934     {
31935         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31936         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31937         
31938         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31939         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31940         
31941         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31942         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31943         
31944         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31945         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31946         
31947         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31948         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31949         
31950         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31951         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31952         
31953         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31954         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31955         
31956         this.thumbEl.on('click', this.onClick, this);
31957         
31958         this.prevIndicator.on('click', this.prev, this);
31959         
31960         this.nextIndicator.on('click', this.next, this);
31961         
31962     },
31963     
31964     initial : function()
31965     {
31966         if(this.files.length){
31967             this.indicator = 1;
31968             this.update()
31969         }
31970         
31971         this.fireEvent('initial', this);
31972     },
31973     
31974     update : function()
31975     {
31976         this.imageEl.attr('src', this.files[this.indicator - 1]);
31977         
31978         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
31979         
31980         this.prevIndicator.show();
31981         
31982         if(this.indicator == 1){
31983             this.prevIndicator.hide();
31984         }
31985         
31986         this.nextIndicator.show();
31987         
31988         if(this.indicator == this.files.length){
31989             this.nextIndicator.hide();
31990         }
31991         
31992         this.thumbEl.scrollTo('top');
31993         
31994         this.fireEvent('update', this);
31995     },
31996     
31997     onClick : function(e)
31998     {
31999         e.preventDefault();
32000         
32001         this.fireEvent('click', this);
32002     },
32003     
32004     prev : function(e)
32005     {
32006         e.preventDefault();
32007         
32008         this.indicator = Math.max(1, this.indicator - 1);
32009         
32010         this.update();
32011     },
32012     
32013     next : function(e)
32014     {
32015         e.preventDefault();
32016         
32017         this.indicator = Math.min(this.files.length, this.indicator + 1);
32018         
32019         this.update();
32020     }
32021 });
32022 /*
32023  * Based on:
32024  * Ext JS Library 1.1.1
32025  * Copyright(c) 2006-2007, Ext JS, LLC.
32026  *
32027  * Originally Released Under LGPL - original licence link has changed is not relivant.
32028  *
32029  * Fork - LGPL
32030  * <script type="text/javascript">
32031  */
32032
32033
32034 /**
32035  * @class Roo.bootstrap.SplitBar
32036  * @extends Roo.util.Observable
32037  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32038  * <br><br>
32039  * Usage:
32040  * <pre><code>
32041 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32042                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32043 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32044 split.minSize = 100;
32045 split.maxSize = 600;
32046 split.animate = true;
32047 split.on('moved', splitterMoved);
32048 </code></pre>
32049  * @constructor
32050  * Create a new SplitBar
32051  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32052  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32053  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32054  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32055                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32056                         position of the SplitBar).
32057  */
32058 Roo.bootstrap.SplitBar = function(cfg){
32059     
32060     /** @private */
32061     
32062     //{
32063     //  dragElement : elm
32064     //  resizingElement: el,
32065         // optional..
32066     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32067     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32068         // existingProxy ???
32069     //}
32070     
32071     this.el = Roo.get(cfg.dragElement, true);
32072     this.el.dom.unselectable = "on";
32073     /** @private */
32074     this.resizingEl = Roo.get(cfg.resizingElement, true);
32075
32076     /**
32077      * @private
32078      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32079      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32080      * @type Number
32081      */
32082     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32083     
32084     /**
32085      * The minimum size of the resizing element. (Defaults to 0)
32086      * @type Number
32087      */
32088     this.minSize = 0;
32089     
32090     /**
32091      * The maximum size of the resizing element. (Defaults to 2000)
32092      * @type Number
32093      */
32094     this.maxSize = 2000;
32095     
32096     /**
32097      * Whether to animate the transition to the new size
32098      * @type Boolean
32099      */
32100     this.animate = false;
32101     
32102     /**
32103      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32104      * @type Boolean
32105      */
32106     this.useShim = false;
32107     
32108     /** @private */
32109     this.shim = null;
32110     
32111     if(!cfg.existingProxy){
32112         /** @private */
32113         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32114     }else{
32115         this.proxy = Roo.get(cfg.existingProxy).dom;
32116     }
32117     /** @private */
32118     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32119     
32120     /** @private */
32121     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32122     
32123     /** @private */
32124     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32125     
32126     /** @private */
32127     this.dragSpecs = {};
32128     
32129     /**
32130      * @private The adapter to use to positon and resize elements
32131      */
32132     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32133     this.adapter.init(this);
32134     
32135     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32136         /** @private */
32137         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32138         this.el.addClass("roo-splitbar-h");
32139     }else{
32140         /** @private */
32141         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32142         this.el.addClass("roo-splitbar-v");
32143     }
32144     
32145     this.addEvents({
32146         /**
32147          * @event resize
32148          * Fires when the splitter is moved (alias for {@link #event-moved})
32149          * @param {Roo.bootstrap.SplitBar} this
32150          * @param {Number} newSize the new width or height
32151          */
32152         "resize" : true,
32153         /**
32154          * @event moved
32155          * Fires when the splitter is moved
32156          * @param {Roo.bootstrap.SplitBar} this
32157          * @param {Number} newSize the new width or height
32158          */
32159         "moved" : true,
32160         /**
32161          * @event beforeresize
32162          * Fires before the splitter is dragged
32163          * @param {Roo.bootstrap.SplitBar} this
32164          */
32165         "beforeresize" : true,
32166
32167         "beforeapply" : true
32168     });
32169
32170     Roo.util.Observable.call(this);
32171 };
32172
32173 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32174     onStartProxyDrag : function(x, y){
32175         this.fireEvent("beforeresize", this);
32176         if(!this.overlay){
32177             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32178             o.unselectable();
32179             o.enableDisplayMode("block");
32180             // all splitbars share the same overlay
32181             Roo.bootstrap.SplitBar.prototype.overlay = o;
32182         }
32183         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32184         this.overlay.show();
32185         Roo.get(this.proxy).setDisplayed("block");
32186         var size = this.adapter.getElementSize(this);
32187         this.activeMinSize = this.getMinimumSize();;
32188         this.activeMaxSize = this.getMaximumSize();;
32189         var c1 = size - this.activeMinSize;
32190         var c2 = Math.max(this.activeMaxSize - size, 0);
32191         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32192             this.dd.resetConstraints();
32193             this.dd.setXConstraint(
32194                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32195                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32196             );
32197             this.dd.setYConstraint(0, 0);
32198         }else{
32199             this.dd.resetConstraints();
32200             this.dd.setXConstraint(0, 0);
32201             this.dd.setYConstraint(
32202                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32203                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32204             );
32205          }
32206         this.dragSpecs.startSize = size;
32207         this.dragSpecs.startPoint = [x, y];
32208         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32209     },
32210     
32211     /** 
32212      * @private Called after the drag operation by the DDProxy
32213      */
32214     onEndProxyDrag : function(e){
32215         Roo.get(this.proxy).setDisplayed(false);
32216         var endPoint = Roo.lib.Event.getXY(e);
32217         if(this.overlay){
32218             this.overlay.hide();
32219         }
32220         var newSize;
32221         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32222             newSize = this.dragSpecs.startSize + 
32223                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32224                     endPoint[0] - this.dragSpecs.startPoint[0] :
32225                     this.dragSpecs.startPoint[0] - endPoint[0]
32226                 );
32227         }else{
32228             newSize = this.dragSpecs.startSize + 
32229                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32230                     endPoint[1] - this.dragSpecs.startPoint[1] :
32231                     this.dragSpecs.startPoint[1] - endPoint[1]
32232                 );
32233         }
32234         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32235         if(newSize != this.dragSpecs.startSize){
32236             if(this.fireEvent('beforeapply', this, newSize) !== false){
32237                 this.adapter.setElementSize(this, newSize);
32238                 this.fireEvent("moved", this, newSize);
32239                 this.fireEvent("resize", this, newSize);
32240             }
32241         }
32242     },
32243     
32244     /**
32245      * Get the adapter this SplitBar uses
32246      * @return The adapter object
32247      */
32248     getAdapter : function(){
32249         return this.adapter;
32250     },
32251     
32252     /**
32253      * Set the adapter this SplitBar uses
32254      * @param {Object} adapter A SplitBar adapter object
32255      */
32256     setAdapter : function(adapter){
32257         this.adapter = adapter;
32258         this.adapter.init(this);
32259     },
32260     
32261     /**
32262      * Gets the minimum size for the resizing element
32263      * @return {Number} The minimum size
32264      */
32265     getMinimumSize : function(){
32266         return this.minSize;
32267     },
32268     
32269     /**
32270      * Sets the minimum size for the resizing element
32271      * @param {Number} minSize The minimum size
32272      */
32273     setMinimumSize : function(minSize){
32274         this.minSize = minSize;
32275     },
32276     
32277     /**
32278      * Gets the maximum size for the resizing element
32279      * @return {Number} The maximum size
32280      */
32281     getMaximumSize : function(){
32282         return this.maxSize;
32283     },
32284     
32285     /**
32286      * Sets the maximum size for the resizing element
32287      * @param {Number} maxSize The maximum size
32288      */
32289     setMaximumSize : function(maxSize){
32290         this.maxSize = maxSize;
32291     },
32292     
32293     /**
32294      * Sets the initialize size for the resizing element
32295      * @param {Number} size The initial size
32296      */
32297     setCurrentSize : function(size){
32298         var oldAnimate = this.animate;
32299         this.animate = false;
32300         this.adapter.setElementSize(this, size);
32301         this.animate = oldAnimate;
32302     },
32303     
32304     /**
32305      * Destroy this splitbar. 
32306      * @param {Boolean} removeEl True to remove the element
32307      */
32308     destroy : function(removeEl){
32309         if(this.shim){
32310             this.shim.remove();
32311         }
32312         this.dd.unreg();
32313         this.proxy.parentNode.removeChild(this.proxy);
32314         if(removeEl){
32315             this.el.remove();
32316         }
32317     }
32318 });
32319
32320 /**
32321  * @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.
32322  */
32323 Roo.bootstrap.SplitBar.createProxy = function(dir){
32324     var proxy = new Roo.Element(document.createElement("div"));
32325     proxy.unselectable();
32326     var cls = 'roo-splitbar-proxy';
32327     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32328     document.body.appendChild(proxy.dom);
32329     return proxy.dom;
32330 };
32331
32332 /** 
32333  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32334  * Default Adapter. It assumes the splitter and resizing element are not positioned
32335  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32336  */
32337 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32338 };
32339
32340 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32341     // do nothing for now
32342     init : function(s){
32343     
32344     },
32345     /**
32346      * Called before drag operations to get the current size of the resizing element. 
32347      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32348      */
32349      getElementSize : function(s){
32350         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32351             return s.resizingEl.getWidth();
32352         }else{
32353             return s.resizingEl.getHeight();
32354         }
32355     },
32356     
32357     /**
32358      * Called after drag operations to set the size of the resizing element.
32359      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32360      * @param {Number} newSize The new size to set
32361      * @param {Function} onComplete A function to be invoked when resizing is complete
32362      */
32363     setElementSize : function(s, newSize, onComplete){
32364         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32365             if(!s.animate){
32366                 s.resizingEl.setWidth(newSize);
32367                 if(onComplete){
32368                     onComplete(s, newSize);
32369                 }
32370             }else{
32371                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32372             }
32373         }else{
32374             
32375             if(!s.animate){
32376                 s.resizingEl.setHeight(newSize);
32377                 if(onComplete){
32378                     onComplete(s, newSize);
32379                 }
32380             }else{
32381                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32382             }
32383         }
32384     }
32385 };
32386
32387 /** 
32388  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32389  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32390  * Adapter that  moves the splitter element to align with the resized sizing element. 
32391  * Used with an absolute positioned SplitBar.
32392  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32393  * document.body, make sure you assign an id to the body element.
32394  */
32395 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32396     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32397     this.container = Roo.get(container);
32398 };
32399
32400 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32401     init : function(s){
32402         this.basic.init(s);
32403     },
32404     
32405     getElementSize : function(s){
32406         return this.basic.getElementSize(s);
32407     },
32408     
32409     setElementSize : function(s, newSize, onComplete){
32410         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32411     },
32412     
32413     moveSplitter : function(s){
32414         var yes = Roo.bootstrap.SplitBar;
32415         switch(s.placement){
32416             case yes.LEFT:
32417                 s.el.setX(s.resizingEl.getRight());
32418                 break;
32419             case yes.RIGHT:
32420                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32421                 break;
32422             case yes.TOP:
32423                 s.el.setY(s.resizingEl.getBottom());
32424                 break;
32425             case yes.BOTTOM:
32426                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32427                 break;
32428         }
32429     }
32430 };
32431
32432 /**
32433  * Orientation constant - Create a vertical SplitBar
32434  * @static
32435  * @type Number
32436  */
32437 Roo.bootstrap.SplitBar.VERTICAL = 1;
32438
32439 /**
32440  * Orientation constant - Create a horizontal SplitBar
32441  * @static
32442  * @type Number
32443  */
32444 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32445
32446 /**
32447  * Placement constant - The resizing element is to the left of the splitter element
32448  * @static
32449  * @type Number
32450  */
32451 Roo.bootstrap.SplitBar.LEFT = 1;
32452
32453 /**
32454  * Placement constant - The resizing element is to the right of the splitter element
32455  * @static
32456  * @type Number
32457  */
32458 Roo.bootstrap.SplitBar.RIGHT = 2;
32459
32460 /**
32461  * Placement constant - The resizing element is positioned above the splitter element
32462  * @static
32463  * @type Number
32464  */
32465 Roo.bootstrap.SplitBar.TOP = 3;
32466
32467 /**
32468  * Placement constant - The resizing element is positioned under splitter element
32469  * @static
32470  * @type Number
32471  */
32472 Roo.bootstrap.SplitBar.BOTTOM = 4;
32473 Roo.namespace("Roo.bootstrap.layout");/*
32474  * Based on:
32475  * Ext JS Library 1.1.1
32476  * Copyright(c) 2006-2007, Ext JS, LLC.
32477  *
32478  * Originally Released Under LGPL - original licence link has changed is not relivant.
32479  *
32480  * Fork - LGPL
32481  * <script type="text/javascript">
32482  */
32483
32484 /**
32485  * @class Roo.bootstrap.layout.Manager
32486  * @extends Roo.bootstrap.Component
32487  * Base class for layout managers.
32488  */
32489 Roo.bootstrap.layout.Manager = function(config)
32490 {
32491     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32492
32493
32494
32495
32496
32497     /** false to disable window resize monitoring @type Boolean */
32498     this.monitorWindowResize = true;
32499     this.regions = {};
32500     this.addEvents({
32501         /**
32502          * @event layout
32503          * Fires when a layout is performed.
32504          * @param {Roo.LayoutManager} this
32505          */
32506         "layout" : true,
32507         /**
32508          * @event regionresized
32509          * Fires when the user resizes a region.
32510          * @param {Roo.LayoutRegion} region The resized region
32511          * @param {Number} newSize The new size (width for east/west, height for north/south)
32512          */
32513         "regionresized" : true,
32514         /**
32515          * @event regioncollapsed
32516          * Fires when a region is collapsed.
32517          * @param {Roo.LayoutRegion} region The collapsed region
32518          */
32519         "regioncollapsed" : true,
32520         /**
32521          * @event regionexpanded
32522          * Fires when a region is expanded.
32523          * @param {Roo.LayoutRegion} region The expanded region
32524          */
32525         "regionexpanded" : true
32526     });
32527     this.updating = false;
32528
32529     if (config.el) {
32530         this.el = Roo.get(config.el);
32531         this.initEvents();
32532     }
32533
32534 };
32535
32536 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32537
32538
32539     regions : null,
32540
32541     monitorWindowResize : true,
32542
32543
32544     updating : false,
32545
32546
32547     onRender : function(ct, position)
32548     {
32549         if(!this.el){
32550             this.el = Roo.get(ct);
32551             this.initEvents();
32552         }
32553         //this.fireEvent('render',this);
32554     },
32555
32556
32557     initEvents: function()
32558     {
32559
32560
32561         // ie scrollbar fix
32562         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32563             document.body.scroll = "no";
32564         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32565             this.el.position('relative');
32566         }
32567         this.id = this.el.id;
32568         this.el.addClass("roo-layout-container");
32569         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32570         if(this.el.dom != document.body ) {
32571             this.el.on('resize', this.layout,this);
32572             this.el.on('show', this.layout,this);
32573         }
32574
32575     },
32576
32577     /**
32578      * Returns true if this layout is currently being updated
32579      * @return {Boolean}
32580      */
32581     isUpdating : function(){
32582         return this.updating;
32583     },
32584
32585     /**
32586      * Suspend the LayoutManager from doing auto-layouts while
32587      * making multiple add or remove calls
32588      */
32589     beginUpdate : function(){
32590         this.updating = true;
32591     },
32592
32593     /**
32594      * Restore auto-layouts and optionally disable the manager from performing a layout
32595      * @param {Boolean} noLayout true to disable a layout update
32596      */
32597     endUpdate : function(noLayout){
32598         this.updating = false;
32599         if(!noLayout){
32600             this.layout();
32601         }
32602     },
32603
32604     layout: function(){
32605         // abstract...
32606     },
32607
32608     onRegionResized : function(region, newSize){
32609         this.fireEvent("regionresized", region, newSize);
32610         this.layout();
32611     },
32612
32613     onRegionCollapsed : function(region){
32614         this.fireEvent("regioncollapsed", region);
32615     },
32616
32617     onRegionExpanded : function(region){
32618         this.fireEvent("regionexpanded", region);
32619     },
32620
32621     /**
32622      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32623      * performs box-model adjustments.
32624      * @return {Object} The size as an object {width: (the width), height: (the height)}
32625      */
32626     getViewSize : function()
32627     {
32628         var size;
32629         if(this.el.dom != document.body){
32630             size = this.el.getSize();
32631         }else{
32632             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32633         }
32634         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32635         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32636         return size;
32637     },
32638
32639     /**
32640      * Returns the Element this layout is bound to.
32641      * @return {Roo.Element}
32642      */
32643     getEl : function(){
32644         return this.el;
32645     },
32646
32647     /**
32648      * Returns the specified region.
32649      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32650      * @return {Roo.LayoutRegion}
32651      */
32652     getRegion : function(target){
32653         return this.regions[target.toLowerCase()];
32654     },
32655
32656     onWindowResize : function(){
32657         if(this.monitorWindowResize){
32658             this.layout();
32659         }
32660     }
32661 });
32662 /*
32663  * Based on:
32664  * Ext JS Library 1.1.1
32665  * Copyright(c) 2006-2007, Ext JS, LLC.
32666  *
32667  * Originally Released Under LGPL - original licence link has changed is not relivant.
32668  *
32669  * Fork - LGPL
32670  * <script type="text/javascript">
32671  */
32672 /**
32673  * @class Roo.bootstrap.layout.Border
32674  * @extends Roo.bootstrap.layout.Manager
32675  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32676  * please see: examples/bootstrap/nested.html<br><br>
32677  
32678 <b>The container the layout is rendered into can be either the body element or any other element.
32679 If it is not the body element, the container needs to either be an absolute positioned element,
32680 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32681 the container size if it is not the body element.</b>
32682
32683 * @constructor
32684 * Create a new Border
32685 * @param {Object} config Configuration options
32686  */
32687 Roo.bootstrap.layout.Border = function(config){
32688     config = config || {};
32689     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32690     
32691     
32692     
32693     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32694         if(config[region]){
32695             config[region].region = region;
32696             this.addRegion(config[region]);
32697         }
32698     },this);
32699     
32700 };
32701
32702 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32703
32704 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32705     /**
32706      * Creates and adds a new region if it doesn't already exist.
32707      * @param {String} target The target region key (north, south, east, west or center).
32708      * @param {Object} config The regions config object
32709      * @return {BorderLayoutRegion} The new region
32710      */
32711     addRegion : function(config)
32712     {
32713         if(!this.regions[config.region]){
32714             var r = this.factory(config);
32715             this.bindRegion(r);
32716         }
32717         return this.regions[config.region];
32718     },
32719
32720     // private (kinda)
32721     bindRegion : function(r){
32722         this.regions[r.config.region] = r;
32723         
32724         r.on("visibilitychange",    this.layout, this);
32725         r.on("paneladded",          this.layout, this);
32726         r.on("panelremoved",        this.layout, this);
32727         r.on("invalidated",         this.layout, this);
32728         r.on("resized",             this.onRegionResized, this);
32729         r.on("collapsed",           this.onRegionCollapsed, this);
32730         r.on("expanded",            this.onRegionExpanded, this);
32731     },
32732
32733     /**
32734      * Performs a layout update.
32735      */
32736     layout : function()
32737     {
32738         if(this.updating) {
32739             return;
32740         }
32741         
32742         // render all the rebions if they have not been done alreayd?
32743         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32744             if(this.regions[region] && !this.regions[region].bodyEl){
32745                 this.regions[region].onRender(this.el)
32746             }
32747         },this);
32748         
32749         var size = this.getViewSize();
32750         var w = size.width;
32751         var h = size.height;
32752         var centerW = w;
32753         var centerH = h;
32754         var centerY = 0;
32755         var centerX = 0;
32756         //var x = 0, y = 0;
32757
32758         var rs = this.regions;
32759         var north = rs["north"];
32760         var south = rs["south"]; 
32761         var west = rs["west"];
32762         var east = rs["east"];
32763         var center = rs["center"];
32764         //if(this.hideOnLayout){ // not supported anymore
32765             //c.el.setStyle("display", "none");
32766         //}
32767         if(north && north.isVisible()){
32768             var b = north.getBox();
32769             var m = north.getMargins();
32770             b.width = w - (m.left+m.right);
32771             b.x = m.left;
32772             b.y = m.top;
32773             centerY = b.height + b.y + m.bottom;
32774             centerH -= centerY;
32775             north.updateBox(this.safeBox(b));
32776         }
32777         if(south && south.isVisible()){
32778             var b = south.getBox();
32779             var m = south.getMargins();
32780             b.width = w - (m.left+m.right);
32781             b.x = m.left;
32782             var totalHeight = (b.height + m.top + m.bottom);
32783             b.y = h - totalHeight + m.top;
32784             centerH -= totalHeight;
32785             south.updateBox(this.safeBox(b));
32786         }
32787         if(west && west.isVisible()){
32788             var b = west.getBox();
32789             var m = west.getMargins();
32790             b.height = centerH - (m.top+m.bottom);
32791             b.x = m.left;
32792             b.y = centerY + m.top;
32793             var totalWidth = (b.width + m.left + m.right);
32794             centerX += totalWidth;
32795             centerW -= totalWidth;
32796             west.updateBox(this.safeBox(b));
32797         }
32798         if(east && east.isVisible()){
32799             var b = east.getBox();
32800             var m = east.getMargins();
32801             b.height = centerH - (m.top+m.bottom);
32802             var totalWidth = (b.width + m.left + m.right);
32803             b.x = w - totalWidth + m.left;
32804             b.y = centerY + m.top;
32805             centerW -= totalWidth;
32806             east.updateBox(this.safeBox(b));
32807         }
32808         if(center){
32809             var m = center.getMargins();
32810             var centerBox = {
32811                 x: centerX + m.left,
32812                 y: centerY + m.top,
32813                 width: centerW - (m.left+m.right),
32814                 height: centerH - (m.top+m.bottom)
32815             };
32816             //if(this.hideOnLayout){
32817                 //center.el.setStyle("display", "block");
32818             //}
32819             center.updateBox(this.safeBox(centerBox));
32820         }
32821         this.el.repaint();
32822         this.fireEvent("layout", this);
32823     },
32824
32825     // private
32826     safeBox : function(box){
32827         box.width = Math.max(0, box.width);
32828         box.height = Math.max(0, box.height);
32829         return box;
32830     },
32831
32832     /**
32833      * Adds a ContentPanel (or subclass) to this layout.
32834      * @param {String} target The target region key (north, south, east, west or center).
32835      * @param {Roo.ContentPanel} panel The panel to add
32836      * @return {Roo.ContentPanel} The added panel
32837      */
32838     add : function(target, panel){
32839          
32840         target = target.toLowerCase();
32841         return this.regions[target].add(panel);
32842     },
32843
32844     /**
32845      * Remove a ContentPanel (or subclass) to this layout.
32846      * @param {String} target The target region key (north, south, east, west or center).
32847      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32848      * @return {Roo.ContentPanel} The removed panel
32849      */
32850     remove : function(target, panel){
32851         target = target.toLowerCase();
32852         return this.regions[target].remove(panel);
32853     },
32854
32855     /**
32856      * Searches all regions for a panel with the specified id
32857      * @param {String} panelId
32858      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32859      */
32860     findPanel : function(panelId){
32861         var rs = this.regions;
32862         for(var target in rs){
32863             if(typeof rs[target] != "function"){
32864                 var p = rs[target].getPanel(panelId);
32865                 if(p){
32866                     return p;
32867                 }
32868             }
32869         }
32870         return null;
32871     },
32872
32873     /**
32874      * Searches all regions for a panel with the specified id and activates (shows) it.
32875      * @param {String/ContentPanel} panelId The panels id or the panel itself
32876      * @return {Roo.ContentPanel} The shown panel or null
32877      */
32878     showPanel : function(panelId) {
32879       var rs = this.regions;
32880       for(var target in rs){
32881          var r = rs[target];
32882          if(typeof r != "function"){
32883             if(r.hasPanel(panelId)){
32884                return r.showPanel(panelId);
32885             }
32886          }
32887       }
32888       return null;
32889    },
32890
32891    /**
32892      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32893      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32894      */
32895    /*
32896     restoreState : function(provider){
32897         if(!provider){
32898             provider = Roo.state.Manager;
32899         }
32900         var sm = new Roo.LayoutStateManager();
32901         sm.init(this, provider);
32902     },
32903 */
32904  
32905  
32906     /**
32907      * Adds a xtype elements to the layout.
32908      * <pre><code>
32909
32910 layout.addxtype({
32911        xtype : 'ContentPanel',
32912        region: 'west',
32913        items: [ .... ]
32914    }
32915 );
32916
32917 layout.addxtype({
32918         xtype : 'NestedLayoutPanel',
32919         region: 'west',
32920         layout: {
32921            center: { },
32922            west: { }   
32923         },
32924         items : [ ... list of content panels or nested layout panels.. ]
32925    }
32926 );
32927 </code></pre>
32928      * @param {Object} cfg Xtype definition of item to add.
32929      */
32930     addxtype : function(cfg)
32931     {
32932         // basically accepts a pannel...
32933         // can accept a layout region..!?!?
32934         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32935         
32936         
32937         // theory?  children can only be panels??
32938         
32939         //if (!cfg.xtype.match(/Panel$/)) {
32940         //    return false;
32941         //}
32942         var ret = false;
32943         
32944         if (typeof(cfg.region) == 'undefined') {
32945             Roo.log("Failed to add Panel, region was not set");
32946             Roo.log(cfg);
32947             return false;
32948         }
32949         var region = cfg.region;
32950         delete cfg.region;
32951         
32952           
32953         var xitems = [];
32954         if (cfg.items) {
32955             xitems = cfg.items;
32956             delete cfg.items;
32957         }
32958         var nb = false;
32959         
32960         switch(cfg.xtype) 
32961         {
32962             case 'Content':  // ContentPanel (el, cfg)
32963             case 'Scroll':  // ContentPanel (el, cfg)
32964             case 'View': 
32965                 cfg.autoCreate = true;
32966                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32967                 //} else {
32968                 //    var el = this.el.createChild();
32969                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32970                 //}
32971                 
32972                 this.add(region, ret);
32973                 break;
32974             
32975             /*
32976             case 'TreePanel': // our new panel!
32977                 cfg.el = this.el.createChild();
32978                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32979                 this.add(region, ret);
32980                 break;
32981             */
32982             
32983             case 'Nest': 
32984                 // create a new Layout (which is  a Border Layout...
32985                 
32986                 var clayout = cfg.layout;
32987                 clayout.el  = this.el.createChild();
32988                 clayout.items   = clayout.items  || [];
32989                 
32990                 delete cfg.layout;
32991                 
32992                 // replace this exitems with the clayout ones..
32993                 xitems = clayout.items;
32994                  
32995                 // force background off if it's in center...
32996                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32997                     cfg.background = false;
32998                 }
32999                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33000                 
33001                 
33002                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33003                 //console.log('adding nested layout panel '  + cfg.toSource());
33004                 this.add(region, ret);
33005                 nb = {}; /// find first...
33006                 break;
33007             
33008             case 'Grid':
33009                 
33010                 // needs grid and region
33011                 
33012                 //var el = this.getRegion(region).el.createChild();
33013                 /*
33014                  *var el = this.el.createChild();
33015                 // create the grid first...
33016                 cfg.grid.container = el;
33017                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33018                 */
33019                 
33020                 if (region == 'center' && this.active ) {
33021                     cfg.background = false;
33022                 }
33023                 
33024                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33025                 
33026                 this.add(region, ret);
33027                 /*
33028                 if (cfg.background) {
33029                     // render grid on panel activation (if panel background)
33030                     ret.on('activate', function(gp) {
33031                         if (!gp.grid.rendered) {
33032                     //        gp.grid.render(el);
33033                         }
33034                     });
33035                 } else {
33036                   //  cfg.grid.render(el);
33037                 }
33038                 */
33039                 break;
33040            
33041            
33042             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33043                 // it was the old xcomponent building that caused this before.
33044                 // espeically if border is the top element in the tree.
33045                 ret = this;
33046                 break; 
33047                 
33048                     
33049                 
33050                 
33051                 
33052             default:
33053                 /*
33054                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33055                     
33056                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33057                     this.add(region, ret);
33058                 } else {
33059                 */
33060                     Roo.log(cfg);
33061                     throw "Can not add '" + cfg.xtype + "' to Border";
33062                     return null;
33063              
33064                                 
33065              
33066         }
33067         this.beginUpdate();
33068         // add children..
33069         var region = '';
33070         var abn = {};
33071         Roo.each(xitems, function(i)  {
33072             region = nb && i.region ? i.region : false;
33073             
33074             var add = ret.addxtype(i);
33075            
33076             if (region) {
33077                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33078                 if (!i.background) {
33079                     abn[region] = nb[region] ;
33080                 }
33081             }
33082             
33083         });
33084         this.endUpdate();
33085
33086         // make the last non-background panel active..
33087         //if (nb) { Roo.log(abn); }
33088         if (nb) {
33089             
33090             for(var r in abn) {
33091                 region = this.getRegion(r);
33092                 if (region) {
33093                     // tried using nb[r], but it does not work..
33094                      
33095                     region.showPanel(abn[r]);
33096                    
33097                 }
33098             }
33099         }
33100         return ret;
33101         
33102     },
33103     
33104     
33105 // private
33106     factory : function(cfg)
33107     {
33108         
33109         var validRegions = Roo.bootstrap.layout.Border.regions;
33110
33111         var target = cfg.region;
33112         cfg.mgr = this;
33113         
33114         var r = Roo.bootstrap.layout;
33115         Roo.log(target);
33116         switch(target){
33117             case "north":
33118                 return new r.North(cfg);
33119             case "south":
33120                 return new r.South(cfg);
33121             case "east":
33122                 return new r.East(cfg);
33123             case "west":
33124                 return new r.West(cfg);
33125             case "center":
33126                 return new r.Center(cfg);
33127         }
33128         throw 'Layout region "'+target+'" not supported.';
33129     }
33130     
33131     
33132 });
33133  /*
33134  * Based on:
33135  * Ext JS Library 1.1.1
33136  * Copyright(c) 2006-2007, Ext JS, LLC.
33137  *
33138  * Originally Released Under LGPL - original licence link has changed is not relivant.
33139  *
33140  * Fork - LGPL
33141  * <script type="text/javascript">
33142  */
33143  
33144 /**
33145  * @class Roo.bootstrap.layout.Basic
33146  * @extends Roo.util.Observable
33147  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33148  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33149  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33150  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33151  * @cfg {string}   region  the region that it inhabits..
33152  * @cfg {bool}   skipConfig skip config?
33153  * 
33154
33155  */
33156 Roo.bootstrap.layout.Basic = function(config){
33157     
33158     this.mgr = config.mgr;
33159     
33160     this.position = config.region;
33161     
33162     var skipConfig = config.skipConfig;
33163     
33164     this.events = {
33165         /**
33166          * @scope Roo.BasicLayoutRegion
33167          */
33168         
33169         /**
33170          * @event beforeremove
33171          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33172          * @param {Roo.LayoutRegion} this
33173          * @param {Roo.ContentPanel} panel The panel
33174          * @param {Object} e The cancel event object
33175          */
33176         "beforeremove" : true,
33177         /**
33178          * @event invalidated
33179          * Fires when the layout for this region is changed.
33180          * @param {Roo.LayoutRegion} this
33181          */
33182         "invalidated" : true,
33183         /**
33184          * @event visibilitychange
33185          * Fires when this region is shown or hidden 
33186          * @param {Roo.LayoutRegion} this
33187          * @param {Boolean} visibility true or false
33188          */
33189         "visibilitychange" : true,
33190         /**
33191          * @event paneladded
33192          * Fires when a panel is added. 
33193          * @param {Roo.LayoutRegion} this
33194          * @param {Roo.ContentPanel} panel The panel
33195          */
33196         "paneladded" : true,
33197         /**
33198          * @event panelremoved
33199          * Fires when a panel is removed. 
33200          * @param {Roo.LayoutRegion} this
33201          * @param {Roo.ContentPanel} panel The panel
33202          */
33203         "panelremoved" : true,
33204         /**
33205          * @event beforecollapse
33206          * Fires when this region before collapse.
33207          * @param {Roo.LayoutRegion} this
33208          */
33209         "beforecollapse" : true,
33210         /**
33211          * @event collapsed
33212          * Fires when this region is collapsed.
33213          * @param {Roo.LayoutRegion} this
33214          */
33215         "collapsed" : true,
33216         /**
33217          * @event expanded
33218          * Fires when this region is expanded.
33219          * @param {Roo.LayoutRegion} this
33220          */
33221         "expanded" : true,
33222         /**
33223          * @event slideshow
33224          * Fires when this region is slid into view.
33225          * @param {Roo.LayoutRegion} this
33226          */
33227         "slideshow" : true,
33228         /**
33229          * @event slidehide
33230          * Fires when this region slides out of view. 
33231          * @param {Roo.LayoutRegion} this
33232          */
33233         "slidehide" : true,
33234         /**
33235          * @event panelactivated
33236          * Fires when a panel is activated. 
33237          * @param {Roo.LayoutRegion} this
33238          * @param {Roo.ContentPanel} panel The activated panel
33239          */
33240         "panelactivated" : true,
33241         /**
33242          * @event resized
33243          * Fires when the user resizes this region. 
33244          * @param {Roo.LayoutRegion} this
33245          * @param {Number} newSize The new size (width for east/west, height for north/south)
33246          */
33247         "resized" : true
33248     };
33249     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33250     this.panels = new Roo.util.MixedCollection();
33251     this.panels.getKey = this.getPanelId.createDelegate(this);
33252     this.box = null;
33253     this.activePanel = null;
33254     // ensure listeners are added...
33255     
33256     if (config.listeners || config.events) {
33257         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33258             listeners : config.listeners || {},
33259             events : config.events || {}
33260         });
33261     }
33262     
33263     if(skipConfig !== true){
33264         this.applyConfig(config);
33265     }
33266 };
33267
33268 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33269 {
33270     getPanelId : function(p){
33271         return p.getId();
33272     },
33273     
33274     applyConfig : function(config){
33275         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33276         this.config = config;
33277         
33278     },
33279     
33280     /**
33281      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33282      * the width, for horizontal (north, south) the height.
33283      * @param {Number} newSize The new width or height
33284      */
33285     resizeTo : function(newSize){
33286         var el = this.el ? this.el :
33287                  (this.activePanel ? this.activePanel.getEl() : null);
33288         if(el){
33289             switch(this.position){
33290                 case "east":
33291                 case "west":
33292                     el.setWidth(newSize);
33293                     this.fireEvent("resized", this, newSize);
33294                 break;
33295                 case "north":
33296                 case "south":
33297                     el.setHeight(newSize);
33298                     this.fireEvent("resized", this, newSize);
33299                 break;                
33300             }
33301         }
33302     },
33303     
33304     getBox : function(){
33305         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33306     },
33307     
33308     getMargins : function(){
33309         return this.margins;
33310     },
33311     
33312     updateBox : function(box){
33313         this.box = box;
33314         var el = this.activePanel.getEl();
33315         el.dom.style.left = box.x + "px";
33316         el.dom.style.top = box.y + "px";
33317         this.activePanel.setSize(box.width, box.height);
33318     },
33319     
33320     /**
33321      * Returns the container element for this region.
33322      * @return {Roo.Element}
33323      */
33324     getEl : function(){
33325         return this.activePanel;
33326     },
33327     
33328     /**
33329      * Returns true if this region is currently visible.
33330      * @return {Boolean}
33331      */
33332     isVisible : function(){
33333         return this.activePanel ? true : false;
33334     },
33335     
33336     setActivePanel : function(panel){
33337         panel = this.getPanel(panel);
33338         if(this.activePanel && this.activePanel != panel){
33339             this.activePanel.setActiveState(false);
33340             this.activePanel.getEl().setLeftTop(-10000,-10000);
33341         }
33342         this.activePanel = panel;
33343         panel.setActiveState(true);
33344         if(this.box){
33345             panel.setSize(this.box.width, this.box.height);
33346         }
33347         this.fireEvent("panelactivated", this, panel);
33348         this.fireEvent("invalidated");
33349     },
33350     
33351     /**
33352      * Show the specified panel.
33353      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33354      * @return {Roo.ContentPanel} The shown panel or null
33355      */
33356     showPanel : function(panel){
33357         panel = this.getPanel(panel);
33358         if(panel){
33359             this.setActivePanel(panel);
33360         }
33361         return panel;
33362     },
33363     
33364     /**
33365      * Get the active panel for this region.
33366      * @return {Roo.ContentPanel} The active panel or null
33367      */
33368     getActivePanel : function(){
33369         return this.activePanel;
33370     },
33371     
33372     /**
33373      * Add the passed ContentPanel(s)
33374      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33375      * @return {Roo.ContentPanel} The panel added (if only one was added)
33376      */
33377     add : function(panel){
33378         if(arguments.length > 1){
33379             for(var i = 0, len = arguments.length; i < len; i++) {
33380                 this.add(arguments[i]);
33381             }
33382             return null;
33383         }
33384         if(this.hasPanel(panel)){
33385             this.showPanel(panel);
33386             return panel;
33387         }
33388         var el = panel.getEl();
33389         if(el.dom.parentNode != this.mgr.el.dom){
33390             this.mgr.el.dom.appendChild(el.dom);
33391         }
33392         if(panel.setRegion){
33393             panel.setRegion(this);
33394         }
33395         this.panels.add(panel);
33396         el.setStyle("position", "absolute");
33397         if(!panel.background){
33398             this.setActivePanel(panel);
33399             if(this.config.initialSize && this.panels.getCount()==1){
33400                 this.resizeTo(this.config.initialSize);
33401             }
33402         }
33403         this.fireEvent("paneladded", this, panel);
33404         return panel;
33405     },
33406     
33407     /**
33408      * Returns true if the panel is in this region.
33409      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33410      * @return {Boolean}
33411      */
33412     hasPanel : function(panel){
33413         if(typeof panel == "object"){ // must be panel obj
33414             panel = panel.getId();
33415         }
33416         return this.getPanel(panel) ? true : false;
33417     },
33418     
33419     /**
33420      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33421      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33422      * @param {Boolean} preservePanel Overrides the config preservePanel option
33423      * @return {Roo.ContentPanel} The panel that was removed
33424      */
33425     remove : function(panel, preservePanel){
33426         panel = this.getPanel(panel);
33427         if(!panel){
33428             return null;
33429         }
33430         var e = {};
33431         this.fireEvent("beforeremove", this, panel, e);
33432         if(e.cancel === true){
33433             return null;
33434         }
33435         var panelId = panel.getId();
33436         this.panels.removeKey(panelId);
33437         return panel;
33438     },
33439     
33440     /**
33441      * Returns the panel specified or null if it's not in this region.
33442      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33443      * @return {Roo.ContentPanel}
33444      */
33445     getPanel : function(id){
33446         if(typeof id == "object"){ // must be panel obj
33447             return id;
33448         }
33449         return this.panels.get(id);
33450     },
33451     
33452     /**
33453      * Returns this regions position (north/south/east/west/center).
33454      * @return {String} 
33455      */
33456     getPosition: function(){
33457         return this.position;    
33458     }
33459 });/*
33460  * Based on:
33461  * Ext JS Library 1.1.1
33462  * Copyright(c) 2006-2007, Ext JS, LLC.
33463  *
33464  * Originally Released Under LGPL - original licence link has changed is not relivant.
33465  *
33466  * Fork - LGPL
33467  * <script type="text/javascript">
33468  */
33469  
33470 /**
33471  * @class Roo.bootstrap.layout.Region
33472  * @extends Roo.bootstrap.layout.Basic
33473  * This class represents a region in a layout manager.
33474  
33475  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33476  * @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})
33477  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33478  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33479  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33480  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33481  * @cfg {String}    title           The title for the region (overrides panel titles)
33482  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33483  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33484  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33485  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33486  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33487  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33488  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33489  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33490  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33491  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33492
33493  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33494  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33495  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33496  * @cfg {Number}    width           For East/West panels
33497  * @cfg {Number}    height          For North/South panels
33498  * @cfg {Boolean}   split           To show the splitter
33499  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33500  * 
33501  * @cfg {string}   cls             Extra CSS classes to add to region
33502  * 
33503  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33504  * @cfg {string}   region  the region that it inhabits..
33505  *
33506
33507  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33508  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33509
33510  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33511  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33512  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33513  */
33514 Roo.bootstrap.layout.Region = function(config)
33515 {
33516     this.applyConfig(config);
33517
33518     var mgr = config.mgr;
33519     var pos = config.region;
33520     config.skipConfig = true;
33521     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33522     
33523     if (mgr.el) {
33524         this.onRender(mgr.el);   
33525     }
33526      
33527     this.visible = true;
33528     this.collapsed = false;
33529     this.unrendered_panels = [];
33530 };
33531
33532 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33533
33534     position: '', // set by wrapper (eg. north/south etc..)
33535     unrendered_panels : null,  // unrendered panels.
33536     createBody : function(){
33537         /** This region's body element 
33538         * @type Roo.Element */
33539         this.bodyEl = this.el.createChild({
33540                 tag: "div",
33541                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33542         });
33543     },
33544
33545     onRender: function(ctr, pos)
33546     {
33547         var dh = Roo.DomHelper;
33548         /** This region's container element 
33549         * @type Roo.Element */
33550         this.el = dh.append(ctr.dom, {
33551                 tag: "div",
33552                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33553             }, true);
33554         /** This region's title element 
33555         * @type Roo.Element */
33556     
33557         this.titleEl = dh.append(this.el.dom,
33558             {
33559                     tag: "div",
33560                     unselectable: "on",
33561                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33562                     children:[
33563                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33564                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33565                     ]}, true);
33566         
33567         this.titleEl.enableDisplayMode();
33568         /** This region's title text element 
33569         * @type HTMLElement */
33570         this.titleTextEl = this.titleEl.dom.firstChild;
33571         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33572         /*
33573         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33574         this.closeBtn.enableDisplayMode();
33575         this.closeBtn.on("click", this.closeClicked, this);
33576         this.closeBtn.hide();
33577     */
33578         this.createBody(this.config);
33579         if(this.config.hideWhenEmpty){
33580             this.hide();
33581             this.on("paneladded", this.validateVisibility, this);
33582             this.on("panelremoved", this.validateVisibility, this);
33583         }
33584         if(this.autoScroll){
33585             this.bodyEl.setStyle("overflow", "auto");
33586         }else{
33587             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33588         }
33589         //if(c.titlebar !== false){
33590             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33591                 this.titleEl.hide();
33592             }else{
33593                 this.titleEl.show();
33594                 if(this.config.title){
33595                     this.titleTextEl.innerHTML = this.config.title;
33596                 }
33597             }
33598         //}
33599         if(this.config.collapsed){
33600             this.collapse(true);
33601         }
33602         if(this.config.hidden){
33603             this.hide();
33604         }
33605         
33606         if (this.unrendered_panels && this.unrendered_panels.length) {
33607             for (var i =0;i< this.unrendered_panels.length; i++) {
33608                 this.add(this.unrendered_panels[i]);
33609             }
33610             this.unrendered_panels = null;
33611             
33612         }
33613         
33614     },
33615     
33616     applyConfig : function(c)
33617     {
33618         /*
33619          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33620             var dh = Roo.DomHelper;
33621             if(c.titlebar !== false){
33622                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33623                 this.collapseBtn.on("click", this.collapse, this);
33624                 this.collapseBtn.enableDisplayMode();
33625                 /*
33626                 if(c.showPin === true || this.showPin){
33627                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33628                     this.stickBtn.enableDisplayMode();
33629                     this.stickBtn.on("click", this.expand, this);
33630                     this.stickBtn.hide();
33631                 }
33632                 
33633             }
33634             */
33635             /** This region's collapsed element
33636             * @type Roo.Element */
33637             /*
33638              *
33639             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33640                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33641             ]}, true);
33642             
33643             if(c.floatable !== false){
33644                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33645                this.collapsedEl.on("click", this.collapseClick, this);
33646             }
33647
33648             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33649                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33650                    id: "message", unselectable: "on", style:{"float":"left"}});
33651                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33652              }
33653             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33654             this.expandBtn.on("click", this.expand, this);
33655             
33656         }
33657         
33658         if(this.collapseBtn){
33659             this.collapseBtn.setVisible(c.collapsible == true);
33660         }
33661         
33662         this.cmargins = c.cmargins || this.cmargins ||
33663                          (this.position == "west" || this.position == "east" ?
33664                              {top: 0, left: 2, right:2, bottom: 0} :
33665                              {top: 2, left: 0, right:0, bottom: 2});
33666         */
33667         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33668         
33669         
33670         this.bottomTabs = c.tabPosition != "top";
33671         
33672         this.autoScroll = c.autoScroll || false;
33673         
33674         
33675        
33676         
33677         this.duration = c.duration || .30;
33678         this.slideDuration = c.slideDuration || .45;
33679         this.config = c;
33680        
33681     },
33682     /**
33683      * Returns true if this region is currently visible.
33684      * @return {Boolean}
33685      */
33686     isVisible : function(){
33687         return this.visible;
33688     },
33689
33690     /**
33691      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33692      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33693      */
33694     //setCollapsedTitle : function(title){
33695     //    title = title || "&#160;";
33696      //   if(this.collapsedTitleTextEl){
33697       //      this.collapsedTitleTextEl.innerHTML = title;
33698        // }
33699     //},
33700
33701     getBox : function(){
33702         var b;
33703       //  if(!this.collapsed){
33704             b = this.el.getBox(false, true);
33705        // }else{
33706           //  b = this.collapsedEl.getBox(false, true);
33707         //}
33708         return b;
33709     },
33710
33711     getMargins : function(){
33712         return this.margins;
33713         //return this.collapsed ? this.cmargins : this.margins;
33714     },
33715 /*
33716     highlight : function(){
33717         this.el.addClass("x-layout-panel-dragover");
33718     },
33719
33720     unhighlight : function(){
33721         this.el.removeClass("x-layout-panel-dragover");
33722     },
33723 */
33724     updateBox : function(box)
33725     {
33726         if (!this.bodyEl) {
33727             return; // not rendered yet..
33728         }
33729         
33730         this.box = box;
33731         if(!this.collapsed){
33732             this.el.dom.style.left = box.x + "px";
33733             this.el.dom.style.top = box.y + "px";
33734             this.updateBody(box.width, box.height);
33735         }else{
33736             this.collapsedEl.dom.style.left = box.x + "px";
33737             this.collapsedEl.dom.style.top = box.y + "px";
33738             this.collapsedEl.setSize(box.width, box.height);
33739         }
33740         if(this.tabs){
33741             this.tabs.autoSizeTabs();
33742         }
33743     },
33744
33745     updateBody : function(w, h)
33746     {
33747         if(w !== null){
33748             this.el.setWidth(w);
33749             w -= this.el.getBorderWidth("rl");
33750             if(this.config.adjustments){
33751                 w += this.config.adjustments[0];
33752             }
33753         }
33754         if(h !== null && h > 0){
33755             this.el.setHeight(h);
33756             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33757             h -= this.el.getBorderWidth("tb");
33758             if(this.config.adjustments){
33759                 h += this.config.adjustments[1];
33760             }
33761             this.bodyEl.setHeight(h);
33762             if(this.tabs){
33763                 h = this.tabs.syncHeight(h);
33764             }
33765         }
33766         if(this.panelSize){
33767             w = w !== null ? w : this.panelSize.width;
33768             h = h !== null ? h : this.panelSize.height;
33769         }
33770         if(this.activePanel){
33771             var el = this.activePanel.getEl();
33772             w = w !== null ? w : el.getWidth();
33773             h = h !== null ? h : el.getHeight();
33774             this.panelSize = {width: w, height: h};
33775             this.activePanel.setSize(w, h);
33776         }
33777         if(Roo.isIE && this.tabs){
33778             this.tabs.el.repaint();
33779         }
33780     },
33781
33782     /**
33783      * Returns the container element for this region.
33784      * @return {Roo.Element}
33785      */
33786     getEl : function(){
33787         return this.el;
33788     },
33789
33790     /**
33791      * Hides this region.
33792      */
33793     hide : function(){
33794         //if(!this.collapsed){
33795             this.el.dom.style.left = "-2000px";
33796             this.el.hide();
33797         //}else{
33798          //   this.collapsedEl.dom.style.left = "-2000px";
33799          //   this.collapsedEl.hide();
33800        // }
33801         this.visible = false;
33802         this.fireEvent("visibilitychange", this, false);
33803     },
33804
33805     /**
33806      * Shows this region if it was previously hidden.
33807      */
33808     show : function(){
33809         //if(!this.collapsed){
33810             this.el.show();
33811         //}else{
33812         //    this.collapsedEl.show();
33813        // }
33814         this.visible = true;
33815         this.fireEvent("visibilitychange", this, true);
33816     },
33817 /*
33818     closeClicked : function(){
33819         if(this.activePanel){
33820             this.remove(this.activePanel);
33821         }
33822     },
33823
33824     collapseClick : function(e){
33825         if(this.isSlid){
33826            e.stopPropagation();
33827            this.slideIn();
33828         }else{
33829            e.stopPropagation();
33830            this.slideOut();
33831         }
33832     },
33833 */
33834     /**
33835      * Collapses this region.
33836      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33837      */
33838     /*
33839     collapse : function(skipAnim, skipCheck = false){
33840         if(this.collapsed) {
33841             return;
33842         }
33843         
33844         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33845             
33846             this.collapsed = true;
33847             if(this.split){
33848                 this.split.el.hide();
33849             }
33850             if(this.config.animate && skipAnim !== true){
33851                 this.fireEvent("invalidated", this);
33852                 this.animateCollapse();
33853             }else{
33854                 this.el.setLocation(-20000,-20000);
33855                 this.el.hide();
33856                 this.collapsedEl.show();
33857                 this.fireEvent("collapsed", this);
33858                 this.fireEvent("invalidated", this);
33859             }
33860         }
33861         
33862     },
33863 */
33864     animateCollapse : function(){
33865         // overridden
33866     },
33867
33868     /**
33869      * Expands this region if it was previously collapsed.
33870      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33871      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33872      */
33873     /*
33874     expand : function(e, skipAnim){
33875         if(e) {
33876             e.stopPropagation();
33877         }
33878         if(!this.collapsed || this.el.hasActiveFx()) {
33879             return;
33880         }
33881         if(this.isSlid){
33882             this.afterSlideIn();
33883             skipAnim = true;
33884         }
33885         this.collapsed = false;
33886         if(this.config.animate && skipAnim !== true){
33887             this.animateExpand();
33888         }else{
33889             this.el.show();
33890             if(this.split){
33891                 this.split.el.show();
33892             }
33893             this.collapsedEl.setLocation(-2000,-2000);
33894             this.collapsedEl.hide();
33895             this.fireEvent("invalidated", this);
33896             this.fireEvent("expanded", this);
33897         }
33898     },
33899 */
33900     animateExpand : function(){
33901         // overridden
33902     },
33903
33904     initTabs : function()
33905     {
33906         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33907         
33908         var ts = new Roo.bootstrap.panel.Tabs({
33909                 el: this.bodyEl.dom,
33910                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33911                 disableTooltips: this.config.disableTabTips,
33912                 toolbar : this.config.toolbar
33913             });
33914         
33915         if(this.config.hideTabs){
33916             ts.stripWrap.setDisplayed(false);
33917         }
33918         this.tabs = ts;
33919         ts.resizeTabs = this.config.resizeTabs === true;
33920         ts.minTabWidth = this.config.minTabWidth || 40;
33921         ts.maxTabWidth = this.config.maxTabWidth || 250;
33922         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33923         ts.monitorResize = false;
33924         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33925         ts.bodyEl.addClass('roo-layout-tabs-body');
33926         this.panels.each(this.initPanelAsTab, this);
33927     },
33928
33929     initPanelAsTab : function(panel){
33930         var ti = this.tabs.addTab(
33931             panel.getEl().id,
33932             panel.getTitle(),
33933             null,
33934             this.config.closeOnTab && panel.isClosable(),
33935             panel.tpl
33936         );
33937         if(panel.tabTip !== undefined){
33938             ti.setTooltip(panel.tabTip);
33939         }
33940         ti.on("activate", function(){
33941               this.setActivePanel(panel);
33942         }, this);
33943         
33944         if(this.config.closeOnTab){
33945             ti.on("beforeclose", function(t, e){
33946                 e.cancel = true;
33947                 this.remove(panel);
33948             }, this);
33949         }
33950         
33951         panel.tabItem = ti;
33952         
33953         return ti;
33954     },
33955
33956     updatePanelTitle : function(panel, title)
33957     {
33958         if(this.activePanel == panel){
33959             this.updateTitle(title);
33960         }
33961         if(this.tabs){
33962             var ti = this.tabs.getTab(panel.getEl().id);
33963             ti.setText(title);
33964             if(panel.tabTip !== undefined){
33965                 ti.setTooltip(panel.tabTip);
33966             }
33967         }
33968     },
33969
33970     updateTitle : function(title){
33971         if(this.titleTextEl && !this.config.title){
33972             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33973         }
33974     },
33975
33976     setActivePanel : function(panel)
33977     {
33978         panel = this.getPanel(panel);
33979         if(this.activePanel && this.activePanel != panel){
33980             this.activePanel.setActiveState(false);
33981         }
33982         this.activePanel = panel;
33983         panel.setActiveState(true);
33984         if(this.panelSize){
33985             panel.setSize(this.panelSize.width, this.panelSize.height);
33986         }
33987         if(this.closeBtn){
33988             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33989         }
33990         this.updateTitle(panel.getTitle());
33991         if(this.tabs){
33992             this.fireEvent("invalidated", this);
33993         }
33994         this.fireEvent("panelactivated", this, panel);
33995     },
33996
33997     /**
33998      * Shows the specified panel.
33999      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34000      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34001      */
34002     showPanel : function(panel)
34003     {
34004         panel = this.getPanel(panel);
34005         if(panel){
34006             if(this.tabs){
34007                 var tab = this.tabs.getTab(panel.getEl().id);
34008                 if(tab.isHidden()){
34009                     this.tabs.unhideTab(tab.id);
34010                 }
34011                 tab.activate();
34012             }else{
34013                 this.setActivePanel(panel);
34014             }
34015         }
34016         return panel;
34017     },
34018
34019     /**
34020      * Get the active panel for this region.
34021      * @return {Roo.ContentPanel} The active panel or null
34022      */
34023     getActivePanel : function(){
34024         return this.activePanel;
34025     },
34026
34027     validateVisibility : function(){
34028         if(this.panels.getCount() < 1){
34029             this.updateTitle("&#160;");
34030             this.closeBtn.hide();
34031             this.hide();
34032         }else{
34033             if(!this.isVisible()){
34034                 this.show();
34035             }
34036         }
34037     },
34038
34039     /**
34040      * Adds the passed ContentPanel(s) to this region.
34041      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34042      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34043      */
34044     add : function(panel)
34045     {
34046         if(arguments.length > 1){
34047             for(var i = 0, len = arguments.length; i < len; i++) {
34048                 this.add(arguments[i]);
34049             }
34050             return null;
34051         }
34052         
34053         // if we have not been rendered yet, then we can not really do much of this..
34054         if (!this.bodyEl) {
34055             this.unrendered_panels.push(panel);
34056             return panel;
34057         }
34058         
34059         
34060         
34061         
34062         if(this.hasPanel(panel)){
34063             this.showPanel(panel);
34064             return panel;
34065         }
34066         panel.setRegion(this);
34067         this.panels.add(panel);
34068        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34069             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34070             // and hide them... ???
34071             this.bodyEl.dom.appendChild(panel.getEl().dom);
34072             if(panel.background !== true){
34073                 this.setActivePanel(panel);
34074             }
34075             this.fireEvent("paneladded", this, panel);
34076             return panel;
34077         }
34078         */
34079         if(!this.tabs){
34080             this.initTabs();
34081         }else{
34082             this.initPanelAsTab(panel);
34083         }
34084         
34085         
34086         if(panel.background !== true){
34087             this.tabs.activate(panel.getEl().id);
34088         }
34089         this.fireEvent("paneladded", this, panel);
34090         return panel;
34091     },
34092
34093     /**
34094      * Hides the tab for the specified panel.
34095      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34096      */
34097     hidePanel : function(panel){
34098         if(this.tabs && (panel = this.getPanel(panel))){
34099             this.tabs.hideTab(panel.getEl().id);
34100         }
34101     },
34102
34103     /**
34104      * Unhides the tab for a previously hidden panel.
34105      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34106      */
34107     unhidePanel : function(panel){
34108         if(this.tabs && (panel = this.getPanel(panel))){
34109             this.tabs.unhideTab(panel.getEl().id);
34110         }
34111     },
34112
34113     clearPanels : function(){
34114         while(this.panels.getCount() > 0){
34115              this.remove(this.panels.first());
34116         }
34117     },
34118
34119     /**
34120      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34121      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34122      * @param {Boolean} preservePanel Overrides the config preservePanel option
34123      * @return {Roo.ContentPanel} The panel that was removed
34124      */
34125     remove : function(panel, preservePanel)
34126     {
34127         panel = this.getPanel(panel);
34128         if(!panel){
34129             return null;
34130         }
34131         var e = {};
34132         this.fireEvent("beforeremove", this, panel, e);
34133         if(e.cancel === true){
34134             return null;
34135         }
34136         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34137         var panelId = panel.getId();
34138         this.panels.removeKey(panelId);
34139         if(preservePanel){
34140             document.body.appendChild(panel.getEl().dom);
34141         }
34142         if(this.tabs){
34143             this.tabs.removeTab(panel.getEl().id);
34144         }else if (!preservePanel){
34145             this.bodyEl.dom.removeChild(panel.getEl().dom);
34146         }
34147         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34148             var p = this.panels.first();
34149             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34150             tempEl.appendChild(p.getEl().dom);
34151             this.bodyEl.update("");
34152             this.bodyEl.dom.appendChild(p.getEl().dom);
34153             tempEl = null;
34154             this.updateTitle(p.getTitle());
34155             this.tabs = null;
34156             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34157             this.setActivePanel(p);
34158         }
34159         panel.setRegion(null);
34160         if(this.activePanel == panel){
34161             this.activePanel = null;
34162         }
34163         if(this.config.autoDestroy !== false && preservePanel !== true){
34164             try{panel.destroy();}catch(e){}
34165         }
34166         this.fireEvent("panelremoved", this, panel);
34167         return panel;
34168     },
34169
34170     /**
34171      * Returns the TabPanel component used by this region
34172      * @return {Roo.TabPanel}
34173      */
34174     getTabs : function(){
34175         return this.tabs;
34176     },
34177
34178     createTool : function(parentEl, className){
34179         var btn = Roo.DomHelper.append(parentEl, {
34180             tag: "div",
34181             cls: "x-layout-tools-button",
34182             children: [ {
34183                 tag: "div",
34184                 cls: "roo-layout-tools-button-inner " + className,
34185                 html: "&#160;"
34186             }]
34187         }, true);
34188         btn.addClassOnOver("roo-layout-tools-button-over");
34189         return btn;
34190     }
34191 });/*
34192  * Based on:
34193  * Ext JS Library 1.1.1
34194  * Copyright(c) 2006-2007, Ext JS, LLC.
34195  *
34196  * Originally Released Under LGPL - original licence link has changed is not relivant.
34197  *
34198  * Fork - LGPL
34199  * <script type="text/javascript">
34200  */
34201  
34202
34203
34204 /**
34205  * @class Roo.SplitLayoutRegion
34206  * @extends Roo.LayoutRegion
34207  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34208  */
34209 Roo.bootstrap.layout.Split = function(config){
34210     this.cursor = config.cursor;
34211     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34212 };
34213
34214 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34215 {
34216     splitTip : "Drag to resize.",
34217     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34218     useSplitTips : false,
34219
34220     applyConfig : function(config){
34221         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34222     },
34223     
34224     onRender : function(ctr,pos) {
34225         
34226         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34227         if(!this.config.split){
34228             return;
34229         }
34230         if(!this.split){
34231             
34232             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34233                             tag: "div",
34234                             id: this.el.id + "-split",
34235                             cls: "roo-layout-split roo-layout-split-"+this.position,
34236                             html: "&#160;"
34237             });
34238             /** The SplitBar for this region 
34239             * @type Roo.SplitBar */
34240             // does not exist yet...
34241             Roo.log([this.position, this.orientation]);
34242             
34243             this.split = new Roo.bootstrap.SplitBar({
34244                 dragElement : splitEl,
34245                 resizingElement: this.el,
34246                 orientation : this.orientation
34247             });
34248             
34249             this.split.on("moved", this.onSplitMove, this);
34250             this.split.useShim = this.config.useShim === true;
34251             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34252             if(this.useSplitTips){
34253                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34254             }
34255             //if(config.collapsible){
34256             //    this.split.el.on("dblclick", this.collapse,  this);
34257             //}
34258         }
34259         if(typeof this.config.minSize != "undefined"){
34260             this.split.minSize = this.config.minSize;
34261         }
34262         if(typeof this.config.maxSize != "undefined"){
34263             this.split.maxSize = this.config.maxSize;
34264         }
34265         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34266             this.hideSplitter();
34267         }
34268         
34269     },
34270
34271     getHMaxSize : function(){
34272          var cmax = this.config.maxSize || 10000;
34273          var center = this.mgr.getRegion("center");
34274          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34275     },
34276
34277     getVMaxSize : function(){
34278          var cmax = this.config.maxSize || 10000;
34279          var center = this.mgr.getRegion("center");
34280          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34281     },
34282
34283     onSplitMove : function(split, newSize){
34284         this.fireEvent("resized", this, newSize);
34285     },
34286     
34287     /** 
34288      * Returns the {@link Roo.SplitBar} for this region.
34289      * @return {Roo.SplitBar}
34290      */
34291     getSplitBar : function(){
34292         return this.split;
34293     },
34294     
34295     hide : function(){
34296         this.hideSplitter();
34297         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34298     },
34299
34300     hideSplitter : function(){
34301         if(this.split){
34302             this.split.el.setLocation(-2000,-2000);
34303             this.split.el.hide();
34304         }
34305     },
34306
34307     show : function(){
34308         if(this.split){
34309             this.split.el.show();
34310         }
34311         Roo.bootstrap.layout.Split.superclass.show.call(this);
34312     },
34313     
34314     beforeSlide: function(){
34315         if(Roo.isGecko){// firefox overflow auto bug workaround
34316             this.bodyEl.clip();
34317             if(this.tabs) {
34318                 this.tabs.bodyEl.clip();
34319             }
34320             if(this.activePanel){
34321                 this.activePanel.getEl().clip();
34322                 
34323                 if(this.activePanel.beforeSlide){
34324                     this.activePanel.beforeSlide();
34325                 }
34326             }
34327         }
34328     },
34329     
34330     afterSlide : function(){
34331         if(Roo.isGecko){// firefox overflow auto bug workaround
34332             this.bodyEl.unclip();
34333             if(this.tabs) {
34334                 this.tabs.bodyEl.unclip();
34335             }
34336             if(this.activePanel){
34337                 this.activePanel.getEl().unclip();
34338                 if(this.activePanel.afterSlide){
34339                     this.activePanel.afterSlide();
34340                 }
34341             }
34342         }
34343     },
34344
34345     initAutoHide : function(){
34346         if(this.autoHide !== false){
34347             if(!this.autoHideHd){
34348                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34349                 this.autoHideHd = {
34350                     "mouseout": function(e){
34351                         if(!e.within(this.el, true)){
34352                             st.delay(500);
34353                         }
34354                     },
34355                     "mouseover" : function(e){
34356                         st.cancel();
34357                     },
34358                     scope : this
34359                 };
34360             }
34361             this.el.on(this.autoHideHd);
34362         }
34363     },
34364
34365     clearAutoHide : function(){
34366         if(this.autoHide !== false){
34367             this.el.un("mouseout", this.autoHideHd.mouseout);
34368             this.el.un("mouseover", this.autoHideHd.mouseover);
34369         }
34370     },
34371
34372     clearMonitor : function(){
34373         Roo.get(document).un("click", this.slideInIf, this);
34374     },
34375
34376     // these names are backwards but not changed for compat
34377     slideOut : function(){
34378         if(this.isSlid || this.el.hasActiveFx()){
34379             return;
34380         }
34381         this.isSlid = true;
34382         if(this.collapseBtn){
34383             this.collapseBtn.hide();
34384         }
34385         this.closeBtnState = this.closeBtn.getStyle('display');
34386         this.closeBtn.hide();
34387         if(this.stickBtn){
34388             this.stickBtn.show();
34389         }
34390         this.el.show();
34391         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34392         this.beforeSlide();
34393         this.el.setStyle("z-index", 10001);
34394         this.el.slideIn(this.getSlideAnchor(), {
34395             callback: function(){
34396                 this.afterSlide();
34397                 this.initAutoHide();
34398                 Roo.get(document).on("click", this.slideInIf, this);
34399                 this.fireEvent("slideshow", this);
34400             },
34401             scope: this,
34402             block: true
34403         });
34404     },
34405
34406     afterSlideIn : function(){
34407         this.clearAutoHide();
34408         this.isSlid = false;
34409         this.clearMonitor();
34410         this.el.setStyle("z-index", "");
34411         if(this.collapseBtn){
34412             this.collapseBtn.show();
34413         }
34414         this.closeBtn.setStyle('display', this.closeBtnState);
34415         if(this.stickBtn){
34416             this.stickBtn.hide();
34417         }
34418         this.fireEvent("slidehide", this);
34419     },
34420
34421     slideIn : function(cb){
34422         if(!this.isSlid || this.el.hasActiveFx()){
34423             Roo.callback(cb);
34424             return;
34425         }
34426         this.isSlid = false;
34427         this.beforeSlide();
34428         this.el.slideOut(this.getSlideAnchor(), {
34429             callback: function(){
34430                 this.el.setLeftTop(-10000, -10000);
34431                 this.afterSlide();
34432                 this.afterSlideIn();
34433                 Roo.callback(cb);
34434             },
34435             scope: this,
34436             block: true
34437         });
34438     },
34439     
34440     slideInIf : function(e){
34441         if(!e.within(this.el)){
34442             this.slideIn();
34443         }
34444     },
34445
34446     animateCollapse : function(){
34447         this.beforeSlide();
34448         this.el.setStyle("z-index", 20000);
34449         var anchor = this.getSlideAnchor();
34450         this.el.slideOut(anchor, {
34451             callback : function(){
34452                 this.el.setStyle("z-index", "");
34453                 this.collapsedEl.slideIn(anchor, {duration:.3});
34454                 this.afterSlide();
34455                 this.el.setLocation(-10000,-10000);
34456                 this.el.hide();
34457                 this.fireEvent("collapsed", this);
34458             },
34459             scope: this,
34460             block: true
34461         });
34462     },
34463
34464     animateExpand : function(){
34465         this.beforeSlide();
34466         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34467         this.el.setStyle("z-index", 20000);
34468         this.collapsedEl.hide({
34469             duration:.1
34470         });
34471         this.el.slideIn(this.getSlideAnchor(), {
34472             callback : function(){
34473                 this.el.setStyle("z-index", "");
34474                 this.afterSlide();
34475                 if(this.split){
34476                     this.split.el.show();
34477                 }
34478                 this.fireEvent("invalidated", this);
34479                 this.fireEvent("expanded", this);
34480             },
34481             scope: this,
34482             block: true
34483         });
34484     },
34485
34486     anchors : {
34487         "west" : "left",
34488         "east" : "right",
34489         "north" : "top",
34490         "south" : "bottom"
34491     },
34492
34493     sanchors : {
34494         "west" : "l",
34495         "east" : "r",
34496         "north" : "t",
34497         "south" : "b"
34498     },
34499
34500     canchors : {
34501         "west" : "tl-tr",
34502         "east" : "tr-tl",
34503         "north" : "tl-bl",
34504         "south" : "bl-tl"
34505     },
34506
34507     getAnchor : function(){
34508         return this.anchors[this.position];
34509     },
34510
34511     getCollapseAnchor : function(){
34512         return this.canchors[this.position];
34513     },
34514
34515     getSlideAnchor : function(){
34516         return this.sanchors[this.position];
34517     },
34518
34519     getAlignAdj : function(){
34520         var cm = this.cmargins;
34521         switch(this.position){
34522             case "west":
34523                 return [0, 0];
34524             break;
34525             case "east":
34526                 return [0, 0];
34527             break;
34528             case "north":
34529                 return [0, 0];
34530             break;
34531             case "south":
34532                 return [0, 0];
34533             break;
34534         }
34535     },
34536
34537     getExpandAdj : function(){
34538         var c = this.collapsedEl, cm = this.cmargins;
34539         switch(this.position){
34540             case "west":
34541                 return [-(cm.right+c.getWidth()+cm.left), 0];
34542             break;
34543             case "east":
34544                 return [cm.right+c.getWidth()+cm.left, 0];
34545             break;
34546             case "north":
34547                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34548             break;
34549             case "south":
34550                 return [0, cm.top+cm.bottom+c.getHeight()];
34551             break;
34552         }
34553     }
34554 });/*
34555  * Based on:
34556  * Ext JS Library 1.1.1
34557  * Copyright(c) 2006-2007, Ext JS, LLC.
34558  *
34559  * Originally Released Under LGPL - original licence link has changed is not relivant.
34560  *
34561  * Fork - LGPL
34562  * <script type="text/javascript">
34563  */
34564 /*
34565  * These classes are private internal classes
34566  */
34567 Roo.bootstrap.layout.Center = function(config){
34568     config.region = "center";
34569     Roo.bootstrap.layout.Region.call(this, config);
34570     this.visible = true;
34571     this.minWidth = config.minWidth || 20;
34572     this.minHeight = config.minHeight || 20;
34573 };
34574
34575 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34576     hide : function(){
34577         // center panel can't be hidden
34578     },
34579     
34580     show : function(){
34581         // center panel can't be hidden
34582     },
34583     
34584     getMinWidth: function(){
34585         return this.minWidth;
34586     },
34587     
34588     getMinHeight: function(){
34589         return this.minHeight;
34590     }
34591 });
34592
34593
34594
34595
34596  
34597
34598
34599
34600
34601
34602 Roo.bootstrap.layout.North = function(config)
34603 {
34604     config.region = 'north';
34605     config.cursor = 'n-resize';
34606     
34607     Roo.bootstrap.layout.Split.call(this, config);
34608     
34609     
34610     if(this.split){
34611         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34612         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34613         this.split.el.addClass("roo-layout-split-v");
34614     }
34615     var size = config.initialSize || config.height;
34616     if(typeof size != "undefined"){
34617         this.el.setHeight(size);
34618     }
34619 };
34620 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34621 {
34622     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34623     
34624     
34625     
34626     getBox : function(){
34627         if(this.collapsed){
34628             return this.collapsedEl.getBox();
34629         }
34630         var box = this.el.getBox();
34631         if(this.split){
34632             box.height += this.split.el.getHeight();
34633         }
34634         return box;
34635     },
34636     
34637     updateBox : function(box){
34638         if(this.split && !this.collapsed){
34639             box.height -= this.split.el.getHeight();
34640             this.split.el.setLeft(box.x);
34641             this.split.el.setTop(box.y+box.height);
34642             this.split.el.setWidth(box.width);
34643         }
34644         if(this.collapsed){
34645             this.updateBody(box.width, null);
34646         }
34647         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34648     }
34649 });
34650
34651
34652
34653
34654
34655 Roo.bootstrap.layout.South = function(config){
34656     config.region = 'south';
34657     config.cursor = 's-resize';
34658     Roo.bootstrap.layout.Split.call(this, config);
34659     if(this.split){
34660         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34661         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34662         this.split.el.addClass("roo-layout-split-v");
34663     }
34664     var size = config.initialSize || config.height;
34665     if(typeof size != "undefined"){
34666         this.el.setHeight(size);
34667     }
34668 };
34669
34670 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34671     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34672     getBox : function(){
34673         if(this.collapsed){
34674             return this.collapsedEl.getBox();
34675         }
34676         var box = this.el.getBox();
34677         if(this.split){
34678             var sh = this.split.el.getHeight();
34679             box.height += sh;
34680             box.y -= sh;
34681         }
34682         return box;
34683     },
34684     
34685     updateBox : function(box){
34686         if(this.split && !this.collapsed){
34687             var sh = this.split.el.getHeight();
34688             box.height -= sh;
34689             box.y += sh;
34690             this.split.el.setLeft(box.x);
34691             this.split.el.setTop(box.y-sh);
34692             this.split.el.setWidth(box.width);
34693         }
34694         if(this.collapsed){
34695             this.updateBody(box.width, null);
34696         }
34697         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34698     }
34699 });
34700
34701 Roo.bootstrap.layout.East = function(config){
34702     config.region = "east";
34703     config.cursor = "e-resize";
34704     Roo.bootstrap.layout.Split.call(this, config);
34705     if(this.split){
34706         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34707         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34708         this.split.el.addClass("roo-layout-split-h");
34709     }
34710     var size = config.initialSize || config.width;
34711     if(typeof size != "undefined"){
34712         this.el.setWidth(size);
34713     }
34714 };
34715 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34716     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34717     getBox : function(){
34718         if(this.collapsed){
34719             return this.collapsedEl.getBox();
34720         }
34721         var box = this.el.getBox();
34722         if(this.split){
34723             var sw = this.split.el.getWidth();
34724             box.width += sw;
34725             box.x -= sw;
34726         }
34727         return box;
34728     },
34729
34730     updateBox : function(box){
34731         if(this.split && !this.collapsed){
34732             var sw = this.split.el.getWidth();
34733             box.width -= sw;
34734             this.split.el.setLeft(box.x);
34735             this.split.el.setTop(box.y);
34736             this.split.el.setHeight(box.height);
34737             box.x += sw;
34738         }
34739         if(this.collapsed){
34740             this.updateBody(null, box.height);
34741         }
34742         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34743     }
34744 });
34745
34746 Roo.bootstrap.layout.West = function(config){
34747     config.region = "west";
34748     config.cursor = "w-resize";
34749     
34750     Roo.bootstrap.layout.Split.call(this, config);
34751     if(this.split){
34752         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34753         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34754         this.split.el.addClass("roo-layout-split-h");
34755     }
34756     
34757 };
34758 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34759     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34760     
34761     onRender: function(ctr, pos)
34762     {
34763         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34764         var size = this.config.initialSize || this.config.width;
34765         if(typeof size != "undefined"){
34766             this.el.setWidth(size);
34767         }
34768     },
34769     
34770     getBox : function(){
34771         if(this.collapsed){
34772             return this.collapsedEl.getBox();
34773         }
34774         var box = this.el.getBox();
34775         if(this.split){
34776             box.width += this.split.el.getWidth();
34777         }
34778         return box;
34779     },
34780     
34781     updateBox : function(box){
34782         if(this.split && !this.collapsed){
34783             var sw = this.split.el.getWidth();
34784             box.width -= sw;
34785             this.split.el.setLeft(box.x+box.width);
34786             this.split.el.setTop(box.y);
34787             this.split.el.setHeight(box.height);
34788         }
34789         if(this.collapsed){
34790             this.updateBody(null, box.height);
34791         }
34792         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34793     }
34794 });
34795 Roo.namespace("Roo.bootstrap.panel");/*
34796  * Based on:
34797  * Ext JS Library 1.1.1
34798  * Copyright(c) 2006-2007, Ext JS, LLC.
34799  *
34800  * Originally Released Under LGPL - original licence link has changed is not relivant.
34801  *
34802  * Fork - LGPL
34803  * <script type="text/javascript">
34804  */
34805 /**
34806  * @class Roo.ContentPanel
34807  * @extends Roo.util.Observable
34808  * A basic ContentPanel element.
34809  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34810  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34811  * @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
34812  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34813  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34814  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34815  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34816  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34817  * @cfg {String} title          The title for this panel
34818  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34819  * @cfg {String} url            Calls {@link #setUrl} with this value
34820  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34821  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34822  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34823  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34824  * @cfg {Boolean} badges render the badges
34825
34826  * @constructor
34827  * Create a new ContentPanel.
34828  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34829  * @param {String/Object} config A string to set only the title or a config object
34830  * @param {String} content (optional) Set the HTML content for this panel
34831  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34832  */
34833 Roo.bootstrap.panel.Content = function( config){
34834     
34835     this.tpl = config.tpl || false;
34836     
34837     var el = config.el;
34838     var content = config.content;
34839
34840     if(config.autoCreate){ // xtype is available if this is called from factory
34841         el = Roo.id();
34842     }
34843     this.el = Roo.get(el);
34844     if(!this.el && config && config.autoCreate){
34845         if(typeof config.autoCreate == "object"){
34846             if(!config.autoCreate.id){
34847                 config.autoCreate.id = config.id||el;
34848             }
34849             this.el = Roo.DomHelper.append(document.body,
34850                         config.autoCreate, true);
34851         }else{
34852             var elcfg =  {   tag: "div",
34853                             cls: "roo-layout-inactive-content",
34854                             id: config.id||el
34855                             };
34856             if (config.html) {
34857                 elcfg.html = config.html;
34858                 
34859             }
34860                         
34861             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34862         }
34863     } 
34864     this.closable = false;
34865     this.loaded = false;
34866     this.active = false;
34867    
34868       
34869     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34870         
34871         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34872         
34873         this.wrapEl = this.el; //this.el.wrap();
34874         var ti = [];
34875         if (config.toolbar.items) {
34876             ti = config.toolbar.items ;
34877             delete config.toolbar.items ;
34878         }
34879         
34880         var nitems = [];
34881         this.toolbar.render(this.wrapEl, 'before');
34882         for(var i =0;i < ti.length;i++) {
34883           //  Roo.log(['add child', items[i]]);
34884             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34885         }
34886         this.toolbar.items = nitems;
34887         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34888         delete config.toolbar;
34889         
34890     }
34891     /*
34892     // xtype created footer. - not sure if will work as we normally have to render first..
34893     if (this.footer && !this.footer.el && this.footer.xtype) {
34894         if (!this.wrapEl) {
34895             this.wrapEl = this.el.wrap();
34896         }
34897     
34898         this.footer.container = this.wrapEl.createChild();
34899          
34900         this.footer = Roo.factory(this.footer, Roo);
34901         
34902     }
34903     */
34904     
34905      if(typeof config == "string"){
34906         this.title = config;
34907     }else{
34908         Roo.apply(this, config);
34909     }
34910     
34911     if(this.resizeEl){
34912         this.resizeEl = Roo.get(this.resizeEl, true);
34913     }else{
34914         this.resizeEl = this.el;
34915     }
34916     // handle view.xtype
34917     
34918  
34919     
34920     
34921     this.addEvents({
34922         /**
34923          * @event activate
34924          * Fires when this panel is activated. 
34925          * @param {Roo.ContentPanel} this
34926          */
34927         "activate" : true,
34928         /**
34929          * @event deactivate
34930          * Fires when this panel is activated. 
34931          * @param {Roo.ContentPanel} this
34932          */
34933         "deactivate" : true,
34934
34935         /**
34936          * @event resize
34937          * Fires when this panel is resized if fitToFrame is true.
34938          * @param {Roo.ContentPanel} this
34939          * @param {Number} width The width after any component adjustments
34940          * @param {Number} height The height after any component adjustments
34941          */
34942         "resize" : true,
34943         
34944          /**
34945          * @event render
34946          * Fires when this tab is created
34947          * @param {Roo.ContentPanel} this
34948          */
34949         "render" : true
34950         
34951         
34952         
34953     });
34954     
34955
34956     
34957     
34958     if(this.autoScroll){
34959         this.resizeEl.setStyle("overflow", "auto");
34960     } else {
34961         // fix randome scrolling
34962         //this.el.on('scroll', function() {
34963         //    Roo.log('fix random scolling');
34964         //    this.scrollTo('top',0); 
34965         //});
34966     }
34967     content = content || this.content;
34968     if(content){
34969         this.setContent(content);
34970     }
34971     if(config && config.url){
34972         this.setUrl(this.url, this.params, this.loadOnce);
34973     }
34974     
34975     
34976     
34977     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34978     
34979     if (this.view && typeof(this.view.xtype) != 'undefined') {
34980         this.view.el = this.el.appendChild(document.createElement("div"));
34981         this.view = Roo.factory(this.view); 
34982         this.view.render  &&  this.view.render(false, '');  
34983     }
34984     
34985     
34986     this.fireEvent('render', this);
34987 };
34988
34989 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34990     
34991     tabTip : '',
34992     
34993     setRegion : function(region){
34994         this.region = region;
34995         this.setActiveClass(region && !this.background);
34996     },
34997     
34998     
34999     setActiveClass: function(state)
35000     {
35001         if(state){
35002            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35003            this.el.setStyle('position','relative');
35004         }else{
35005            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35006            this.el.setStyle('position', 'absolute');
35007         } 
35008     },
35009     
35010     /**
35011      * Returns the toolbar for this Panel if one was configured. 
35012      * @return {Roo.Toolbar} 
35013      */
35014     getToolbar : function(){
35015         return this.toolbar;
35016     },
35017     
35018     setActiveState : function(active)
35019     {
35020         this.active = active;
35021         this.setActiveClass(active);
35022         if(!active){
35023             this.fireEvent("deactivate", this);
35024         }else{
35025             this.fireEvent("activate", this);
35026         }
35027     },
35028     /**
35029      * Updates this panel's element
35030      * @param {String} content The new content
35031      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35032     */
35033     setContent : function(content, loadScripts){
35034         this.el.update(content, loadScripts);
35035     },
35036
35037     ignoreResize : function(w, h){
35038         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35039             return true;
35040         }else{
35041             this.lastSize = {width: w, height: h};
35042             return false;
35043         }
35044     },
35045     /**
35046      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35047      * @return {Roo.UpdateManager} The UpdateManager
35048      */
35049     getUpdateManager : function(){
35050         return this.el.getUpdateManager();
35051     },
35052      /**
35053      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35054      * @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:
35055 <pre><code>
35056 panel.load({
35057     url: "your-url.php",
35058     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35059     callback: yourFunction,
35060     scope: yourObject, //(optional scope)
35061     discardUrl: false,
35062     nocache: false,
35063     text: "Loading...",
35064     timeout: 30,
35065     scripts: false
35066 });
35067 </code></pre>
35068      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35069      * 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.
35070      * @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}
35071      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35072      * @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.
35073      * @return {Roo.ContentPanel} this
35074      */
35075     load : function(){
35076         var um = this.el.getUpdateManager();
35077         um.update.apply(um, arguments);
35078         return this;
35079     },
35080
35081
35082     /**
35083      * 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.
35084      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35085      * @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)
35086      * @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)
35087      * @return {Roo.UpdateManager} The UpdateManager
35088      */
35089     setUrl : function(url, params, loadOnce){
35090         if(this.refreshDelegate){
35091             this.removeListener("activate", this.refreshDelegate);
35092         }
35093         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35094         this.on("activate", this.refreshDelegate);
35095         return this.el.getUpdateManager();
35096     },
35097     
35098     _handleRefresh : function(url, params, loadOnce){
35099         if(!loadOnce || !this.loaded){
35100             var updater = this.el.getUpdateManager();
35101             updater.update(url, params, this._setLoaded.createDelegate(this));
35102         }
35103     },
35104     
35105     _setLoaded : function(){
35106         this.loaded = true;
35107     }, 
35108     
35109     /**
35110      * Returns this panel's id
35111      * @return {String} 
35112      */
35113     getId : function(){
35114         return this.el.id;
35115     },
35116     
35117     /** 
35118      * Returns this panel's element - used by regiosn to add.
35119      * @return {Roo.Element} 
35120      */
35121     getEl : function(){
35122         return this.wrapEl || this.el;
35123     },
35124     
35125    
35126     
35127     adjustForComponents : function(width, height)
35128     {
35129         //Roo.log('adjustForComponents ');
35130         if(this.resizeEl != this.el){
35131             width -= this.el.getFrameWidth('lr');
35132             height -= this.el.getFrameWidth('tb');
35133         }
35134         if(this.toolbar){
35135             var te = this.toolbar.getEl();
35136             height -= te.getHeight();
35137             te.setWidth(width);
35138         }
35139         if(this.footer){
35140             var te = this.footer.getEl();
35141             Roo.log("footer:" + te.getHeight());
35142             
35143             height -= te.getHeight();
35144             te.setWidth(width);
35145         }
35146         
35147         
35148         if(this.adjustments){
35149             width += this.adjustments[0];
35150             height += this.adjustments[1];
35151         }
35152         return {"width": width, "height": height};
35153     },
35154     
35155     setSize : function(width, height){
35156         if(this.fitToFrame && !this.ignoreResize(width, height)){
35157             if(this.fitContainer && this.resizeEl != this.el){
35158                 this.el.setSize(width, height);
35159             }
35160             var size = this.adjustForComponents(width, height);
35161             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35162             this.fireEvent('resize', this, size.width, size.height);
35163         }
35164     },
35165     
35166     /**
35167      * Returns this panel's title
35168      * @return {String} 
35169      */
35170     getTitle : function(){
35171         return this.title;
35172     },
35173     
35174     /**
35175      * Set this panel's title
35176      * @param {String} title
35177      */
35178     setTitle : function(title){
35179         this.title = title;
35180         if(this.region){
35181             this.region.updatePanelTitle(this, title);
35182         }
35183     },
35184     
35185     /**
35186      * Returns true is this panel was configured to be closable
35187      * @return {Boolean} 
35188      */
35189     isClosable : function(){
35190         return this.closable;
35191     },
35192     
35193     beforeSlide : function(){
35194         this.el.clip();
35195         this.resizeEl.clip();
35196     },
35197     
35198     afterSlide : function(){
35199         this.el.unclip();
35200         this.resizeEl.unclip();
35201     },
35202     
35203     /**
35204      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35205      *   Will fail silently if the {@link #setUrl} method has not been called.
35206      *   This does not activate the panel, just updates its content.
35207      */
35208     refresh : function(){
35209         if(this.refreshDelegate){
35210            this.loaded = false;
35211            this.refreshDelegate();
35212         }
35213     },
35214     
35215     /**
35216      * Destroys this panel
35217      */
35218     destroy : function(){
35219         this.el.removeAllListeners();
35220         var tempEl = document.createElement("span");
35221         tempEl.appendChild(this.el.dom);
35222         tempEl.innerHTML = "";
35223         this.el.remove();
35224         this.el = null;
35225     },
35226     
35227     /**
35228      * form - if the content panel contains a form - this is a reference to it.
35229      * @type {Roo.form.Form}
35230      */
35231     form : false,
35232     /**
35233      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35234      *    This contains a reference to it.
35235      * @type {Roo.View}
35236      */
35237     view : false,
35238     
35239       /**
35240      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35241      * <pre><code>
35242
35243 layout.addxtype({
35244        xtype : 'Form',
35245        items: [ .... ]
35246    }
35247 );
35248
35249 </code></pre>
35250      * @param {Object} cfg Xtype definition of item to add.
35251      */
35252     
35253     
35254     getChildContainer: function () {
35255         return this.getEl();
35256     }
35257     
35258     
35259     /*
35260         var  ret = new Roo.factory(cfg);
35261         return ret;
35262         
35263         
35264         // add form..
35265         if (cfg.xtype.match(/^Form$/)) {
35266             
35267             var el;
35268             //if (this.footer) {
35269             //    el = this.footer.container.insertSibling(false, 'before');
35270             //} else {
35271                 el = this.el.createChild();
35272             //}
35273
35274             this.form = new  Roo.form.Form(cfg);
35275             
35276             
35277             if ( this.form.allItems.length) {
35278                 this.form.render(el.dom);
35279             }
35280             return this.form;
35281         }
35282         // should only have one of theses..
35283         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35284             // views.. should not be just added - used named prop 'view''
35285             
35286             cfg.el = this.el.appendChild(document.createElement("div"));
35287             // factory?
35288             
35289             var ret = new Roo.factory(cfg);
35290              
35291              ret.render && ret.render(false, ''); // render blank..
35292             this.view = ret;
35293             return ret;
35294         }
35295         return false;
35296     }
35297     \*/
35298 });
35299  
35300 /**
35301  * @class Roo.bootstrap.panel.Grid
35302  * @extends Roo.bootstrap.panel.Content
35303  * @constructor
35304  * Create a new GridPanel.
35305  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35306  * @param {Object} config A the config object
35307   
35308  */
35309
35310
35311
35312 Roo.bootstrap.panel.Grid = function(config)
35313 {
35314     
35315       
35316     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35317         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35318
35319     config.el = this.wrapper;
35320     //this.el = this.wrapper;
35321     
35322       if (config.container) {
35323         // ctor'ed from a Border/panel.grid
35324         
35325         
35326         this.wrapper.setStyle("overflow", "hidden");
35327         this.wrapper.addClass('roo-grid-container');
35328
35329     }
35330     
35331     
35332     if(config.toolbar){
35333         var tool_el = this.wrapper.createChild();    
35334         this.toolbar = Roo.factory(config.toolbar);
35335         var ti = [];
35336         if (config.toolbar.items) {
35337             ti = config.toolbar.items ;
35338             delete config.toolbar.items ;
35339         }
35340         
35341         var nitems = [];
35342         this.toolbar.render(tool_el);
35343         for(var i =0;i < ti.length;i++) {
35344           //  Roo.log(['add child', items[i]]);
35345             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35346         }
35347         this.toolbar.items = nitems;
35348         
35349         delete config.toolbar;
35350     }
35351     
35352     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35353     config.grid.scrollBody = true;;
35354     config.grid.monitorWindowResize = false; // turn off autosizing
35355     config.grid.autoHeight = false;
35356     config.grid.autoWidth = false;
35357     
35358     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35359     
35360     if (config.background) {
35361         // render grid on panel activation (if panel background)
35362         this.on('activate', function(gp) {
35363             if (!gp.grid.rendered) {
35364                 gp.grid.render(this.wrapper);
35365                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35366             }
35367         });
35368             
35369     } else {
35370         this.grid.render(this.wrapper);
35371         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35372
35373     }
35374     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35375     // ??? needed ??? config.el = this.wrapper;
35376     
35377     
35378     
35379   
35380     // xtype created footer. - not sure if will work as we normally have to render first..
35381     if (this.footer && !this.footer.el && this.footer.xtype) {
35382         
35383         var ctr = this.grid.getView().getFooterPanel(true);
35384         this.footer.dataSource = this.grid.dataSource;
35385         this.footer = Roo.factory(this.footer, Roo);
35386         this.footer.render(ctr);
35387         
35388     }
35389     
35390     
35391     
35392     
35393      
35394 };
35395
35396 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35397     getId : function(){
35398         return this.grid.id;
35399     },
35400     
35401     /**
35402      * Returns the grid for this panel
35403      * @return {Roo.bootstrap.Table} 
35404      */
35405     getGrid : function(){
35406         return this.grid;    
35407     },
35408     
35409     setSize : function(width, height){
35410         if(!this.ignoreResize(width, height)){
35411             var grid = this.grid;
35412             var size = this.adjustForComponents(width, height);
35413             var gridel = grid.getGridEl();
35414             gridel.setSize(size.width, size.height);
35415             /*
35416             var thd = grid.getGridEl().select('thead',true).first();
35417             var tbd = grid.getGridEl().select('tbody', true).first();
35418             if (tbd) {
35419                 tbd.setSize(width, height - thd.getHeight());
35420             }
35421             */
35422             grid.autoSize();
35423         }
35424     },
35425      
35426     
35427     
35428     beforeSlide : function(){
35429         this.grid.getView().scroller.clip();
35430     },
35431     
35432     afterSlide : function(){
35433         this.grid.getView().scroller.unclip();
35434     },
35435     
35436     destroy : function(){
35437         this.grid.destroy();
35438         delete this.grid;
35439         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35440     }
35441 });
35442
35443 /**
35444  * @class Roo.bootstrap.panel.Nest
35445  * @extends Roo.bootstrap.panel.Content
35446  * @constructor
35447  * Create a new Panel, that can contain a layout.Border.
35448  * 
35449  * 
35450  * @param {Roo.BorderLayout} layout The layout for this panel
35451  * @param {String/Object} config A string to set only the title or a config object
35452  */
35453 Roo.bootstrap.panel.Nest = function(config)
35454 {
35455     // construct with only one argument..
35456     /* FIXME - implement nicer consturctors
35457     if (layout.layout) {
35458         config = layout;
35459         layout = config.layout;
35460         delete config.layout;
35461     }
35462     if (layout.xtype && !layout.getEl) {
35463         // then layout needs constructing..
35464         layout = Roo.factory(layout, Roo);
35465     }
35466     */
35467     
35468     config.el =  config.layout.getEl();
35469     
35470     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35471     
35472     config.layout.monitorWindowResize = false; // turn off autosizing
35473     this.layout = config.layout;
35474     this.layout.getEl().addClass("roo-layout-nested-layout");
35475     
35476     
35477     
35478     
35479 };
35480
35481 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35482
35483     setSize : function(width, height){
35484         if(!this.ignoreResize(width, height)){
35485             var size = this.adjustForComponents(width, height);
35486             var el = this.layout.getEl();
35487             if (size.height < 1) {
35488                 el.setWidth(size.width);   
35489             } else {
35490                 el.setSize(size.width, size.height);
35491             }
35492             var touch = el.dom.offsetWidth;
35493             this.layout.layout();
35494             // ie requires a double layout on the first pass
35495             if(Roo.isIE && !this.initialized){
35496                 this.initialized = true;
35497                 this.layout.layout();
35498             }
35499         }
35500     },
35501     
35502     // activate all subpanels if not currently active..
35503     
35504     setActiveState : function(active){
35505         this.active = active;
35506         this.setActiveClass(active);
35507         
35508         if(!active){
35509             this.fireEvent("deactivate", this);
35510             return;
35511         }
35512         
35513         this.fireEvent("activate", this);
35514         // not sure if this should happen before or after..
35515         if (!this.layout) {
35516             return; // should not happen..
35517         }
35518         var reg = false;
35519         for (var r in this.layout.regions) {
35520             reg = this.layout.getRegion(r);
35521             if (reg.getActivePanel()) {
35522                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35523                 reg.setActivePanel(reg.getActivePanel());
35524                 continue;
35525             }
35526             if (!reg.panels.length) {
35527                 continue;
35528             }
35529             reg.showPanel(reg.getPanel(0));
35530         }
35531         
35532         
35533         
35534         
35535     },
35536     
35537     /**
35538      * Returns the nested BorderLayout for this panel
35539      * @return {Roo.BorderLayout} 
35540      */
35541     getLayout : function(){
35542         return this.layout;
35543     },
35544     
35545      /**
35546      * Adds a xtype elements to the layout of the nested panel
35547      * <pre><code>
35548
35549 panel.addxtype({
35550        xtype : 'ContentPanel',
35551        region: 'west',
35552        items: [ .... ]
35553    }
35554 );
35555
35556 panel.addxtype({
35557         xtype : 'NestedLayoutPanel',
35558         region: 'west',
35559         layout: {
35560            center: { },
35561            west: { }   
35562         },
35563         items : [ ... list of content panels or nested layout panels.. ]
35564    }
35565 );
35566 </code></pre>
35567      * @param {Object} cfg Xtype definition of item to add.
35568      */
35569     addxtype : function(cfg) {
35570         return this.layout.addxtype(cfg);
35571     
35572     }
35573 });        /*
35574  * Based on:
35575  * Ext JS Library 1.1.1
35576  * Copyright(c) 2006-2007, Ext JS, LLC.
35577  *
35578  * Originally Released Under LGPL - original licence link has changed is not relivant.
35579  *
35580  * Fork - LGPL
35581  * <script type="text/javascript">
35582  */
35583 /**
35584  * @class Roo.TabPanel
35585  * @extends Roo.util.Observable
35586  * A lightweight tab container.
35587  * <br><br>
35588  * Usage:
35589  * <pre><code>
35590 // basic tabs 1, built from existing content
35591 var tabs = new Roo.TabPanel("tabs1");
35592 tabs.addTab("script", "View Script");
35593 tabs.addTab("markup", "View Markup");
35594 tabs.activate("script");
35595
35596 // more advanced tabs, built from javascript
35597 var jtabs = new Roo.TabPanel("jtabs");
35598 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35599
35600 // set up the UpdateManager
35601 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35602 var updater = tab2.getUpdateManager();
35603 updater.setDefaultUrl("ajax1.htm");
35604 tab2.on('activate', updater.refresh, updater, true);
35605
35606 // Use setUrl for Ajax loading
35607 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35608 tab3.setUrl("ajax2.htm", null, true);
35609
35610 // Disabled tab
35611 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35612 tab4.disable();
35613
35614 jtabs.activate("jtabs-1");
35615  * </code></pre>
35616  * @constructor
35617  * Create a new TabPanel.
35618  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35619  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35620  */
35621 Roo.bootstrap.panel.Tabs = function(config){
35622     /**
35623     * The container element for this TabPanel.
35624     * @type Roo.Element
35625     */
35626     this.el = Roo.get(config.el);
35627     delete config.el;
35628     if(config){
35629         if(typeof config == "boolean"){
35630             this.tabPosition = config ? "bottom" : "top";
35631         }else{
35632             Roo.apply(this, config);
35633         }
35634     }
35635     
35636     if(this.tabPosition == "bottom"){
35637         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35638         this.el.addClass("roo-tabs-bottom");
35639     }
35640     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35641     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35642     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35643     if(Roo.isIE){
35644         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35645     }
35646     if(this.tabPosition != "bottom"){
35647         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35648          * @type Roo.Element
35649          */
35650         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35651         this.el.addClass("roo-tabs-top");
35652     }
35653     this.items = [];
35654
35655     this.bodyEl.setStyle("position", "relative");
35656
35657     this.active = null;
35658     this.activateDelegate = this.activate.createDelegate(this);
35659
35660     this.addEvents({
35661         /**
35662          * @event tabchange
35663          * Fires when the active tab changes
35664          * @param {Roo.TabPanel} this
35665          * @param {Roo.TabPanelItem} activePanel The new active tab
35666          */
35667         "tabchange": true,
35668         /**
35669          * @event beforetabchange
35670          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35671          * @param {Roo.TabPanel} this
35672          * @param {Object} e Set cancel to true on this object to cancel the tab change
35673          * @param {Roo.TabPanelItem} tab The tab being changed to
35674          */
35675         "beforetabchange" : true
35676     });
35677
35678     Roo.EventManager.onWindowResize(this.onResize, this);
35679     this.cpad = this.el.getPadding("lr");
35680     this.hiddenCount = 0;
35681
35682
35683     // toolbar on the tabbar support...
35684     if (this.toolbar) {
35685         alert("no toolbar support yet");
35686         this.toolbar  = false;
35687         /*
35688         var tcfg = this.toolbar;
35689         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35690         this.toolbar = new Roo.Toolbar(tcfg);
35691         if (Roo.isSafari) {
35692             var tbl = tcfg.container.child('table', true);
35693             tbl.setAttribute('width', '100%');
35694         }
35695         */
35696         
35697     }
35698    
35699
35700
35701     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35702 };
35703
35704 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35705     /*
35706      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35707      */
35708     tabPosition : "top",
35709     /*
35710      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35711      */
35712     currentTabWidth : 0,
35713     /*
35714      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35715      */
35716     minTabWidth : 40,
35717     /*
35718      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35719      */
35720     maxTabWidth : 250,
35721     /*
35722      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35723      */
35724     preferredTabWidth : 175,
35725     /*
35726      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35727      */
35728     resizeTabs : false,
35729     /*
35730      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35731      */
35732     monitorResize : true,
35733     /*
35734      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35735      */
35736     toolbar : false,
35737
35738     /**
35739      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35740      * @param {String} id The id of the div to use <b>or create</b>
35741      * @param {String} text The text for the tab
35742      * @param {String} content (optional) Content to put in the TabPanelItem body
35743      * @param {Boolean} closable (optional) True to create a close icon on the tab
35744      * @return {Roo.TabPanelItem} The created TabPanelItem
35745      */
35746     addTab : function(id, text, content, closable, tpl)
35747     {
35748         var item = new Roo.bootstrap.panel.TabItem({
35749             panel: this,
35750             id : id,
35751             text : text,
35752             closable : closable,
35753             tpl : tpl
35754         });
35755         this.addTabItem(item);
35756         if(content){
35757             item.setContent(content);
35758         }
35759         return item;
35760     },
35761
35762     /**
35763      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35764      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35765      * @return {Roo.TabPanelItem}
35766      */
35767     getTab : function(id){
35768         return this.items[id];
35769     },
35770
35771     /**
35772      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35773      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35774      */
35775     hideTab : function(id){
35776         var t = this.items[id];
35777         if(!t.isHidden()){
35778            t.setHidden(true);
35779            this.hiddenCount++;
35780            this.autoSizeTabs();
35781         }
35782     },
35783
35784     /**
35785      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35786      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35787      */
35788     unhideTab : function(id){
35789         var t = this.items[id];
35790         if(t.isHidden()){
35791            t.setHidden(false);
35792            this.hiddenCount--;
35793            this.autoSizeTabs();
35794         }
35795     },
35796
35797     /**
35798      * Adds an existing {@link Roo.TabPanelItem}.
35799      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35800      */
35801     addTabItem : function(item){
35802         this.items[item.id] = item;
35803         this.items.push(item);
35804       //  if(this.resizeTabs){
35805     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35806   //         this.autoSizeTabs();
35807 //        }else{
35808 //            item.autoSize();
35809        // }
35810     },
35811
35812     /**
35813      * Removes a {@link Roo.TabPanelItem}.
35814      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35815      */
35816     removeTab : function(id){
35817         var items = this.items;
35818         var tab = items[id];
35819         if(!tab) { return; }
35820         var index = items.indexOf(tab);
35821         if(this.active == tab && items.length > 1){
35822             var newTab = this.getNextAvailable(index);
35823             if(newTab) {
35824                 newTab.activate();
35825             }
35826         }
35827         this.stripEl.dom.removeChild(tab.pnode.dom);
35828         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35829             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35830         }
35831         items.splice(index, 1);
35832         delete this.items[tab.id];
35833         tab.fireEvent("close", tab);
35834         tab.purgeListeners();
35835         this.autoSizeTabs();
35836     },
35837
35838     getNextAvailable : function(start){
35839         var items = this.items;
35840         var index = start;
35841         // look for a next tab that will slide over to
35842         // replace the one being removed
35843         while(index < items.length){
35844             var item = items[++index];
35845             if(item && !item.isHidden()){
35846                 return item;
35847             }
35848         }
35849         // if one isn't found select the previous tab (on the left)
35850         index = start;
35851         while(index >= 0){
35852             var item = items[--index];
35853             if(item && !item.isHidden()){
35854                 return item;
35855             }
35856         }
35857         return null;
35858     },
35859
35860     /**
35861      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35862      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35863      */
35864     disableTab : function(id){
35865         var tab = this.items[id];
35866         if(tab && this.active != tab){
35867             tab.disable();
35868         }
35869     },
35870
35871     /**
35872      * Enables a {@link Roo.TabPanelItem} that is disabled.
35873      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35874      */
35875     enableTab : function(id){
35876         var tab = this.items[id];
35877         tab.enable();
35878     },
35879
35880     /**
35881      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35882      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35883      * @return {Roo.TabPanelItem} The TabPanelItem.
35884      */
35885     activate : function(id){
35886         var tab = this.items[id];
35887         if(!tab){
35888             return null;
35889         }
35890         if(tab == this.active || tab.disabled){
35891             return tab;
35892         }
35893         var e = {};
35894         this.fireEvent("beforetabchange", this, e, tab);
35895         if(e.cancel !== true && !tab.disabled){
35896             if(this.active){
35897                 this.active.hide();
35898             }
35899             this.active = this.items[id];
35900             this.active.show();
35901             this.fireEvent("tabchange", this, this.active);
35902         }
35903         return tab;
35904     },
35905
35906     /**
35907      * Gets the active {@link Roo.TabPanelItem}.
35908      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35909      */
35910     getActiveTab : function(){
35911         return this.active;
35912     },
35913
35914     /**
35915      * Updates the tab body element to fit the height of the container element
35916      * for overflow scrolling
35917      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35918      */
35919     syncHeight : function(targetHeight){
35920         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35921         var bm = this.bodyEl.getMargins();
35922         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35923         this.bodyEl.setHeight(newHeight);
35924         return newHeight;
35925     },
35926
35927     onResize : function(){
35928         if(this.monitorResize){
35929             this.autoSizeTabs();
35930         }
35931     },
35932
35933     /**
35934      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35935      */
35936     beginUpdate : function(){
35937         this.updating = true;
35938     },
35939
35940     /**
35941      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35942      */
35943     endUpdate : function(){
35944         this.updating = false;
35945         this.autoSizeTabs();
35946     },
35947
35948     /**
35949      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35950      */
35951     autoSizeTabs : function(){
35952         var count = this.items.length;
35953         var vcount = count - this.hiddenCount;
35954         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35955             return;
35956         }
35957         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35958         var availWidth = Math.floor(w / vcount);
35959         var b = this.stripBody;
35960         if(b.getWidth() > w){
35961             var tabs = this.items;
35962             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35963             if(availWidth < this.minTabWidth){
35964                 /*if(!this.sleft){    // incomplete scrolling code
35965                     this.createScrollButtons();
35966                 }
35967                 this.showScroll();
35968                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35969             }
35970         }else{
35971             if(this.currentTabWidth < this.preferredTabWidth){
35972                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35973             }
35974         }
35975     },
35976
35977     /**
35978      * Returns the number of tabs in this TabPanel.
35979      * @return {Number}
35980      */
35981      getCount : function(){
35982          return this.items.length;
35983      },
35984
35985     /**
35986      * Resizes all the tabs to the passed width
35987      * @param {Number} The new width
35988      */
35989     setTabWidth : function(width){
35990         this.currentTabWidth = width;
35991         for(var i = 0, len = this.items.length; i < len; i++) {
35992                 if(!this.items[i].isHidden()) {
35993                 this.items[i].setWidth(width);
35994             }
35995         }
35996     },
35997
35998     /**
35999      * Destroys this TabPanel
36000      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36001      */
36002     destroy : function(removeEl){
36003         Roo.EventManager.removeResizeListener(this.onResize, this);
36004         for(var i = 0, len = this.items.length; i < len; i++){
36005             this.items[i].purgeListeners();
36006         }
36007         if(removeEl === true){
36008             this.el.update("");
36009             this.el.remove();
36010         }
36011     },
36012     
36013     createStrip : function(container)
36014     {
36015         var strip = document.createElement("nav");
36016         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36017         container.appendChild(strip);
36018         return strip;
36019     },
36020     
36021     createStripList : function(strip)
36022     {
36023         // div wrapper for retard IE
36024         // returns the "tr" element.
36025         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36026         //'<div class="x-tabs-strip-wrap">'+
36027           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36028           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36029         return strip.firstChild; //.firstChild.firstChild.firstChild;
36030     },
36031     createBody : function(container)
36032     {
36033         var body = document.createElement("div");
36034         Roo.id(body, "tab-body");
36035         //Roo.fly(body).addClass("x-tabs-body");
36036         Roo.fly(body).addClass("tab-content");
36037         container.appendChild(body);
36038         return body;
36039     },
36040     createItemBody :function(bodyEl, id){
36041         var body = Roo.getDom(id);
36042         if(!body){
36043             body = document.createElement("div");
36044             body.id = id;
36045         }
36046         //Roo.fly(body).addClass("x-tabs-item-body");
36047         Roo.fly(body).addClass("tab-pane");
36048          bodyEl.insertBefore(body, bodyEl.firstChild);
36049         return body;
36050     },
36051     /** @private */
36052     createStripElements :  function(stripEl, text, closable, tpl)
36053     {
36054         var td = document.createElement("li"); // was td..
36055         
36056         
36057         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36058         
36059         
36060         stripEl.appendChild(td);
36061         /*if(closable){
36062             td.className = "x-tabs-closable";
36063             if(!this.closeTpl){
36064                 this.closeTpl = new Roo.Template(
36065                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36066                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36067                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36068                 );
36069             }
36070             var el = this.closeTpl.overwrite(td, {"text": text});
36071             var close = el.getElementsByTagName("div")[0];
36072             var inner = el.getElementsByTagName("em")[0];
36073             return {"el": el, "close": close, "inner": inner};
36074         } else {
36075         */
36076         // not sure what this is..
36077 //            if(!this.tabTpl){
36078                 //this.tabTpl = new Roo.Template(
36079                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36080                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36081                 //);
36082 //                this.tabTpl = new Roo.Template(
36083 //                   '<a href="#">' +
36084 //                   '<span unselectable="on"' +
36085 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36086 //                            ' >{text}</span></a>'
36087 //                );
36088 //                
36089 //            }
36090
36091
36092             var template = tpl || this.tabTpl || false;
36093             
36094             if(!template){
36095                 
36096                 template = new Roo.Template(
36097                    '<a href="#">' +
36098                    '<span unselectable="on"' +
36099                             (this.disableTooltips ? '' : ' title="{text}"') +
36100                             ' >{text}</span></a>'
36101                 );
36102             }
36103             
36104             switch (typeof(template)) {
36105                 case 'object' :
36106                     break;
36107                 case 'string' :
36108                     template = new Roo.Template(template);
36109                     break;
36110                 default :
36111                     break;
36112             }
36113             
36114             var el = template.overwrite(td, {"text": text});
36115             
36116             var inner = el.getElementsByTagName("span")[0];
36117             
36118             return {"el": el, "inner": inner};
36119             
36120     }
36121         
36122     
36123 });
36124
36125 /**
36126  * @class Roo.TabPanelItem
36127  * @extends Roo.util.Observable
36128  * Represents an individual item (tab plus body) in a TabPanel.
36129  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36130  * @param {String} id The id of this TabPanelItem
36131  * @param {String} text The text for the tab of this TabPanelItem
36132  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36133  */
36134 Roo.bootstrap.panel.TabItem = function(config){
36135     /**
36136      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36137      * @type Roo.TabPanel
36138      */
36139     this.tabPanel = config.panel;
36140     /**
36141      * The id for this TabPanelItem
36142      * @type String
36143      */
36144     this.id = config.id;
36145     /** @private */
36146     this.disabled = false;
36147     /** @private */
36148     this.text = config.text;
36149     /** @private */
36150     this.loaded = false;
36151     this.closable = config.closable;
36152
36153     /**
36154      * The body element for this TabPanelItem.
36155      * @type Roo.Element
36156      */
36157     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36158     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36159     this.bodyEl.setStyle("display", "block");
36160     this.bodyEl.setStyle("zoom", "1");
36161     //this.hideAction();
36162
36163     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36164     /** @private */
36165     this.el = Roo.get(els.el);
36166     this.inner = Roo.get(els.inner, true);
36167     this.textEl = Roo.get(this.el.dom.firstChild, true);
36168     this.pnode = Roo.get(els.el.parentNode, true);
36169     this.el.on("mousedown", this.onTabMouseDown, this);
36170     this.el.on("click", this.onTabClick, this);
36171     /** @private */
36172     if(config.closable){
36173         var c = Roo.get(els.close, true);
36174         c.dom.title = this.closeText;
36175         c.addClassOnOver("close-over");
36176         c.on("click", this.closeClick, this);
36177      }
36178
36179     this.addEvents({
36180          /**
36181          * @event activate
36182          * Fires when this tab becomes the active tab.
36183          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36184          * @param {Roo.TabPanelItem} this
36185          */
36186         "activate": true,
36187         /**
36188          * @event beforeclose
36189          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36190          * @param {Roo.TabPanelItem} this
36191          * @param {Object} e Set cancel to true on this object to cancel the close.
36192          */
36193         "beforeclose": true,
36194         /**
36195          * @event close
36196          * Fires when this tab is closed.
36197          * @param {Roo.TabPanelItem} this
36198          */
36199          "close": true,
36200         /**
36201          * @event deactivate
36202          * Fires when this tab is no longer the active tab.
36203          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36204          * @param {Roo.TabPanelItem} this
36205          */
36206          "deactivate" : true
36207     });
36208     this.hidden = false;
36209
36210     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36211 };
36212
36213 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36214            {
36215     purgeListeners : function(){
36216        Roo.util.Observable.prototype.purgeListeners.call(this);
36217        this.el.removeAllListeners();
36218     },
36219     /**
36220      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36221      */
36222     show : function(){
36223         this.pnode.addClass("active");
36224         this.showAction();
36225         if(Roo.isOpera){
36226             this.tabPanel.stripWrap.repaint();
36227         }
36228         this.fireEvent("activate", this.tabPanel, this);
36229     },
36230
36231     /**
36232      * Returns true if this tab is the active tab.
36233      * @return {Boolean}
36234      */
36235     isActive : function(){
36236         return this.tabPanel.getActiveTab() == this;
36237     },
36238
36239     /**
36240      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36241      */
36242     hide : function(){
36243         this.pnode.removeClass("active");
36244         this.hideAction();
36245         this.fireEvent("deactivate", this.tabPanel, this);
36246     },
36247
36248     hideAction : function(){
36249         this.bodyEl.hide();
36250         this.bodyEl.setStyle("position", "absolute");
36251         this.bodyEl.setLeft("-20000px");
36252         this.bodyEl.setTop("-20000px");
36253     },
36254
36255     showAction : function(){
36256         this.bodyEl.setStyle("position", "relative");
36257         this.bodyEl.setTop("");
36258         this.bodyEl.setLeft("");
36259         this.bodyEl.show();
36260     },
36261
36262     /**
36263      * Set the tooltip for the tab.
36264      * @param {String} tooltip The tab's tooltip
36265      */
36266     setTooltip : function(text){
36267         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36268             this.textEl.dom.qtip = text;
36269             this.textEl.dom.removeAttribute('title');
36270         }else{
36271             this.textEl.dom.title = text;
36272         }
36273     },
36274
36275     onTabClick : function(e){
36276         e.preventDefault();
36277         this.tabPanel.activate(this.id);
36278     },
36279
36280     onTabMouseDown : function(e){
36281         e.preventDefault();
36282         this.tabPanel.activate(this.id);
36283     },
36284 /*
36285     getWidth : function(){
36286         return this.inner.getWidth();
36287     },
36288
36289     setWidth : function(width){
36290         var iwidth = width - this.pnode.getPadding("lr");
36291         this.inner.setWidth(iwidth);
36292         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36293         this.pnode.setWidth(width);
36294     },
36295 */
36296     /**
36297      * Show or hide the tab
36298      * @param {Boolean} hidden True to hide or false to show.
36299      */
36300     setHidden : function(hidden){
36301         this.hidden = hidden;
36302         this.pnode.setStyle("display", hidden ? "none" : "");
36303     },
36304
36305     /**
36306      * Returns true if this tab is "hidden"
36307      * @return {Boolean}
36308      */
36309     isHidden : function(){
36310         return this.hidden;
36311     },
36312
36313     /**
36314      * Returns the text for this tab
36315      * @return {String}
36316      */
36317     getText : function(){
36318         return this.text;
36319     },
36320     /*
36321     autoSize : function(){
36322         //this.el.beginMeasure();
36323         this.textEl.setWidth(1);
36324         /*
36325          *  #2804 [new] Tabs in Roojs
36326          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36327          */
36328         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36329         //this.el.endMeasure();
36330     //},
36331
36332     /**
36333      * Sets the text for the tab (Note: this also sets the tooltip text)
36334      * @param {String} text The tab's text and tooltip
36335      */
36336     setText : function(text){
36337         this.text = text;
36338         this.textEl.update(text);
36339         this.setTooltip(text);
36340         //if(!this.tabPanel.resizeTabs){
36341         //    this.autoSize();
36342         //}
36343     },
36344     /**
36345      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36346      */
36347     activate : function(){
36348         this.tabPanel.activate(this.id);
36349     },
36350
36351     /**
36352      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36353      */
36354     disable : function(){
36355         if(this.tabPanel.active != this){
36356             this.disabled = true;
36357             this.pnode.addClass("disabled");
36358         }
36359     },
36360
36361     /**
36362      * Enables this TabPanelItem if it was previously disabled.
36363      */
36364     enable : function(){
36365         this.disabled = false;
36366         this.pnode.removeClass("disabled");
36367     },
36368
36369     /**
36370      * Sets the content for this TabPanelItem.
36371      * @param {String} content The content
36372      * @param {Boolean} loadScripts true to look for and load scripts
36373      */
36374     setContent : function(content, loadScripts){
36375         this.bodyEl.update(content, loadScripts);
36376     },
36377
36378     /**
36379      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36380      * @return {Roo.UpdateManager} The UpdateManager
36381      */
36382     getUpdateManager : function(){
36383         return this.bodyEl.getUpdateManager();
36384     },
36385
36386     /**
36387      * Set a URL to be used to load the content for this TabPanelItem.
36388      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36389      * @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)
36390      * @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)
36391      * @return {Roo.UpdateManager} The UpdateManager
36392      */
36393     setUrl : function(url, params, loadOnce){
36394         if(this.refreshDelegate){
36395             this.un('activate', this.refreshDelegate);
36396         }
36397         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36398         this.on("activate", this.refreshDelegate);
36399         return this.bodyEl.getUpdateManager();
36400     },
36401
36402     /** @private */
36403     _handleRefresh : function(url, params, loadOnce){
36404         if(!loadOnce || !this.loaded){
36405             var updater = this.bodyEl.getUpdateManager();
36406             updater.update(url, params, this._setLoaded.createDelegate(this));
36407         }
36408     },
36409
36410     /**
36411      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36412      *   Will fail silently if the setUrl method has not been called.
36413      *   This does not activate the panel, just updates its content.
36414      */
36415     refresh : function(){
36416         if(this.refreshDelegate){
36417            this.loaded = false;
36418            this.refreshDelegate();
36419         }
36420     },
36421
36422     /** @private */
36423     _setLoaded : function(){
36424         this.loaded = true;
36425     },
36426
36427     /** @private */
36428     closeClick : function(e){
36429         var o = {};
36430         e.stopEvent();
36431         this.fireEvent("beforeclose", this, o);
36432         if(o.cancel !== true){
36433             this.tabPanel.removeTab(this.id);
36434         }
36435     },
36436     /**
36437      * The text displayed in the tooltip for the close icon.
36438      * @type String
36439      */
36440     closeText : "Close this tab"
36441 });