Roo/bootstrap/ComboBox.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         
1528         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529             this.el.dom.src =  url;
1530             return;
1531         }
1532         
1533         this.el.select('img', true).first().dom.src =  url;
1534     }
1535     
1536     
1537    
1538 });
1539
1540  /*
1541  * - LGPL
1542  *
1543  * image
1544  * 
1545  */
1546
1547
1548 /**
1549  * @class Roo.bootstrap.Link
1550  * @extends Roo.bootstrap.Component
1551  * Bootstrap Link Class
1552  * @cfg {String} alt image alternative text
1553  * @cfg {String} href a tag href
1554  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555  * @cfg {String} html the content of the link.
1556  * @cfg {String} anchor name for the anchor link
1557  * @cfg {String} fa - favicon
1558
1559  * @cfg {Boolean} preventDefault (true | false) default false
1560
1561  * 
1562  * @constructor
1563  * Create a new Input
1564  * @param {Object} config The config object
1565  */
1566
1567 Roo.bootstrap.Link = function(config){
1568     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1569     
1570     this.addEvents({
1571         // img events
1572         /**
1573          * @event click
1574          * The img click event for the img.
1575          * @param {Roo.EventObject} e
1576          */
1577         "click" : true
1578     });
1579 };
1580
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1582     
1583     href: false,
1584     target: false,
1585     preventDefault: false,
1586     anchor : false,
1587     alt : false,
1588     fa: false,
1589
1590
1591     getAutoCreate : function()
1592     {
1593         var html = this.html || '';
1594         
1595         if (this.fa !== false) {
1596             html = '<i class="fa fa-' + this.fa + '"></i>';
1597         }
1598         var cfg = {
1599             tag: 'a'
1600         };
1601         // anchor's do not require html/href...
1602         if (this.anchor === false) {
1603             cfg.html = html;
1604             cfg.href = this.href || '#';
1605         } else {
1606             cfg.name = this.anchor;
1607             if (this.html !== false || this.fa !== false) {
1608                 cfg.html = html;
1609             }
1610             if (this.href !== false) {
1611                 cfg.href = this.href;
1612             }
1613         }
1614         
1615         if(this.alt !== false){
1616             cfg.alt = this.alt;
1617         }
1618         
1619         
1620         if(this.target !== false) {
1621             cfg.target = this.target;
1622         }
1623         
1624         return cfg;
1625     },
1626     
1627     initEvents: function() {
1628         
1629         if(!this.href || this.preventDefault){
1630             this.el.on('click', this.onClick, this);
1631         }
1632     },
1633     
1634     onClick : function(e)
1635     {
1636         if(this.preventDefault){
1637             e.preventDefault();
1638         }
1639         //Roo.log('img onclick');
1640         this.fireEvent('click', this, e);
1641     }
1642    
1643 });
1644
1645  /*
1646  * - LGPL
1647  *
1648  * header
1649  * 
1650  */
1651
1652 /**
1653  * @class Roo.bootstrap.Header
1654  * @extends Roo.bootstrap.Component
1655  * Bootstrap Header class
1656  * @cfg {String} html content of header
1657  * @cfg {Number} level (1|2|3|4|5|6) default 1
1658  * 
1659  * @constructor
1660  * Create a new Header
1661  * @param {Object} config The config object
1662  */
1663
1664
1665 Roo.bootstrap.Header  = function(config){
1666     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1667 };
1668
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1670     
1671     //href : false,
1672     html : false,
1673     level : 1,
1674     
1675     
1676     
1677     getAutoCreate : function(){
1678         
1679         
1680         
1681         var cfg = {
1682             tag: 'h' + (1 *this.level),
1683             html: this.html || ''
1684         } ;
1685         
1686         return cfg;
1687     }
1688    
1689 });
1690
1691  
1692
1693  /*
1694  * Based on:
1695  * Ext JS Library 1.1.1
1696  * Copyright(c) 2006-2007, Ext JS, LLC.
1697  *
1698  * Originally Released Under LGPL - original licence link has changed is not relivant.
1699  *
1700  * Fork - LGPL
1701  * <script type="text/javascript">
1702  */
1703  
1704 /**
1705  * @class Roo.bootstrap.MenuMgr
1706  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1707  * @singleton
1708  */
1709 Roo.bootstrap.MenuMgr = function(){
1710    var menus, active, groups = {}, attached = false, lastShow = new Date();
1711
1712    // private - called when first menu is created
1713    function init(){
1714        menus = {};
1715        active = new Roo.util.MixedCollection();
1716        Roo.get(document).addKeyListener(27, function(){
1717            if(active.length > 0){
1718                hideAll();
1719            }
1720        });
1721    }
1722
1723    // private
1724    function hideAll(){
1725        if(active && active.length > 0){
1726            var c = active.clone();
1727            c.each(function(m){
1728                m.hide();
1729            });
1730        }
1731    }
1732
1733    // private
1734    function onHide(m){
1735        active.remove(m);
1736        if(active.length < 1){
1737            Roo.get(document).un("mouseup", onMouseDown);
1738             
1739            attached = false;
1740        }
1741    }
1742
1743    // private
1744    function onShow(m){
1745        var last = active.last();
1746        lastShow = new Date();
1747        active.add(m);
1748        if(!attached){
1749           Roo.get(document).on("mouseup", onMouseDown);
1750            
1751            attached = true;
1752        }
1753        if(m.parentMenu){
1754           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755           m.parentMenu.activeChild = m;
1756        }else if(last && last.isVisible()){
1757           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1758        }
1759    }
1760
1761    // private
1762    function onBeforeHide(m){
1763        if(m.activeChild){
1764            m.activeChild.hide();
1765        }
1766        if(m.autoHideTimer){
1767            clearTimeout(m.autoHideTimer);
1768            delete m.autoHideTimer;
1769        }
1770    }
1771
1772    // private
1773    function onBeforeShow(m){
1774        var pm = m.parentMenu;
1775        if(!pm && !m.allowOtherMenus){
1776            hideAll();
1777        }else if(pm && pm.activeChild && active != m){
1778            pm.activeChild.hide();
1779        }
1780    }
1781
1782    // private this should really trigger on mouseup..
1783    function onMouseDown(e){
1784         Roo.log("on Mouse Up");
1785         
1786         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787             Roo.log("MenuManager hideAll");
1788             hideAll();
1789             e.stopEvent();
1790         }
1791         
1792         
1793    }
1794
1795    // private
1796    function onBeforeCheck(mi, state){
1797        if(state){
1798            var g = groups[mi.group];
1799            for(var i = 0, l = g.length; i < l; i++){
1800                if(g[i] != mi){
1801                    g[i].setChecked(false);
1802                }
1803            }
1804        }
1805    }
1806
1807    return {
1808
1809        /**
1810         * Hides all menus that are currently visible
1811         */
1812        hideAll : function(){
1813             hideAll();  
1814        },
1815
1816        // private
1817        register : function(menu){
1818            if(!menus){
1819                init();
1820            }
1821            menus[menu.id] = menu;
1822            menu.on("beforehide", onBeforeHide);
1823            menu.on("hide", onHide);
1824            menu.on("beforeshow", onBeforeShow);
1825            menu.on("show", onShow);
1826            var g = menu.group;
1827            if(g && menu.events["checkchange"]){
1828                if(!groups[g]){
1829                    groups[g] = [];
1830                }
1831                groups[g].push(menu);
1832                menu.on("checkchange", onCheck);
1833            }
1834        },
1835
1836         /**
1837          * Returns a {@link Roo.menu.Menu} object
1838          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839          * be used to generate and return a new Menu instance.
1840          */
1841        get : function(menu){
1842            if(typeof menu == "string"){ // menu id
1843                return menus[menu];
1844            }else if(menu.events){  // menu instance
1845                return menu;
1846            }
1847            /*else if(typeof menu.length == 'number'){ // array of menu items?
1848                return new Roo.bootstrap.Menu({items:menu});
1849            }else{ // otherwise, must be a config
1850                return new Roo.bootstrap.Menu(menu);
1851            }
1852            */
1853            return false;
1854        },
1855
1856        // private
1857        unregister : function(menu){
1858            delete menus[menu.id];
1859            menu.un("beforehide", onBeforeHide);
1860            menu.un("hide", onHide);
1861            menu.un("beforeshow", onBeforeShow);
1862            menu.un("show", onShow);
1863            var g = menu.group;
1864            if(g && menu.events["checkchange"]){
1865                groups[g].remove(menu);
1866                menu.un("checkchange", onCheck);
1867            }
1868        },
1869
1870        // private
1871        registerCheckable : function(menuItem){
1872            var g = menuItem.group;
1873            if(g){
1874                if(!groups[g]){
1875                    groups[g] = [];
1876                }
1877                groups[g].push(menuItem);
1878                menuItem.on("beforecheckchange", onBeforeCheck);
1879            }
1880        },
1881
1882        // private
1883        unregisterCheckable : function(menuItem){
1884            var g = menuItem.group;
1885            if(g){
1886                groups[g].remove(menuItem);
1887                menuItem.un("beforecheckchange", onBeforeCheck);
1888            }
1889        }
1890    };
1891 }();/*
1892  * - LGPL
1893  *
1894  * menu
1895  * 
1896  */
1897
1898 /**
1899  * @class Roo.bootstrap.Menu
1900  * @extends Roo.bootstrap.Component
1901  * Bootstrap Menu class - container for MenuItems
1902  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1904  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1905  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1906  * 
1907  * @constructor
1908  * Create a new Menu
1909  * @param {Object} config The config object
1910  */
1911
1912
1913 Roo.bootstrap.Menu = function(config){
1914     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915     if (this.registerMenu && this.type != 'treeview')  {
1916         Roo.bootstrap.MenuMgr.register(this);
1917     }
1918     this.addEvents({
1919         /**
1920          * @event beforeshow
1921          * Fires before this menu is displayed
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforeshow : true,
1925         /**
1926          * @event beforehide
1927          * Fires before this menu is hidden
1928          * @param {Roo.menu.Menu} this
1929          */
1930         beforehide : true,
1931         /**
1932          * @event show
1933          * Fires after this menu is displayed
1934          * @param {Roo.menu.Menu} this
1935          */
1936         show : true,
1937         /**
1938          * @event hide
1939          * Fires after this menu is hidden
1940          * @param {Roo.menu.Menu} this
1941          */
1942         hide : true,
1943         /**
1944          * @event click
1945          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946          * @param {Roo.menu.Menu} this
1947          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948          * @param {Roo.EventObject} e
1949          */
1950         click : true,
1951         /**
1952          * @event mouseover
1953          * Fires when the mouse is hovering over this menu
1954          * @param {Roo.menu.Menu} this
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          */
1958         mouseover : true,
1959         /**
1960          * @event mouseout
1961          * Fires when the mouse exits this menu
1962          * @param {Roo.menu.Menu} this
1963          * @param {Roo.EventObject} e
1964          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1965          */
1966         mouseout : true,
1967         /**
1968          * @event itemclick
1969          * Fires when a menu item contained in this menu is clicked
1970          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971          * @param {Roo.EventObject} e
1972          */
1973         itemclick: true
1974     });
1975     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1976 };
1977
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1979     
1980    /// html : false,
1981     //align : '',
1982     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1983     type: false,
1984     /**
1985      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1986      */
1987     registerMenu : true,
1988     
1989     menuItems :false, // stores the menu items..
1990     
1991     hidden:true,
1992         
1993     parentMenu : false,
1994     
1995     stopEvent : true,
1996     
1997     isLink : false,
1998     
1999     getChildContainer : function() {
2000         return this.el;  
2001     },
2002     
2003     getAutoCreate : function(){
2004          
2005         //if (['right'].indexOf(this.align)!==-1) {
2006         //    cfg.cn[1].cls += ' pull-right'
2007         //}
2008         
2009         
2010         var cfg = {
2011             tag : 'ul',
2012             cls : 'dropdown-menu' ,
2013             style : 'z-index:1000'
2014             
2015         };
2016         
2017         if (this.type === 'submenu') {
2018             cfg.cls = 'submenu active';
2019         }
2020         if (this.type === 'treeview') {
2021             cfg.cls = 'treeview-menu';
2022         }
2023         
2024         return cfg;
2025     },
2026     initEvents : function() {
2027         
2028        // Roo.log("ADD event");
2029        // Roo.log(this.triggerEl.dom);
2030         
2031         this.triggerEl.on('click', this.onTriggerClick, this);
2032         
2033         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2034         
2035         this.triggerEl.addClass('dropdown-toggle');
2036         
2037         if (Roo.isTouch) {
2038             this.el.on('touchstart'  , this.onTouch, this);
2039         }
2040         this.el.on('click' , this.onClick, this);
2041
2042         this.el.on("mouseover", this.onMouseOver, this);
2043         this.el.on("mouseout", this.onMouseOut, this);
2044         
2045     },
2046     
2047     findTargetItem : function(e)
2048     {
2049         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2050         if(!t){
2051             return false;
2052         }
2053         //Roo.log(t);         Roo.log(t.id);
2054         if(t && t.id){
2055             //Roo.log(this.menuitems);
2056             return this.menuitems.get(t.id);
2057             
2058             //return this.items.get(t.menuItemId);
2059         }
2060         
2061         return false;
2062     },
2063     
2064     onTouch : function(e) 
2065     {
2066         Roo.log("menu.onTouch");
2067         //e.stopEvent(); this make the user popdown broken
2068         this.onClick(e);
2069     },
2070     
2071     onClick : function(e)
2072     {
2073         Roo.log("menu.onClick");
2074         
2075         var t = this.findTargetItem(e);
2076         if(!t || t.isContainer){
2077             return;
2078         }
2079         Roo.log(e);
2080         /*
2081         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2082             if(t == this.activeItem && t.shouldDeactivate(e)){
2083                 this.activeItem.deactivate();
2084                 delete this.activeItem;
2085                 return;
2086             }
2087             if(t.canActivate){
2088                 this.setActiveItem(t, true);
2089             }
2090             return;
2091             
2092             
2093         }
2094         */
2095        
2096         Roo.log('pass click event');
2097         
2098         t.onClick(e);
2099         
2100         this.fireEvent("click", this, t, e);
2101         
2102         var _this = this;
2103         
2104         (function() { _this.hide(); }).defer(100);
2105     },
2106     
2107     onMouseOver : function(e){
2108         var t  = this.findTargetItem(e);
2109         //Roo.log(t);
2110         //if(t){
2111         //    if(t.canActivate && !t.disabled){
2112         //        this.setActiveItem(t, true);
2113         //    }
2114         //}
2115         
2116         this.fireEvent("mouseover", this, e, t);
2117     },
2118     isVisible : function(){
2119         return !this.hidden;
2120     },
2121      onMouseOut : function(e){
2122         var t  = this.findTargetItem(e);
2123         
2124         //if(t ){
2125         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2126         //        this.activeItem.deactivate();
2127         //        delete this.activeItem;
2128         //    }
2129         //}
2130         this.fireEvent("mouseout", this, e, t);
2131     },
2132     
2133     
2134     /**
2135      * Displays this menu relative to another element
2136      * @param {String/HTMLElement/Roo.Element} element The element to align to
2137      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138      * the element (defaults to this.defaultAlign)
2139      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2140      */
2141     show : function(el, pos, parentMenu){
2142         this.parentMenu = parentMenu;
2143         if(!this.el){
2144             this.render();
2145         }
2146         this.fireEvent("beforeshow", this);
2147         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2148     },
2149      /**
2150      * Displays this menu at a specific xy position
2151      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2153      */
2154     showAt : function(xy, parentMenu, /* private: */_e){
2155         this.parentMenu = parentMenu;
2156         if(!this.el){
2157             this.render();
2158         }
2159         if(_e !== false){
2160             this.fireEvent("beforeshow", this);
2161             //xy = this.el.adjustForConstraints(xy);
2162         }
2163         
2164         //this.el.show();
2165         this.hideMenuItems();
2166         this.hidden = false;
2167         this.triggerEl.addClass('open');
2168         
2169         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2171         }
2172         
2173         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2174             this.el.setXY(xy);
2175         }
2176         
2177         this.focus();
2178         this.fireEvent("show", this);
2179     },
2180     
2181     focus : function(){
2182         return;
2183         if(!this.hidden){
2184             this.doFocus.defer(50, this);
2185         }
2186     },
2187
2188     doFocus : function(){
2189         if(!this.hidden){
2190             this.focusEl.focus();
2191         }
2192     },
2193
2194     /**
2195      * Hides this menu and optionally all parent menus
2196      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2197      */
2198     hide : function(deep)
2199     {
2200         
2201         this.hideMenuItems();
2202         if(this.el && this.isVisible()){
2203             this.fireEvent("beforehide", this);
2204             if(this.activeItem){
2205                 this.activeItem.deactivate();
2206                 this.activeItem = null;
2207             }
2208             this.triggerEl.removeClass('open');;
2209             this.hidden = true;
2210             this.fireEvent("hide", this);
2211         }
2212         if(deep === true && this.parentMenu){
2213             this.parentMenu.hide(true);
2214         }
2215     },
2216     
2217     onTriggerClick : function(e)
2218     {
2219         Roo.log('trigger click');
2220         
2221         var target = e.getTarget();
2222         
2223         Roo.log(target.nodeName.toLowerCase());
2224         
2225         if(target.nodeName.toLowerCase() === 'i'){
2226             e.preventDefault();
2227         }
2228         
2229     },
2230     
2231     onTriggerPress  : function(e)
2232     {
2233         Roo.log('trigger press');
2234         //Roo.log(e.getTarget());
2235        // Roo.log(this.triggerEl.dom);
2236        
2237         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238         var pel = Roo.get(e.getTarget());
2239         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240             Roo.log('is treeview or dropdown?');
2241             return;
2242         }
2243         
2244         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2245             return;
2246         }
2247         
2248         if (this.isVisible()) {
2249             Roo.log('hide');
2250             this.hide();
2251         } else {
2252             Roo.log('show');
2253             this.show(this.triggerEl, false, false);
2254         }
2255         
2256         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2257             e.stopEvent();
2258         }
2259         
2260     },
2261        
2262     
2263     hideMenuItems : function()
2264     {
2265         Roo.log("hide Menu Items");
2266         if (!this.el) { 
2267             return;
2268         }
2269         //$(backdrop).remove()
2270         this.el.select('.open',true).each(function(aa) {
2271             
2272             aa.removeClass('open');
2273           //var parent = getParent($(this))
2274           //var relatedTarget = { relatedTarget: this }
2275           
2276            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277           //if (e.isDefaultPrevented()) return
2278            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2279         });
2280     },
2281     addxtypeChild : function (tree, cntr) {
2282         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2283           
2284         this.menuitems.add(comp);
2285         return comp;
2286
2287     },
2288     getEl : function()
2289     {
2290         Roo.log(this.el);
2291         return this.el;
2292     }
2293 });
2294
2295  
2296  /*
2297  * - LGPL
2298  *
2299  * menu item
2300  * 
2301  */
2302
2303
2304 /**
2305  * @class Roo.bootstrap.MenuItem
2306  * @extends Roo.bootstrap.Component
2307  * Bootstrap MenuItem class
2308  * @cfg {String} html the menu label
2309  * @cfg {String} href the link
2310  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2313  * @cfg {String} fa favicon to show on left of menu item.
2314  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2315  * 
2316  * 
2317  * @constructor
2318  * Create a new MenuItem
2319  * @param {Object} config The config object
2320  */
2321
2322
2323 Roo.bootstrap.MenuItem = function(config){
2324     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2325     this.addEvents({
2326         // raw events
2327         /**
2328          * @event click
2329          * The raw click event for the entire grid.
2330          * @param {Roo.bootstrap.MenuItem} this
2331          * @param {Roo.EventObject} e
2332          */
2333         "click" : true
2334     });
2335 };
2336
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2338     
2339     href : false,
2340     html : false,
2341     preventDefault: false,
2342     isContainer : false,
2343     active : false,
2344     fa: false,
2345     
2346     getAutoCreate : function(){
2347         
2348         if(this.isContainer){
2349             return {
2350                 tag: 'li',
2351                 cls: 'dropdown-menu-item'
2352             };
2353         }
2354         var ctag = {
2355             tag: 'span',
2356             html: 'Link'
2357         };
2358         
2359         var anc = {
2360             tag : 'a',
2361             href : '#',
2362             cn : [  ]
2363         };
2364         
2365         if (this.fa !== false) {
2366             anc.cn.push({
2367                 tag : 'i',
2368                 cls : 'fa fa-' + this.fa
2369             });
2370         }
2371         
2372         anc.cn.push(ctag);
2373         
2374         
2375         var cfg= {
2376             tag: 'li',
2377             cls: 'dropdown-menu-item',
2378             cn: [ anc ]
2379         };
2380         if (this.parent().type == 'treeview') {
2381             cfg.cls = 'treeview-menu';
2382         }
2383         if (this.active) {
2384             cfg.cls += ' active';
2385         }
2386         
2387         
2388         
2389         anc.href = this.href || cfg.cn[0].href ;
2390         ctag.html = this.html || cfg.cn[0].html ;
2391         return cfg;
2392     },
2393     
2394     initEvents: function()
2395     {
2396         if (this.parent().type == 'treeview') {
2397             this.el.select('a').on('click', this.onClick, this);
2398         }
2399         if (this.menu) {
2400             this.menu.parentType = this.xtype;
2401             this.menu.triggerEl = this.el;
2402             this.menu = this.addxtype(Roo.apply({}, this.menu));
2403         }
2404         
2405     },
2406     onClick : function(e)
2407     {
2408         Roo.log('item on click ');
2409         
2410         if(this.preventDefault){
2411             e.preventDefault();
2412         }
2413         //this.parent().hideMenuItems();
2414         
2415         this.fireEvent('click', this, e);
2416     },
2417     getEl : function()
2418     {
2419         return this.el;
2420     } 
2421 });
2422
2423  
2424
2425  /*
2426  * - LGPL
2427  *
2428  * menu separator
2429  * 
2430  */
2431
2432
2433 /**
2434  * @class Roo.bootstrap.MenuSeparator
2435  * @extends Roo.bootstrap.Component
2436  * Bootstrap MenuSeparator class
2437  * 
2438  * @constructor
2439  * Create a new MenuItem
2440  * @param {Object} config The config object
2441  */
2442
2443
2444 Roo.bootstrap.MenuSeparator = function(config){
2445     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2446 };
2447
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2449     
2450     getAutoCreate : function(){
2451         var cfg = {
2452             cls: 'divider',
2453             tag : 'li'
2454         };
2455         
2456         return cfg;
2457     }
2458    
2459 });
2460
2461  
2462
2463  
2464 /*
2465 * Licence: LGPL
2466 */
2467
2468 /**
2469  * @class Roo.bootstrap.Modal
2470  * @extends Roo.bootstrap.Component
2471  * Bootstrap Modal class
2472  * @cfg {String} title Title of dialog
2473  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2475  * @cfg {Boolean} specificTitle default false
2476  * @cfg {Array} buttons Array of buttons or standard button set..
2477  * @cfg {String} buttonPosition (left|right|center) default right
2478  * @cfg {Boolean} animate default true
2479  * @cfg {Boolean} allow_close default true
2480  * @cfg {Boolean} fitwindow default false
2481  * @cfg {String} size (sm|lg) default empty
2482  *
2483  *
2484  * @constructor
2485  * Create a new Modal Dialog
2486  * @param {Object} config The config object
2487  */
2488
2489 Roo.bootstrap.Modal = function(config){
2490     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event btnclick
2495          * The raw btnclick event for the button
2496          * @param {Roo.EventObject} e
2497          */
2498         "btnclick" : true
2499     });
2500     this.buttons = this.buttons || [];
2501
2502     if (this.tmpl) {
2503         this.tmpl = Roo.factory(this.tmpl);
2504     }
2505
2506 };
2507
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2509
2510     title : 'test dialog',
2511
2512     buttons : false,
2513
2514     // set on load...
2515
2516     html: false,
2517
2518     tmp: false,
2519
2520     specificTitle: false,
2521
2522     buttonPosition: 'right',
2523
2524     allow_close : true,
2525
2526     animate : true,
2527
2528     fitwindow: false,
2529
2530
2531      // private
2532     dialogEl: false,
2533     bodyEl:  false,
2534     footerEl:  false,
2535     titleEl:  false,
2536     closeEl:  false,
2537
2538     size: '',
2539
2540
2541     onRender : function(ct, position)
2542     {
2543         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2544
2545         if(!this.el){
2546             var cfg = Roo.apply({},  this.getAutoCreate());
2547             cfg.id = Roo.id();
2548             //if(!cfg.name){
2549             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2550             //}
2551             //if (!cfg.name.length) {
2552             //    delete cfg.name;
2553            // }
2554             if (this.cls) {
2555                 cfg.cls += ' ' + this.cls;
2556             }
2557             if (this.style) {
2558                 cfg.style = this.style;
2559             }
2560             this.el = Roo.get(document.body).createChild(cfg, position);
2561         }
2562         //var type = this.el.dom.type;
2563
2564
2565         if(this.tabIndex !== undefined){
2566             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2567         }
2568
2569         this.dialogEl = this.el.select('.modal-dialog',true).first();
2570         this.bodyEl = this.el.select('.modal-body',true).first();
2571         this.closeEl = this.el.select('.modal-header .close', true).first();
2572         this.headerEl = this.el.select('.modal-header',true).first();
2573         this.titleEl = this.el.select('.modal-title',true).first();
2574         this.footerEl = this.el.select('.modal-footer',true).first();
2575
2576         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577         this.maskEl.enableDisplayMode("block");
2578         this.maskEl.hide();
2579         //this.el.addClass("x-dlg-modal");
2580
2581         if (this.buttons.length) {
2582             Roo.each(this.buttons, function(bb) {
2583                 var b = Roo.apply({}, bb);
2584                 b.xns = b.xns || Roo.bootstrap;
2585                 b.xtype = b.xtype || 'Button';
2586                 if (typeof(b.listeners) == 'undefined') {
2587                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2588                 }
2589
2590                 var btn = Roo.factory(b);
2591
2592                 btn.render(this.el.select('.modal-footer div').first());
2593
2594             },this);
2595         }
2596         // render the children.
2597         var nitems = [];
2598
2599         if(typeof(this.items) != 'undefined'){
2600             var items = this.items;
2601             delete this.items;
2602
2603             for(var i =0;i < items.length;i++) {
2604                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2605             }
2606         }
2607
2608         this.items = nitems;
2609
2610         // where are these used - they used to be body/close/footer
2611
2612
2613         this.initEvents();
2614         //this.el.addClass([this.fieldClass, this.cls]);
2615
2616     },
2617
2618     getAutoCreate : function(){
2619
2620
2621         var bdy = {
2622                 cls : 'modal-body',
2623                 html : this.html || ''
2624         };
2625
2626         var title = {
2627             tag: 'h4',
2628             cls : 'modal-title',
2629             html : this.title
2630         };
2631
2632         if(this.specificTitle){
2633             title = this.title;
2634
2635         };
2636
2637         var header = [];
2638         if (this.allow_close) {
2639             header.push({
2640                 tag: 'button',
2641                 cls : 'close',
2642                 html : '&times'
2643             });
2644         }
2645
2646         header.push(title);
2647
2648         var size = '';
2649
2650         if(this.size.length){
2651             size = 'modal-' + this.size;
2652         }
2653
2654         var modal = {
2655             cls: "modal",
2656             style : 'display: none',
2657             cn : [
2658                 {
2659                     cls: "modal-dialog " + size,
2660                     cn : [
2661                         {
2662                             cls : "modal-content",
2663                             cn : [
2664                                 {
2665                                     cls : 'modal-header',
2666                                     cn : header
2667                                 },
2668                                 bdy,
2669                                 {
2670                                     cls : 'modal-footer',
2671                                     cn : [
2672                                         {
2673                                             tag: 'div',
2674                                             cls: 'btn-' + this.buttonPosition
2675                                         }
2676                                     ]
2677
2678                                 }
2679
2680
2681                             ]
2682
2683                         }
2684                     ]
2685
2686                 }
2687             ]
2688         };
2689
2690         if(this.animate){
2691             modal.cls += ' fade';
2692         }
2693
2694         return modal;
2695
2696     },
2697     getChildContainer : function() {
2698
2699          return this.bodyEl;
2700
2701     },
2702     getButtonContainer : function() {
2703          return this.el.select('.modal-footer div',true).first();
2704
2705     },
2706     initEvents : function()
2707     {
2708         if (this.allow_close) {
2709             this.closeEl.on('click', this.hide, this);
2710         }
2711         Roo.EventManager.onWindowResize(this.resize, this, true);
2712
2713
2714     },
2715
2716     resize : function()
2717     {
2718         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2719         if (this.fitwindow) {
2720             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2722             this.setSize(w,h);
2723         }
2724     },
2725
2726     setSize : function(w,h)
2727     {
2728         if (!w && !h) {
2729             return;
2730         }
2731         this.resizeTo(w,h);
2732     },
2733
2734     show : function() {
2735
2736         if (!this.rendered) {
2737             this.render();
2738         }
2739
2740         this.el.setStyle('display', 'block');
2741
2742         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2743             var _this = this;
2744             (function(){
2745                 this.el.addClass('in');
2746             }).defer(50, this);
2747         }else{
2748             this.el.addClass('in');
2749
2750         }
2751
2752         // not sure how we can show data in here..
2753         //if (this.tmpl) {
2754         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2755         //}
2756
2757         Roo.get(document.body).addClass("x-body-masked");
2758         
2759         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2760         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2761         this.maskEl.show();
2762         
2763         this.resize();
2764         
2765         this.fireEvent('show', this);
2766
2767         // set zindex here - otherwise it appears to be ignored...
2768         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2769
2770         (function () {
2771             this.items.forEach( function(e) {
2772                 e.layout ? e.layout() : false;
2773
2774             });
2775         }).defer(100,this);
2776
2777     },
2778     hide : function()
2779     {
2780         if(this.fireEvent("beforehide", this) !== false){
2781             this.maskEl.hide();
2782             Roo.get(document.body).removeClass("x-body-masked");
2783             this.el.removeClass('in');
2784             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2785
2786             if(this.animate){ // why
2787                 var _this = this;
2788                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2789             }else{
2790                 this.el.setStyle('display', 'none');
2791             }
2792             this.fireEvent('hide', this);
2793         }
2794     },
2795
2796     addButton : function(str, cb)
2797     {
2798
2799
2800         var b = Roo.apply({}, { html : str } );
2801         b.xns = b.xns || Roo.bootstrap;
2802         b.xtype = b.xtype || 'Button';
2803         if (typeof(b.listeners) == 'undefined') {
2804             b.listeners = { click : cb.createDelegate(this)  };
2805         }
2806
2807         var btn = Roo.factory(b);
2808
2809         btn.render(this.el.select('.modal-footer div').first());
2810
2811         return btn;
2812
2813     },
2814
2815     setDefaultButton : function(btn)
2816     {
2817         //this.el.select('.modal-footer').()
2818     },
2819     diff : false,
2820
2821     resizeTo: function(w,h)
2822     {
2823         // skip.. ?? why??
2824
2825         this.dialogEl.setWidth(w);
2826         if (this.diff === false) {
2827             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2828         }
2829
2830         this.bodyEl.setHeight(h-this.diff);
2831
2832
2833     },
2834     setContentSize  : function(w, h)
2835     {
2836
2837     },
2838     onButtonClick: function(btn,e)
2839     {
2840         //Roo.log([a,b,c]);
2841         this.fireEvent('btnclick', btn.name, e);
2842     },
2843      /**
2844      * Set the title of the Dialog
2845      * @param {String} str new Title
2846      */
2847     setTitle: function(str) {
2848         this.titleEl.dom.innerHTML = str;
2849     },
2850     /**
2851      * Set the body of the Dialog
2852      * @param {String} str new Title
2853      */
2854     setBody: function(str) {
2855         this.bodyEl.dom.innerHTML = str;
2856     },
2857     /**
2858      * Set the body of the Dialog using the template
2859      * @param {Obj} data - apply this data to the template and replace the body contents.
2860      */
2861     applyBody: function(obj)
2862     {
2863         if (!this.tmpl) {
2864             Roo.log("Error - using apply Body without a template");
2865             //code
2866         }
2867         this.tmpl.overwrite(this.bodyEl, obj);
2868     }
2869
2870 });
2871
2872
2873 Roo.apply(Roo.bootstrap.Modal,  {
2874     /**
2875          * Button config that displays a single OK button
2876          * @type Object
2877          */
2878         OK :  [{
2879             name : 'ok',
2880             weight : 'primary',
2881             html : 'OK'
2882         }],
2883         /**
2884          * Button config that displays Yes and No buttons
2885          * @type Object
2886          */
2887         YESNO : [
2888             {
2889                 name  : 'no',
2890                 html : 'No'
2891             },
2892             {
2893                 name  :'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             }
2897         ],
2898
2899         /**
2900          * Button config that displays OK and Cancel buttons
2901          * @type Object
2902          */
2903         OKCANCEL : [
2904             {
2905                name : 'cancel',
2906                 html : 'Cancel'
2907             },
2908             {
2909                 name : 'ok',
2910                 weight : 'primary',
2911                 html : 'OK'
2912             }
2913         ],
2914         /**
2915          * Button config that displays Yes, No and Cancel buttons
2916          * @type Object
2917          */
2918         YESNOCANCEL : [
2919             {
2920                 name : 'yes',
2921                 weight : 'primary',
2922                 html : 'Yes'
2923             },
2924             {
2925                 name : 'no',
2926                 html : 'No'
2927             },
2928             {
2929                 name : 'cancel',
2930                 html : 'Cancel'
2931             }
2932         ],
2933         
2934         zIndex : 10001
2935 });
2936 /*
2937  * - LGPL
2938  *
2939  * messagebox - can be used as a replace
2940  * 
2941  */
2942 /**
2943  * @class Roo.MessageBox
2944  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2945  * Example usage:
2946  *<pre><code>
2947 // Basic alert:
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2949
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2952     if (btn == 'ok'){
2953         // process text value...
2954     }
2955 });
2956
2957 // Show a dialog using config options:
2958 Roo.Msg.show({
2959    title:'Save Changes?',
2960    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961    buttons: Roo.Msg.YESNOCANCEL,
2962    fn: processResult,
2963    animEl: 'elId'
2964 });
2965 </code></pre>
2966  * @singleton
2967  */
2968 Roo.bootstrap.MessageBox = function(){
2969     var dlg, opt, mask, waitTimer;
2970     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971     var buttons, activeTextEl, bwidth;
2972
2973     
2974     // private
2975     var handleButton = function(button){
2976         dlg.hide();
2977         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2978     };
2979
2980     // private
2981     var handleHide = function(){
2982         if(opt && opt.cls){
2983             dlg.el.removeClass(opt.cls);
2984         }
2985         //if(waitTimer){
2986         //    Roo.TaskMgr.stop(waitTimer);
2987         //    waitTimer = null;
2988         //}
2989     };
2990
2991     // private
2992     var updateButtons = function(b){
2993         var width = 0;
2994         if(!b){
2995             buttons["ok"].hide();
2996             buttons["cancel"].hide();
2997             buttons["yes"].hide();
2998             buttons["no"].hide();
2999             //dlg.footer.dom.style.display = 'none';
3000             return width;
3001         }
3002         dlg.footerEl.dom.style.display = '';
3003         for(var k in buttons){
3004             if(typeof buttons[k] != "function"){
3005                 if(b[k]){
3006                     buttons[k].show();
3007                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008                     width += buttons[k].el.getWidth()+15;
3009                 }else{
3010                     buttons[k].hide();
3011                 }
3012             }
3013         }
3014         return width;
3015     };
3016
3017     // private
3018     var handleEsc = function(d, k, e){
3019         if(opt && opt.closable !== false){
3020             dlg.hide();
3021         }
3022         if(e){
3023             e.stopEvent();
3024         }
3025     };
3026
3027     return {
3028         /**
3029          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030          * @return {Roo.BasicDialog} The BasicDialog element
3031          */
3032         getDialog : function(){
3033            if(!dlg){
3034                 dlg = new Roo.bootstrap.Modal( {
3035                     //draggable: true,
3036                     //resizable:false,
3037                     //constraintoviewport:false,
3038                     //fixedcenter:true,
3039                     //collapsible : false,
3040                     //shim:true,
3041                     //modal: true,
3042                   //  width:400,
3043                   //  height:100,
3044                     //buttonAlign:"center",
3045                     closeClick : function(){
3046                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3047                             handleButton("no");
3048                         }else{
3049                             handleButton("cancel");
3050                         }
3051                     }
3052                 });
3053                 dlg.render();
3054                 dlg.on("hide", handleHide);
3055                 mask = dlg.mask;
3056                 //dlg.addKeyListener(27, handleEsc);
3057                 buttons = {};
3058                 this.buttons = buttons;
3059                 var bt = this.buttonText;
3060                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3061                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3062                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3063                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3064                 //Roo.log(buttons);
3065                 bodyEl = dlg.bodyEl.createChild({
3066
3067                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3068                         '<textarea class="roo-mb-textarea"></textarea>' +
3069                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3070                 });
3071                 msgEl = bodyEl.dom.firstChild;
3072                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3073                 textboxEl.enableDisplayMode();
3074                 textboxEl.addKeyListener([10,13], function(){
3075                     if(dlg.isVisible() && opt && opt.buttons){
3076                         if(opt.buttons.ok){
3077                             handleButton("ok");
3078                         }else if(opt.buttons.yes){
3079                             handleButton("yes");
3080                         }
3081                     }
3082                 });
3083                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3084                 textareaEl.enableDisplayMode();
3085                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3086                 progressEl.enableDisplayMode();
3087                 
3088                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3089                 //var pf = progressEl.dom.firstChild;
3090                 //if (pf) {
3091                     //pp = Roo.get(pf.firstChild);
3092                     //pp.setHeight(pf.offsetHeight);
3093                 //}
3094                 
3095             }
3096             return dlg;
3097         },
3098
3099         /**
3100          * Updates the message box body text
3101          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3102          * the XHTML-compliant non-breaking space character '&amp;#160;')
3103          * @return {Roo.MessageBox} This message box
3104          */
3105         updateText : function(text)
3106         {
3107             if(!dlg.isVisible() && !opt.width){
3108                 dlg.dialogEl.setWidth(this.maxWidth);
3109                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3110             }
3111             msgEl.innerHTML = text || '&#160;';
3112       
3113             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3114             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3115             var w = Math.max(
3116                     Math.min(opt.width || cw , this.maxWidth), 
3117                     Math.max(opt.minWidth || this.minWidth, bwidth)
3118             );
3119             if(opt.prompt){
3120                 activeTextEl.setWidth(w);
3121             }
3122             if(dlg.isVisible()){
3123                 dlg.fixedcenter = false;
3124             }
3125             // to big, make it scroll. = But as usual stupid IE does not support
3126             // !important..
3127             
3128             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3129                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3130                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3131             } else {
3132                 bodyEl.dom.style.height = '';
3133                 bodyEl.dom.style.overflowY = '';
3134             }
3135             if (cw > w) {
3136                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3137             } else {
3138                 bodyEl.dom.style.overflowX = '';
3139             }
3140             
3141             dlg.setContentSize(w, bodyEl.getHeight());
3142             if(dlg.isVisible()){
3143                 dlg.fixedcenter = true;
3144             }
3145             return this;
3146         },
3147
3148         /**
3149          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3150          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3151          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3152          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3153          * @return {Roo.MessageBox} This message box
3154          */
3155         updateProgress : function(value, text){
3156             if(text){
3157                 this.updateText(text);
3158             }
3159             if (pp) { // weird bug on my firefox - for some reason this is not defined
3160                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3161             }
3162             return this;
3163         },        
3164
3165         /**
3166          * Returns true if the message box is currently displayed
3167          * @return {Boolean} True if the message box is visible, else false
3168          */
3169         isVisible : function(){
3170             return dlg && dlg.isVisible();  
3171         },
3172
3173         /**
3174          * Hides the message box if it is displayed
3175          */
3176         hide : function(){
3177             if(this.isVisible()){
3178                 dlg.hide();
3179             }  
3180         },
3181
3182         /**
3183          * Displays a new message box, or reinitializes an existing message box, based on the config options
3184          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3185          * The following config object properties are supported:
3186          * <pre>
3187 Property    Type             Description
3188 ----------  ---------------  ------------------------------------------------------------------------------------
3189 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3190                                    closes (defaults to undefined)
3191 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3192                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3193 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3194                                    progress and wait dialogs will ignore this property and always hide the
3195                                    close button as they can only be closed programmatically.
3196 cls               String           A custom CSS class to apply to the message box element
3197 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3198                                    displayed (defaults to 75)
3199 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3200                                    function will be btn (the name of the button that was clicked, if applicable,
3201                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3202                                    Progress and wait dialogs will ignore this option since they do not respond to
3203                                    user actions and can only be closed programmatically, so any required function
3204                                    should be called by the same code after it closes the dialog.
3205 icon              String           A CSS class that provides a background image to be used as an icon for
3206                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3207 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3208 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3209 modal             Boolean          False to allow user interaction with the page while the message box is
3210                                    displayed (defaults to true)
3211 msg               String           A string that will replace the existing message box body text (defaults
3212                                    to the XHTML-compliant non-breaking space character '&#160;')
3213 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3214 progress          Boolean          True to display a progress bar (defaults to false)
3215 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3216 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3217 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3218 title             String           The title text
3219 value             String           The string value to set into the active textbox element if displayed
3220 wait              Boolean          True to display a progress bar (defaults to false)
3221 width             Number           The width of the dialog in pixels
3222 </pre>
3223          *
3224          * Example usage:
3225          * <pre><code>
3226 Roo.Msg.show({
3227    title: 'Address',
3228    msg: 'Please enter your address:',
3229    width: 300,
3230    buttons: Roo.MessageBox.OKCANCEL,
3231    multiline: true,
3232    fn: saveAddress,
3233    animEl: 'addAddressBtn'
3234 });
3235 </code></pre>
3236          * @param {Object} config Configuration options
3237          * @return {Roo.MessageBox} This message box
3238          */
3239         show : function(options)
3240         {
3241             
3242             // this causes nightmares if you show one dialog after another
3243             // especially on callbacks..
3244              
3245             if(this.isVisible()){
3246                 
3247                 this.hide();
3248                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3249                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3250                 Roo.log("New Dialog Message:" +  options.msg )
3251                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3252                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3253                 
3254             }
3255             var d = this.getDialog();
3256             opt = options;
3257             d.setTitle(opt.title || "&#160;");
3258             d.closeEl.setDisplayed(opt.closable !== false);
3259             activeTextEl = textboxEl;
3260             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3261             if(opt.prompt){
3262                 if(opt.multiline){
3263                     textboxEl.hide();
3264                     textareaEl.show();
3265                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3266                         opt.multiline : this.defaultTextHeight);
3267                     activeTextEl = textareaEl;
3268                 }else{
3269                     textboxEl.show();
3270                     textareaEl.hide();
3271                 }
3272             }else{
3273                 textboxEl.hide();
3274                 textareaEl.hide();
3275             }
3276             progressEl.setDisplayed(opt.progress === true);
3277             this.updateProgress(0);
3278             activeTextEl.dom.value = opt.value || "";
3279             if(opt.prompt){
3280                 dlg.setDefaultButton(activeTextEl);
3281             }else{
3282                 var bs = opt.buttons;
3283                 var db = null;
3284                 if(bs && bs.ok){
3285                     db = buttons["ok"];
3286                 }else if(bs && bs.yes){
3287                     db = buttons["yes"];
3288                 }
3289                 dlg.setDefaultButton(db);
3290             }
3291             bwidth = updateButtons(opt.buttons);
3292             this.updateText(opt.msg);
3293             if(opt.cls){
3294                 d.el.addClass(opt.cls);
3295             }
3296             d.proxyDrag = opt.proxyDrag === true;
3297             d.modal = opt.modal !== false;
3298             d.mask = opt.modal !== false ? mask : false;
3299             if(!d.isVisible()){
3300                 // force it to the end of the z-index stack so it gets a cursor in FF
3301                 document.body.appendChild(dlg.el.dom);
3302                 d.animateTarget = null;
3303                 d.show(options.animEl);
3304             }
3305             return this;
3306         },
3307
3308         /**
3309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3311          * and closing the message box when the process is complete.
3312          * @param {String} title The title bar text
3313          * @param {String} msg The message box body text
3314          * @return {Roo.MessageBox} This message box
3315          */
3316         progress : function(title, msg){
3317             this.show({
3318                 title : title,
3319                 msg : msg,
3320                 buttons: false,
3321                 progress:true,
3322                 closable:false,
3323                 minWidth: this.minProgressWidth,
3324                 modal : true
3325             });
3326             return this;
3327         },
3328
3329         /**
3330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3331          * If a callback function is passed it will be called after the user clicks the button, and the
3332          * id of the button that was clicked will be passed as the only parameter to the callback
3333          * (could also be the top-right close button).
3334          * @param {String} title The title bar text
3335          * @param {String} msg The message box body text
3336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3337          * @param {Object} scope (optional) The scope of the callback function
3338          * @return {Roo.MessageBox} This message box
3339          */
3340         alert : function(title, msg, fn, scope)
3341         {
3342             this.show({
3343                 title : title,
3344                 msg : msg,
3345                 buttons: this.OK,
3346                 fn: fn,
3347                 closable : false,
3348                 scope : scope,
3349                 modal : true
3350             });
3351             return this;
3352         },
3353
3354         /**
3355          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3356          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3357          * You are responsible for closing the message box when the process is complete.
3358          * @param {String} msg The message box body text
3359          * @param {String} title (optional) The title bar text
3360          * @return {Roo.MessageBox} This message box
3361          */
3362         wait : function(msg, title){
3363             this.show({
3364                 title : title,
3365                 msg : msg,
3366                 buttons: false,
3367                 closable:false,
3368                 progress:true,
3369                 modal:true,
3370                 width:300,
3371                 wait:true
3372             });
3373             waitTimer = Roo.TaskMgr.start({
3374                 run: function(i){
3375                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3376                 },
3377                 interval: 1000
3378             });
3379             return this;
3380         },
3381
3382         /**
3383          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3384          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3385          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3386          * @param {String} title The title bar text
3387          * @param {String} msg The message box body text
3388          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3389          * @param {Object} scope (optional) The scope of the callback function
3390          * @return {Roo.MessageBox} This message box
3391          */
3392         confirm : function(title, msg, fn, scope){
3393             this.show({
3394                 title : title,
3395                 msg : msg,
3396                 buttons: this.YESNO,
3397                 fn: fn,
3398                 scope : scope,
3399                 modal : true
3400             });
3401             return this;
3402         },
3403
3404         /**
3405          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3406          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3407          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3408          * (could also be the top-right close button) and the text that was entered will be passed as the two
3409          * parameters to the callback.
3410          * @param {String} title The title bar text
3411          * @param {String} msg The message box body text
3412          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3413          * @param {Object} scope (optional) The scope of the callback function
3414          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3415          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3416          * @return {Roo.MessageBox} This message box
3417          */
3418         prompt : function(title, msg, fn, scope, multiline){
3419             this.show({
3420                 title : title,
3421                 msg : msg,
3422                 buttons: this.OKCANCEL,
3423                 fn: fn,
3424                 minWidth:250,
3425                 scope : scope,
3426                 prompt:true,
3427                 multiline: multiline,
3428                 modal : true
3429             });
3430             return this;
3431         },
3432
3433         /**
3434          * Button config that displays a single OK button
3435          * @type Object
3436          */
3437         OK : {ok:true},
3438         /**
3439          * Button config that displays Yes and No buttons
3440          * @type Object
3441          */
3442         YESNO : {yes:true, no:true},
3443         /**
3444          * Button config that displays OK and Cancel buttons
3445          * @type Object
3446          */
3447         OKCANCEL : {ok:true, cancel:true},
3448         /**
3449          * Button config that displays Yes, No and Cancel buttons
3450          * @type Object
3451          */
3452         YESNOCANCEL : {yes:true, no:true, cancel:true},
3453
3454         /**
3455          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3456          * @type Number
3457          */
3458         defaultTextHeight : 75,
3459         /**
3460          * The maximum width in pixels of the message box (defaults to 600)
3461          * @type Number
3462          */
3463         maxWidth : 600,
3464         /**
3465          * The minimum width in pixels of the message box (defaults to 100)
3466          * @type Number
3467          */
3468         minWidth : 100,
3469         /**
3470          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3471          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3472          * @type Number
3473          */
3474         minProgressWidth : 250,
3475         /**
3476          * An object containing the default button text strings that can be overriden for localized language support.
3477          * Supported properties are: ok, cancel, yes and no.
3478          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3479          * @type Object
3480          */
3481         buttonText : {
3482             ok : "OK",
3483             cancel : "Cancel",
3484             yes : "Yes",
3485             no : "No"
3486         }
3487     };
3488 }();
3489
3490 /**
3491  * Shorthand for {@link Roo.MessageBox}
3492  */
3493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3494 Roo.Msg = Roo.Msg || Roo.MessageBox;
3495 /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.Navbar
3504  * @extends Roo.bootstrap.Component
3505  * Bootstrap Navbar class
3506
3507  * @constructor
3508  * Create a new Navbar
3509  * @param {Object} config The config object
3510  */
3511
3512
3513 Roo.bootstrap.Navbar = function(config){
3514     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3515     this.addEvents({
3516         // raw events
3517         /**
3518          * @event beforetoggle
3519          * Fire before toggle the menu
3520          * @param {Roo.EventObject} e
3521          */
3522         "beforetoggle" : true
3523     });
3524 };
3525
3526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3527     
3528     
3529    
3530     // private
3531     navItems : false,
3532     loadMask : false,
3533     
3534     
3535     getAutoCreate : function(){
3536         
3537         
3538         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3539         
3540     },
3541     
3542     initEvents :function ()
3543     {
3544         //Roo.log(this.el.select('.navbar-toggle',true));
3545         this.el.select('.navbar-toggle',true).on('click', function() {
3546             if(this.fireEvent('beforetoggle', this) !== false){
3547                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3548             }
3549             
3550         }, this);
3551         
3552         var mark = {
3553             tag: "div",
3554             cls:"x-dlg-mask"
3555         };
3556         
3557         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3558         
3559         var size = this.el.getSize();
3560         this.maskEl.setSize(size.width, size.height);
3561         this.maskEl.enableDisplayMode("block");
3562         this.maskEl.hide();
3563         
3564         if(this.loadMask){
3565             this.maskEl.show();
3566         }
3567     },
3568     
3569     
3570     getChildContainer : function()
3571     {
3572         if (this.el.select('.collapse').getCount()) {
3573             return this.el.select('.collapse',true).first();
3574         }
3575         
3576         return this.el;
3577     },
3578     
3579     mask : function()
3580     {
3581         this.maskEl.show();
3582     },
3583     
3584     unmask : function()
3585     {
3586         this.maskEl.hide();
3587     } 
3588     
3589     
3590     
3591     
3592 });
3593
3594
3595
3596  
3597
3598  /*
3599  * - LGPL
3600  *
3601  * navbar
3602  * 
3603  */
3604
3605 /**
3606  * @class Roo.bootstrap.NavSimplebar
3607  * @extends Roo.bootstrap.Navbar
3608  * Bootstrap Sidebar class
3609  *
3610  * @cfg {Boolean} inverse is inverted color
3611  * 
3612  * @cfg {String} type (nav | pills | tabs)
3613  * @cfg {Boolean} arrangement stacked | justified
3614  * @cfg {String} align (left | right) alignment
3615  * 
3616  * @cfg {Boolean} main (true|false) main nav bar? default false
3617  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3618  * 
3619  * @cfg {String} tag (header|footer|nav|div) default is nav 
3620
3621  * 
3622  * 
3623  * 
3624  * @constructor
3625  * Create a new Sidebar
3626  * @param {Object} config The config object
3627  */
3628
3629
3630 Roo.bootstrap.NavSimplebar = function(config){
3631     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3632 };
3633
3634 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3635     
3636     inverse: false,
3637     
3638     type: false,
3639     arrangement: '',
3640     align : false,
3641     
3642     
3643     
3644     main : false,
3645     
3646     
3647     tag : false,
3648     
3649     
3650     getAutoCreate : function(){
3651         
3652         
3653         var cfg = {
3654             tag : this.tag || 'div',
3655             cls : 'navbar'
3656         };
3657           
3658         
3659         cfg.cn = [
3660             {
3661                 cls: 'nav',
3662                 tag : 'ul'
3663             }
3664         ];
3665         
3666          
3667         this.type = this.type || 'nav';
3668         if (['tabs','pills'].indexOf(this.type)!==-1) {
3669             cfg.cn[0].cls += ' nav-' + this.type
3670         
3671         
3672         } else {
3673             if (this.type!=='nav') {
3674                 Roo.log('nav type must be nav/tabs/pills')
3675             }
3676             cfg.cn[0].cls += ' navbar-nav'
3677         }
3678         
3679         
3680         
3681         
3682         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3683             cfg.cn[0].cls += ' nav-' + this.arrangement;
3684         }
3685         
3686         
3687         if (this.align === 'right') {
3688             cfg.cn[0].cls += ' navbar-right';
3689         }
3690         
3691         if (this.inverse) {
3692             cfg.cls += ' navbar-inverse';
3693             
3694         }
3695         
3696         
3697         return cfg;
3698     
3699         
3700     }
3701     
3702     
3703     
3704 });
3705
3706
3707
3708  
3709
3710  
3711        /*
3712  * - LGPL
3713  *
3714  * navbar
3715  * 
3716  */
3717
3718 /**
3719  * @class Roo.bootstrap.NavHeaderbar
3720  * @extends Roo.bootstrap.NavSimplebar
3721  * Bootstrap Sidebar class
3722  *
3723  * @cfg {String} brand what is brand
3724  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3725  * @cfg {String} brand_href href of the brand
3726  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3727  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3728  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3729  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3730  * 
3731  * @constructor
3732  * Create a new Sidebar
3733  * @param {Object} config The config object
3734  */
3735
3736
3737 Roo.bootstrap.NavHeaderbar = function(config){
3738     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3739       
3740 };
3741
3742 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3743     
3744     position: '',
3745     brand: '',
3746     brand_href: false,
3747     srButton : true,
3748     autohide : false,
3749     desktopCenter : false,
3750    
3751     
3752     getAutoCreate : function(){
3753         
3754         var   cfg = {
3755             tag: this.nav || 'nav',
3756             cls: 'navbar',
3757             role: 'navigation',
3758             cn: []
3759         };
3760         
3761         var cn = cfg.cn;
3762         if (this.desktopCenter) {
3763             cn.push({cls : 'container', cn : []});
3764             cn = cn[0].cn;
3765         }
3766         
3767         if(this.srButton){
3768             cn.push({
3769                 tag: 'div',
3770                 cls: 'navbar-header',
3771                 cn: [
3772                     {
3773                         tag: 'button',
3774                         type: 'button',
3775                         cls: 'navbar-toggle',
3776                         'data-toggle': 'collapse',
3777                         cn: [
3778                             {
3779                                 tag: 'span',
3780                                 cls: 'sr-only',
3781                                 html: 'Toggle navigation'
3782                             },
3783                             {
3784                                 tag: 'span',
3785                                 cls: 'icon-bar'
3786                             },
3787                             {
3788                                 tag: 'span',
3789                                 cls: 'icon-bar'
3790                             },
3791                             {
3792                                 tag: 'span',
3793                                 cls: 'icon-bar'
3794                             }
3795                         ]
3796                     }
3797                 ]
3798             });
3799         }
3800         
3801         cn.push({
3802             tag: 'div',
3803             cls: 'collapse navbar-collapse',
3804             cn : []
3805         });
3806         
3807         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3808         
3809         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3810             cfg.cls += ' navbar-' + this.position;
3811             
3812             // tag can override this..
3813             
3814             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3815         }
3816         
3817         if (this.brand !== '') {
3818             cn[0].cn.push({
3819                 tag: 'a',
3820                 href: this.brand_href ? this.brand_href : '#',
3821                 cls: 'navbar-brand',
3822                 cn: [
3823                 this.brand
3824                 ]
3825             });
3826         }
3827         
3828         if(this.main){
3829             cfg.cls += ' main-nav';
3830         }
3831         
3832         
3833         return cfg;
3834
3835         
3836     },
3837     getHeaderChildContainer : function()
3838     {
3839         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3840             return this.el.select('.navbar-header',true).first();
3841         }
3842         
3843         return this.getChildContainer();
3844     },
3845     
3846     
3847     initEvents : function()
3848     {
3849         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3850         
3851         if (this.autohide) {
3852             
3853             var prevScroll = 0;
3854             var ft = this.el;
3855             
3856             Roo.get(document).on('scroll',function(e) {
3857                 var ns = Roo.get(document).getScroll().top;
3858                 var os = prevScroll;
3859                 prevScroll = ns;
3860                 
3861                 if(ns > os){
3862                     ft.removeClass('slideDown');
3863                     ft.addClass('slideUp');
3864                     return;
3865                 }
3866                 ft.removeClass('slideUp');
3867                 ft.addClass('slideDown');
3868                  
3869               
3870           },this);
3871         }
3872     }    
3873     
3874 });
3875
3876
3877
3878  
3879
3880  /*
3881  * - LGPL
3882  *
3883  * navbar
3884  * 
3885  */
3886
3887 /**
3888  * @class Roo.bootstrap.NavSidebar
3889  * @extends Roo.bootstrap.Navbar
3890  * Bootstrap Sidebar class
3891  * 
3892  * @constructor
3893  * Create a new Sidebar
3894  * @param {Object} config The config object
3895  */
3896
3897
3898 Roo.bootstrap.NavSidebar = function(config){
3899     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3900 };
3901
3902 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3903     
3904     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3905     
3906     getAutoCreate : function(){
3907         
3908         
3909         return  {
3910             tag: 'div',
3911             cls: 'sidebar sidebar-nav'
3912         };
3913     
3914         
3915     }
3916     
3917     
3918     
3919 });
3920
3921
3922
3923  
3924
3925  /*
3926  * - LGPL
3927  *
3928  * nav group
3929  * 
3930  */
3931
3932 /**
3933  * @class Roo.bootstrap.NavGroup
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap NavGroup class
3936  * @cfg {String} align (left|right)
3937  * @cfg {Boolean} inverse
3938  * @cfg {String} type (nav|pills|tab) default nav
3939  * @cfg {String} navId - reference Id for navbar.
3940
3941  * 
3942  * @constructor
3943  * Create a new nav group
3944  * @param {Object} config The config object
3945  */
3946
3947 Roo.bootstrap.NavGroup = function(config){
3948     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3949     this.navItems = [];
3950    
3951     Roo.bootstrap.NavGroup.register(this);
3952      this.addEvents({
3953         /**
3954              * @event changed
3955              * Fires when the active item changes
3956              * @param {Roo.bootstrap.NavGroup} this
3957              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3958              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3959          */
3960         'changed': true
3961      });
3962     
3963 };
3964
3965 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3966     
3967     align: '',
3968     inverse: false,
3969     form: false,
3970     type: 'nav',
3971     navId : '',
3972     // private
3973     
3974     navItems : false, 
3975     
3976     getAutoCreate : function()
3977     {
3978         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3979         
3980         cfg = {
3981             tag : 'ul',
3982             cls: 'nav' 
3983         };
3984         
3985         if (['tabs','pills'].indexOf(this.type)!==-1) {
3986             cfg.cls += ' nav-' + this.type
3987         } else {
3988             if (this.type!=='nav') {
3989                 Roo.log('nav type must be nav/tabs/pills')
3990             }
3991             cfg.cls += ' navbar-nav'
3992         }
3993         
3994         if (this.parent().sidebar) {
3995             cfg = {
3996                 tag: 'ul',
3997                 cls: 'dashboard-menu sidebar-menu'
3998             };
3999             
4000             return cfg;
4001         }
4002         
4003         if (this.form === true) {
4004             cfg = {
4005                 tag: 'form',
4006                 cls: 'navbar-form'
4007             };
4008             
4009             if (this.align === 'right') {
4010                 cfg.cls += ' navbar-right';
4011             } else {
4012                 cfg.cls += ' navbar-left';
4013             }
4014         }
4015         
4016         if (this.align === 'right') {
4017             cfg.cls += ' navbar-right';
4018         }
4019         
4020         if (this.inverse) {
4021             cfg.cls += ' navbar-inverse';
4022             
4023         }
4024         
4025         
4026         return cfg;
4027     },
4028     /**
4029     * sets the active Navigation item
4030     * @param {Roo.bootstrap.NavItem} the new current navitem
4031     */
4032     setActiveItem : function(item)
4033     {
4034         var prev = false;
4035         Roo.each(this.navItems, function(v){
4036             if (v == item) {
4037                 return ;
4038             }
4039             if (v.isActive()) {
4040                 v.setActive(false, true);
4041                 prev = v;
4042                 
4043             }
4044             
4045         });
4046
4047         item.setActive(true, true);
4048         this.fireEvent('changed', this, item, prev);
4049         
4050         
4051     },
4052     /**
4053     * gets the active Navigation item
4054     * @return {Roo.bootstrap.NavItem} the current navitem
4055     */
4056     getActive : function()
4057     {
4058         
4059         var prev = false;
4060         Roo.each(this.navItems, function(v){
4061             
4062             if (v.isActive()) {
4063                 prev = v;
4064                 
4065             }
4066             
4067         });
4068         return prev;
4069     },
4070     
4071     indexOfNav : function()
4072     {
4073         
4074         var prev = false;
4075         Roo.each(this.navItems, function(v,i){
4076             
4077             if (v.isActive()) {
4078                 prev = i;
4079                 
4080             }
4081             
4082         });
4083         return prev;
4084     },
4085     /**
4086     * adds a Navigation item
4087     * @param {Roo.bootstrap.NavItem} the navitem to add
4088     */
4089     addItem : function(cfg)
4090     {
4091         var cn = new Roo.bootstrap.NavItem(cfg);
4092         this.register(cn);
4093         cn.parentId = this.id;
4094         cn.onRender(this.el, null);
4095         return cn;
4096     },
4097     /**
4098     * register a Navigation item
4099     * @param {Roo.bootstrap.NavItem} the navitem to add
4100     */
4101     register : function(item)
4102     {
4103         this.navItems.push( item);
4104         item.navId = this.navId;
4105     
4106     },
4107     
4108     /**
4109     * clear all the Navigation item
4110     */
4111    
4112     clearAll : function()
4113     {
4114         this.navItems = [];
4115         this.el.dom.innerHTML = '';
4116     },
4117     
4118     getNavItem: function(tabId)
4119     {
4120         var ret = false;
4121         Roo.each(this.navItems, function(e) {
4122             if (e.tabId == tabId) {
4123                ret =  e;
4124                return false;
4125             }
4126             return true;
4127             
4128         });
4129         return ret;
4130     },
4131     
4132     setActiveNext : function()
4133     {
4134         var i = this.indexOfNav(this.getActive());
4135         if (i > this.navItems.length) {
4136             return;
4137         }
4138         this.setActiveItem(this.navItems[i+1]);
4139     },
4140     setActivePrev : function()
4141     {
4142         var i = this.indexOfNav(this.getActive());
4143         if (i  < 1) {
4144             return;
4145         }
4146         this.setActiveItem(this.navItems[i-1]);
4147     },
4148     clearWasActive : function(except) {
4149         Roo.each(this.navItems, function(e) {
4150             if (e.tabId != except.tabId && e.was_active) {
4151                e.was_active = false;
4152                return false;
4153             }
4154             return true;
4155             
4156         });
4157     },
4158     getWasActive : function ()
4159     {
4160         var r = false;
4161         Roo.each(this.navItems, function(e) {
4162             if (e.was_active) {
4163                r = e;
4164                return false;
4165             }
4166             return true;
4167             
4168         });
4169         return r;
4170     }
4171     
4172     
4173 });
4174
4175  
4176 Roo.apply(Roo.bootstrap.NavGroup, {
4177     
4178     groups: {},
4179      /**
4180     * register a Navigation Group
4181     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4182     */
4183     register : function(navgrp)
4184     {
4185         this.groups[navgrp.navId] = navgrp;
4186         
4187     },
4188     /**
4189     * fetch a Navigation Group based on the navigation ID
4190     * @param {string} the navgroup to add
4191     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4192     */
4193     get: function(navId) {
4194         if (typeof(this.groups[navId]) == 'undefined') {
4195             return false;
4196             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4197         }
4198         return this.groups[navId] ;
4199     }
4200     
4201     
4202     
4203 });
4204
4205  /*
4206  * - LGPL
4207  *
4208  * row
4209  * 
4210  */
4211
4212 /**
4213  * @class Roo.bootstrap.NavItem
4214  * @extends Roo.bootstrap.Component
4215  * Bootstrap Navbar.NavItem class
4216  * @cfg {String} href  link to
4217  * @cfg {String} html content of button
4218  * @cfg {String} badge text inside badge
4219  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4220  * @cfg {String} glyphicon name of glyphicon
4221  * @cfg {String} icon name of font awesome icon
4222  * @cfg {Boolean} active Is item active
4223  * @cfg {Boolean} disabled Is item disabled
4224  
4225  * @cfg {Boolean} preventDefault (true | false) default false
4226  * @cfg {String} tabId the tab that this item activates.
4227  * @cfg {String} tagtype (a|span) render as a href or span?
4228  * @cfg {Boolean} animateRef (true|false) link to element default false  
4229   
4230  * @constructor
4231  * Create a new Navbar Item
4232  * @param {Object} config The config object
4233  */
4234 Roo.bootstrap.NavItem = function(config){
4235     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4236     this.addEvents({
4237         // raw events
4238         /**
4239          * @event click
4240          * The raw click event for the entire grid.
4241          * @param {Roo.EventObject} e
4242          */
4243         "click" : true,
4244          /**
4245             * @event changed
4246             * Fires when the active item active state changes
4247             * @param {Roo.bootstrap.NavItem} this
4248             * @param {boolean} state the new state
4249              
4250          */
4251         'changed': true,
4252         /**
4253             * @event scrollto
4254             * Fires when scroll to element
4255             * @param {Roo.bootstrap.NavItem} this
4256             * @param {Object} options
4257             * @param {Roo.EventObject} e
4258              
4259          */
4260         'scrollto': true
4261     });
4262    
4263 };
4264
4265 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4266     
4267     href: false,
4268     html: '',
4269     badge: '',
4270     icon: false,
4271     glyphicon: false,
4272     active: false,
4273     preventDefault : false,
4274     tabId : false,
4275     tagtype : 'a',
4276     disabled : false,
4277     animateRef : false,
4278     was_active : false,
4279     
4280     getAutoCreate : function(){
4281          
4282         var cfg = {
4283             tag: 'li',
4284             cls: 'nav-item'
4285             
4286         };
4287         
4288         if (this.active) {
4289             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4290         }
4291         if (this.disabled) {
4292             cfg.cls += ' disabled';
4293         }
4294         
4295         if (this.href || this.html || this.glyphicon || this.icon) {
4296             cfg.cn = [
4297                 {
4298                     tag: this.tagtype,
4299                     href : this.href || "#",
4300                     html: this.html || ''
4301                 }
4302             ];
4303             
4304             if (this.icon) {
4305                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4306             }
4307
4308             if(this.glyphicon) {
4309                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4310             }
4311             
4312             if (this.menu) {
4313                 
4314                 cfg.cn[0].html += " <span class='caret'></span>";
4315              
4316             }
4317             
4318             if (this.badge !== '') {
4319                  
4320                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4321             }
4322         }
4323         
4324         
4325         
4326         return cfg;
4327     },
4328     initEvents: function() 
4329     {
4330         if (typeof (this.menu) != 'undefined') {
4331             this.menu.parentType = this.xtype;
4332             this.menu.triggerEl = this.el;
4333             this.menu = this.addxtype(Roo.apply({}, this.menu));
4334         }
4335         
4336         this.el.select('a',true).on('click', this.onClick, this);
4337         
4338         if(this.tagtype == 'span'){
4339             this.el.select('span',true).on('click', this.onClick, this);
4340         }
4341        
4342         // at this point parent should be available..
4343         this.parent().register(this);
4344     },
4345     
4346     onClick : function(e)
4347     {
4348         if (e.getTarget('.dropdown-menu-item')) {
4349             // did you click on a menu itemm.... - then don't trigger onclick..
4350             return;
4351         }
4352         
4353         if(
4354                 this.preventDefault || 
4355                 this.href == '#' 
4356         ){
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359         }
4360         
4361         if (this.disabled) {
4362             return;
4363         }
4364         
4365         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4366         if (tg && tg.transition) {
4367             Roo.log("waiting for the transitionend");
4368             return;
4369         }
4370         
4371         
4372         
4373         //Roo.log("fire event clicked");
4374         if(this.fireEvent('click', this, e) === false){
4375             return;
4376         };
4377         
4378         if(this.tagtype == 'span'){
4379             return;
4380         }
4381         
4382         //Roo.log(this.href);
4383         var ael = this.el.select('a',true).first();
4384         //Roo.log(ael);
4385         
4386         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4387             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4388             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4389                 return; // ignore... - it's a 'hash' to another page.
4390             }
4391             Roo.log("NavItem - prevent Default?");
4392             e.preventDefault();
4393             this.scrollToElement(e);
4394         }
4395         
4396         
4397         var p =  this.parent();
4398    
4399         if (['tabs','pills'].indexOf(p.type)!==-1) {
4400             if (typeof(p.setActiveItem) !== 'undefined') {
4401                 p.setActiveItem(this);
4402             }
4403         }
4404         
4405         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4406         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4407             // remove the collapsed menu expand...
4408             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4409         }
4410     },
4411     
4412     isActive: function () {
4413         return this.active
4414     },
4415     setActive : function(state, fire, is_was_active)
4416     {
4417         if (this.active && !state && this.navId) {
4418             this.was_active = true;
4419             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4420             if (nv) {
4421                 nv.clearWasActive(this);
4422             }
4423             
4424         }
4425         this.active = state;
4426         
4427         if (!state ) {
4428             this.el.removeClass('active');
4429         } else if (!this.el.hasClass('active')) {
4430             this.el.addClass('active');
4431         }
4432         if (fire) {
4433             this.fireEvent('changed', this, state);
4434         }
4435         
4436         // show a panel if it's registered and related..
4437         
4438         if (!this.navId || !this.tabId || !state || is_was_active) {
4439             return;
4440         }
4441         
4442         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4443         if (!tg) {
4444             return;
4445         }
4446         var pan = tg.getPanelByName(this.tabId);
4447         if (!pan) {
4448             return;
4449         }
4450         // if we can not flip to new panel - go back to old nav highlight..
4451         if (false == tg.showPanel(pan)) {
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 var onav = nv.getWasActive();
4455                 if (onav) {
4456                     onav.setActive(true, false, true);
4457                 }
4458             }
4459             
4460         }
4461         
4462         
4463         
4464     },
4465      // this should not be here...
4466     setDisabled : function(state)
4467     {
4468         this.disabled = state;
4469         if (!state ) {
4470             this.el.removeClass('disabled');
4471         } else if (!this.el.hasClass('disabled')) {
4472             this.el.addClass('disabled');
4473         }
4474         
4475     },
4476     
4477     /**
4478      * Fetch the element to display the tooltip on.
4479      * @return {Roo.Element} defaults to this.el
4480      */
4481     tooltipEl : function()
4482     {
4483         return this.el.select('' + this.tagtype + '', true).first();
4484     },
4485     
4486     scrollToElement : function(e)
4487     {
4488         var c = document.body;
4489         
4490         /*
4491          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4492          */
4493         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4494             c = document.documentElement;
4495         }
4496         
4497         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4498         
4499         if(!target){
4500             return;
4501         }
4502
4503         var o = target.calcOffsetsTo(c);
4504         
4505         var options = {
4506             target : target,
4507             value : o[1]
4508         };
4509         
4510         this.fireEvent('scrollto', this, options, e);
4511         
4512         Roo.get(c).scrollTo('top', options.value, true);
4513         
4514         return;
4515     }
4516 });
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * sidebar item
4523  *
4524  *  li
4525  *    <span> icon </span>
4526  *    <span> text </span>
4527  *    <span>badge </span>
4528  */
4529
4530 /**
4531  * @class Roo.bootstrap.NavSidebarItem
4532  * @extends Roo.bootstrap.NavItem
4533  * Bootstrap Navbar.NavSidebarItem class
4534  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4535  * {bool} open is the menu open
4536  * @constructor
4537  * Create a new Navbar Button
4538  * @param {Object} config The config object
4539  */
4540 Roo.bootstrap.NavSidebarItem = function(config){
4541     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4542     this.addEvents({
4543         // raw events
4544         /**
4545          * @event click
4546          * The raw click event for the entire grid.
4547          * @param {Roo.EventObject} e
4548          */
4549         "click" : true,
4550          /**
4551             * @event changed
4552             * Fires when the active item active state changes
4553             * @param {Roo.bootstrap.NavSidebarItem} this
4554             * @param {boolean} state the new state
4555              
4556          */
4557         'changed': true
4558     });
4559    
4560 };
4561
4562 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4563     
4564     badgeWeight : 'default',
4565     
4566     open: false,
4567     
4568     getAutoCreate : function(){
4569         
4570         
4571         var a = {
4572                 tag: 'a',
4573                 href : this.href || '#',
4574                 cls: '',
4575                 html : '',
4576                 cn : []
4577         };
4578         var cfg = {
4579             tag: 'li',
4580             cls: '',
4581             cn: [ a ]
4582         };
4583         var span = {
4584             tag: 'span',
4585             html : this.html || ''
4586         };
4587         
4588         
4589         if (this.active) {
4590             cfg.cls += ' active';
4591         }
4592         
4593         if (this.disabled) {
4594             cfg.cls += ' disabled';
4595         }
4596         if (this.open) {
4597             cfg.cls += ' open x-open';
4598         }
4599         // left icon..
4600         if (this.glyphicon || this.icon) {
4601             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4602             a.cn.push({ tag : 'i', cls : c }) ;
4603         }
4604         // html..
4605         a.cn.push(span);
4606         // then badge..
4607         if (this.badge !== '') {
4608             
4609             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4610         }
4611         // fi
4612         if (this.menu) {
4613             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4614             a.cls += 'dropdown-toggle treeview' ;
4615         }
4616         
4617         return cfg;
4618          
4619            
4620     },
4621     
4622     initEvents : function()
4623     { 
4624         if (typeof (this.menu) != 'undefined') {
4625             this.menu.parentType = this.xtype;
4626             this.menu.triggerEl = this.el;
4627             this.menu = this.addxtype(Roo.apply({}, this.menu));
4628         }
4629         
4630         this.el.on('click', this.onClick, this);
4631        
4632     
4633         if(this.badge !== ''){
4634  
4635             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4636         }
4637         
4638     },
4639     
4640     onClick : function(e)
4641     {
4642         if(this.disabled){
4643             e.preventDefault();
4644             return;
4645         }
4646         
4647         if(this.preventDefault){
4648             e.preventDefault();
4649         }
4650         
4651         this.fireEvent('click', this);
4652     },
4653     
4654     disable : function()
4655     {
4656         this.setDisabled(true);
4657     },
4658     
4659     enable : function()
4660     {
4661         this.setDisabled(false);
4662     },
4663     
4664     setDisabled : function(state)
4665     {
4666         if(this.disabled == state){
4667             return;
4668         }
4669         
4670         this.disabled = state;
4671         
4672         if (state) {
4673             this.el.addClass('disabled');
4674             return;
4675         }
4676         
4677         this.el.removeClass('disabled');
4678         
4679         return;
4680     },
4681     
4682     setActive : function(state)
4683     {
4684         if(this.active == state){
4685             return;
4686         }
4687         
4688         this.active = state;
4689         
4690         if (state) {
4691             this.el.addClass('active');
4692             return;
4693         }
4694         
4695         this.el.removeClass('active');
4696         
4697         return;
4698     },
4699     
4700     isActive: function () 
4701     {
4702         return this.active;
4703     },
4704     
4705     setBadge : function(str)
4706     {
4707         if(!this.badgeEl){
4708             return;
4709         }
4710         
4711         this.badgeEl.dom.innerHTML = str;
4712     }
4713     
4714    
4715      
4716  
4717 });
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * row
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Row
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Row class (contains columns...)
4731  * 
4732  * @constructor
4733  * Create a new Row
4734  * @param {Object} config The config object
4735  */
4736
4737 Roo.bootstrap.Row = function(config){
4738     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4739 };
4740
4741 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4742     
4743     getAutoCreate : function(){
4744        return {
4745             cls: 'row clearfix'
4746        };
4747     }
4748     
4749     
4750 });
4751
4752  
4753
4754  /*
4755  * - LGPL
4756  *
4757  * element
4758  * 
4759  */
4760
4761 /**
4762  * @class Roo.bootstrap.Element
4763  * @extends Roo.bootstrap.Component
4764  * Bootstrap Element class
4765  * @cfg {String} html contents of the element
4766  * @cfg {String} tag tag of the element
4767  * @cfg {String} cls class of the element
4768  * @cfg {Boolean} preventDefault (true|false) default false
4769  * @cfg {Boolean} clickable (true|false) default false
4770  * 
4771  * @constructor
4772  * Create a new Element
4773  * @param {Object} config The config object
4774  */
4775
4776 Roo.bootstrap.Element = function(config){
4777     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4778     
4779     this.addEvents({
4780         // raw events
4781         /**
4782          * @event click
4783          * When a element is chick
4784          * @param {Roo.bootstrap.Element} this
4785          * @param {Roo.EventObject} e
4786          */
4787         "click" : true
4788     });
4789 };
4790
4791 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4792     
4793     tag: 'div',
4794     cls: '',
4795     html: '',
4796     preventDefault: false, 
4797     clickable: false,
4798     
4799     getAutoCreate : function(){
4800         
4801         var cfg = {
4802             tag: this.tag,
4803             cls: this.cls,
4804             html: this.html
4805         };
4806         
4807         return cfg;
4808     },
4809     
4810     initEvents: function() 
4811     {
4812         Roo.bootstrap.Element.superclass.initEvents.call(this);
4813         
4814         if(this.clickable){
4815             this.el.on('click', this.onClick, this);
4816         }
4817         
4818     },
4819     
4820     onClick : function(e)
4821     {
4822         if(this.preventDefault){
4823             e.preventDefault();
4824         }
4825         
4826         this.fireEvent('click', this, e);
4827     },
4828     
4829     getValue : function()
4830     {
4831         return this.el.dom.innerHTML;
4832     },
4833     
4834     setValue : function(value)
4835     {
4836         this.el.dom.innerHTML = value;
4837     }
4838    
4839 });
4840
4841  
4842
4843  /*
4844  * - LGPL
4845  *
4846  * pagination
4847  * 
4848  */
4849
4850 /**
4851  * @class Roo.bootstrap.Pagination
4852  * @extends Roo.bootstrap.Component
4853  * Bootstrap Pagination class
4854  * @cfg {String} size xs | sm | md | lg
4855  * @cfg {Boolean} inverse false | true
4856  * 
4857  * @constructor
4858  * Create a new Pagination
4859  * @param {Object} config The config object
4860  */
4861
4862 Roo.bootstrap.Pagination = function(config){
4863     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4864 };
4865
4866 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4867     
4868     cls: false,
4869     size: false,
4870     inverse: false,
4871     
4872     getAutoCreate : function(){
4873         var cfg = {
4874             tag: 'ul',
4875                 cls: 'pagination'
4876         };
4877         if (this.inverse) {
4878             cfg.cls += ' inverse';
4879         }
4880         if (this.html) {
4881             cfg.html=this.html;
4882         }
4883         if (this.cls) {
4884             cfg.cls += " " + this.cls;
4885         }
4886         return cfg;
4887     }
4888    
4889 });
4890
4891  
4892
4893  /*
4894  * - LGPL
4895  *
4896  * Pagination item
4897  * 
4898  */
4899
4900
4901 /**
4902  * @class Roo.bootstrap.PaginationItem
4903  * @extends Roo.bootstrap.Component
4904  * Bootstrap PaginationItem class
4905  * @cfg {String} html text
4906  * @cfg {String} href the link
4907  * @cfg {Boolean} preventDefault (true | false) default true
4908  * @cfg {Boolean} active (true | false) default false
4909  * @cfg {Boolean} disabled default false
4910  * 
4911  * 
4912  * @constructor
4913  * Create a new PaginationItem
4914  * @param {Object} config The config object
4915  */
4916
4917
4918 Roo.bootstrap.PaginationItem = function(config){
4919     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4920     this.addEvents({
4921         // raw events
4922         /**
4923          * @event click
4924          * The raw click event for the entire grid.
4925          * @param {Roo.EventObject} e
4926          */
4927         "click" : true
4928     });
4929 };
4930
4931 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4932     
4933     href : false,
4934     html : false,
4935     preventDefault: true,
4936     active : false,
4937     cls : false,
4938     disabled: false,
4939     
4940     getAutoCreate : function(){
4941         var cfg= {
4942             tag: 'li',
4943             cn: [
4944                 {
4945                     tag : 'a',
4946                     href : this.href ? this.href : '#',
4947                     html : this.html ? this.html : ''
4948                 }
4949             ]
4950         };
4951         
4952         if(this.cls){
4953             cfg.cls = this.cls;
4954         }
4955         
4956         if(this.disabled){
4957             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4958         }
4959         
4960         if(this.active){
4961             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4962         }
4963         
4964         return cfg;
4965     },
4966     
4967     initEvents: function() {
4968         
4969         this.el.on('click', this.onClick, this);
4970         
4971     },
4972     onClick : function(e)
4973     {
4974         Roo.log('PaginationItem on click ');
4975         if(this.preventDefault){
4976             e.preventDefault();
4977         }
4978         
4979         if(this.disabled){
4980             return;
4981         }
4982         
4983         this.fireEvent('click', this, e);
4984     }
4985    
4986 });
4987
4988  
4989
4990  /*
4991  * - LGPL
4992  *
4993  * slider
4994  * 
4995  */
4996
4997
4998 /**
4999  * @class Roo.bootstrap.Slider
5000  * @extends Roo.bootstrap.Component
5001  * Bootstrap Slider class
5002  *    
5003  * @constructor
5004  * Create a new Slider
5005  * @param {Object} config The config object
5006  */
5007
5008 Roo.bootstrap.Slider = function(config){
5009     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5010 };
5011
5012 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5013     
5014     getAutoCreate : function(){
5015         
5016         var cfg = {
5017             tag: 'div',
5018             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5019             cn: [
5020                 {
5021                     tag: 'a',
5022                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5023                 }
5024             ]
5025         };
5026         
5027         return cfg;
5028     }
5029    
5030 });
5031
5032  /*
5033  * Based on:
5034  * Ext JS Library 1.1.1
5035  * Copyright(c) 2006-2007, Ext JS, LLC.
5036  *
5037  * Originally Released Under LGPL - original licence link has changed is not relivant.
5038  *
5039  * Fork - LGPL
5040  * <script type="text/javascript">
5041  */
5042  
5043
5044 /**
5045  * @class Roo.grid.ColumnModel
5046  * @extends Roo.util.Observable
5047  * This is the default implementation of a ColumnModel used by the Grid. It defines
5048  * the columns in the grid.
5049  * <br>Usage:<br>
5050  <pre><code>
5051  var colModel = new Roo.grid.ColumnModel([
5052         {header: "Ticker", width: 60, sortable: true, locked: true},
5053         {header: "Company Name", width: 150, sortable: true},
5054         {header: "Market Cap.", width: 100, sortable: true},
5055         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5056         {header: "Employees", width: 100, sortable: true, resizable: false}
5057  ]);
5058  </code></pre>
5059  * <p>
5060  
5061  * The config options listed for this class are options which may appear in each
5062  * individual column definition.
5063  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5064  * @constructor
5065  * @param {Object} config An Array of column config objects. See this class's
5066  * config objects for details.
5067 */
5068 Roo.grid.ColumnModel = function(config){
5069         /**
5070      * The config passed into the constructor
5071      */
5072     this.config = config;
5073     this.lookup = {};
5074
5075     // if no id, create one
5076     // if the column does not have a dataIndex mapping,
5077     // map it to the order it is in the config
5078     for(var i = 0, len = config.length; i < len; i++){
5079         var c = config[i];
5080         if(typeof c.dataIndex == "undefined"){
5081             c.dataIndex = i;
5082         }
5083         if(typeof c.renderer == "string"){
5084             c.renderer = Roo.util.Format[c.renderer];
5085         }
5086         if(typeof c.id == "undefined"){
5087             c.id = Roo.id();
5088         }
5089         if(c.editor && c.editor.xtype){
5090             c.editor  = Roo.factory(c.editor, Roo.grid);
5091         }
5092         if(c.editor && c.editor.isFormField){
5093             c.editor = new Roo.grid.GridEditor(c.editor);
5094         }
5095         this.lookup[c.id] = c;
5096     }
5097
5098     /**
5099      * The width of columns which have no width specified (defaults to 100)
5100      * @type Number
5101      */
5102     this.defaultWidth = 100;
5103
5104     /**
5105      * Default sortable of columns which have no sortable specified (defaults to false)
5106      * @type Boolean
5107      */
5108     this.defaultSortable = false;
5109
5110     this.addEvents({
5111         /**
5112              * @event widthchange
5113              * Fires when the width of a column changes.
5114              * @param {ColumnModel} this
5115              * @param {Number} columnIndex The column index
5116              * @param {Number} newWidth The new width
5117              */
5118             "widthchange": true,
5119         /**
5120              * @event headerchange
5121              * Fires when the text of a header changes.
5122              * @param {ColumnModel} this
5123              * @param {Number} columnIndex The column index
5124              * @param {Number} newText The new header text
5125              */
5126             "headerchange": true,
5127         /**
5128              * @event hiddenchange
5129              * Fires when a column is hidden or "unhidden".
5130              * @param {ColumnModel} this
5131              * @param {Number} columnIndex The column index
5132              * @param {Boolean} hidden true if hidden, false otherwise
5133              */
5134             "hiddenchange": true,
5135             /**
5136          * @event columnmoved
5137          * Fires when a column is moved.
5138          * @param {ColumnModel} this
5139          * @param {Number} oldIndex
5140          * @param {Number} newIndex
5141          */
5142         "columnmoved" : true,
5143         /**
5144          * @event columlockchange
5145          * Fires when a column's locked state is changed
5146          * @param {ColumnModel} this
5147          * @param {Number} colIndex
5148          * @param {Boolean} locked true if locked
5149          */
5150         "columnlockchange" : true
5151     });
5152     Roo.grid.ColumnModel.superclass.constructor.call(this);
5153 };
5154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5155     /**
5156      * @cfg {String} header The header text to display in the Grid view.
5157      */
5158     /**
5159      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5160      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5161      * specified, the column's index is used as an index into the Record's data Array.
5162      */
5163     /**
5164      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5165      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5166      */
5167     /**
5168      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5169      * Defaults to the value of the {@link #defaultSortable} property.
5170      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5171      */
5172     /**
5173      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5174      */
5175     /**
5176      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5177      */
5178     /**
5179      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5180      */
5181     /**
5182      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5183      */
5184     /**
5185      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5186      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5187      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5188      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5189      */
5190        /**
5191      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5192      */
5193     /**
5194      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5195      */
5196     /**
5197      * @cfg {String} cursor (Optional)
5198      */
5199     /**
5200      * @cfg {String} tooltip (Optional)
5201      */
5202     /**
5203      * @cfg {Number} xs (Optional)
5204      */
5205     /**
5206      * @cfg {Number} sm (Optional)
5207      */
5208     /**
5209      * @cfg {Number} md (Optional)
5210      */
5211     /**
5212      * @cfg {Number} lg (Optional)
5213      */
5214     /**
5215      * Returns the id of the column at the specified index.
5216      * @param {Number} index The column index
5217      * @return {String} the id
5218      */
5219     getColumnId : function(index){
5220         return this.config[index].id;
5221     },
5222
5223     /**
5224      * Returns the column for a specified id.
5225      * @param {String} id The column id
5226      * @return {Object} the column
5227      */
5228     getColumnById : function(id){
5229         return this.lookup[id];
5230     },
5231
5232     
5233     /**
5234      * Returns the column for a specified dataIndex.
5235      * @param {String} dataIndex The column dataIndex
5236      * @return {Object|Boolean} the column or false if not found
5237      */
5238     getColumnByDataIndex: function(dataIndex){
5239         var index = this.findColumnIndex(dataIndex);
5240         return index > -1 ? this.config[index] : false;
5241     },
5242     
5243     /**
5244      * Returns the index for a specified column id.
5245      * @param {String} id The column id
5246      * @return {Number} the index, or -1 if not found
5247      */
5248     getIndexById : function(id){
5249         for(var i = 0, len = this.config.length; i < len; i++){
5250             if(this.config[i].id == id){
5251                 return i;
5252             }
5253         }
5254         return -1;
5255     },
5256     
5257     /**
5258      * Returns the index for a specified column dataIndex.
5259      * @param {String} dataIndex The column dataIndex
5260      * @return {Number} the index, or -1 if not found
5261      */
5262     
5263     findColumnIndex : function(dataIndex){
5264         for(var i = 0, len = this.config.length; i < len; i++){
5265             if(this.config[i].dataIndex == dataIndex){
5266                 return i;
5267             }
5268         }
5269         return -1;
5270     },
5271     
5272     
5273     moveColumn : function(oldIndex, newIndex){
5274         var c = this.config[oldIndex];
5275         this.config.splice(oldIndex, 1);
5276         this.config.splice(newIndex, 0, c);
5277         this.dataMap = null;
5278         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5279     },
5280
5281     isLocked : function(colIndex){
5282         return this.config[colIndex].locked === true;
5283     },
5284
5285     setLocked : function(colIndex, value, suppressEvent){
5286         if(this.isLocked(colIndex) == value){
5287             return;
5288         }
5289         this.config[colIndex].locked = value;
5290         if(!suppressEvent){
5291             this.fireEvent("columnlockchange", this, colIndex, value);
5292         }
5293     },
5294
5295     getTotalLockedWidth : function(){
5296         var totalWidth = 0;
5297         for(var i = 0; i < this.config.length; i++){
5298             if(this.isLocked(i) && !this.isHidden(i)){
5299                 this.totalWidth += this.getColumnWidth(i);
5300             }
5301         }
5302         return totalWidth;
5303     },
5304
5305     getLockedCount : function(){
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             if(!this.isLocked(i)){
5308                 return i;
5309             }
5310         }
5311         
5312         return this.config.length;
5313     },
5314
5315     /**
5316      * Returns the number of columns.
5317      * @return {Number}
5318      */
5319     getColumnCount : function(visibleOnly){
5320         if(visibleOnly === true){
5321             var c = 0;
5322             for(var i = 0, len = this.config.length; i < len; i++){
5323                 if(!this.isHidden(i)){
5324                     c++;
5325                 }
5326             }
5327             return c;
5328         }
5329         return this.config.length;
5330     },
5331
5332     /**
5333      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5334      * @param {Function} fn
5335      * @param {Object} scope (optional)
5336      * @return {Array} result
5337      */
5338     getColumnsBy : function(fn, scope){
5339         var r = [];
5340         for(var i = 0, len = this.config.length; i < len; i++){
5341             var c = this.config[i];
5342             if(fn.call(scope||this, c, i) === true){
5343                 r[r.length] = c;
5344             }
5345         }
5346         return r;
5347     },
5348
5349     /**
5350      * Returns true if the specified column is sortable.
5351      * @param {Number} col The column index
5352      * @return {Boolean}
5353      */
5354     isSortable : function(col){
5355         if(typeof this.config[col].sortable == "undefined"){
5356             return this.defaultSortable;
5357         }
5358         return this.config[col].sortable;
5359     },
5360
5361     /**
5362      * Returns the rendering (formatting) function defined for the column.
5363      * @param {Number} col The column index.
5364      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5365      */
5366     getRenderer : function(col){
5367         if(!this.config[col].renderer){
5368             return Roo.grid.ColumnModel.defaultRenderer;
5369         }
5370         return this.config[col].renderer;
5371     },
5372
5373     /**
5374      * Sets the rendering (formatting) function for a column.
5375      * @param {Number} col The column index
5376      * @param {Function} fn The function to use to process the cell's raw data
5377      * to return HTML markup for the grid view. The render function is called with
5378      * the following parameters:<ul>
5379      * <li>Data value.</li>
5380      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5381      * <li>css A CSS style string to apply to the table cell.</li>
5382      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5383      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5384      * <li>Row index</li>
5385      * <li>Column index</li>
5386      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5387      */
5388     setRenderer : function(col, fn){
5389         this.config[col].renderer = fn;
5390     },
5391
5392     /**
5393      * Returns the width for the specified column.
5394      * @param {Number} col The column index
5395      * @return {Number}
5396      */
5397     getColumnWidth : function(col){
5398         return this.config[col].width * 1 || this.defaultWidth;
5399     },
5400
5401     /**
5402      * Sets the width for a column.
5403      * @param {Number} col The column index
5404      * @param {Number} width The new width
5405      */
5406     setColumnWidth : function(col, width, suppressEvent){
5407         this.config[col].width = width;
5408         this.totalWidth = null;
5409         if(!suppressEvent){
5410              this.fireEvent("widthchange", this, col, width);
5411         }
5412     },
5413
5414     /**
5415      * Returns the total width of all columns.
5416      * @param {Boolean} includeHidden True to include hidden column widths
5417      * @return {Number}
5418      */
5419     getTotalWidth : function(includeHidden){
5420         if(!this.totalWidth){
5421             this.totalWidth = 0;
5422             for(var i = 0, len = this.config.length; i < len; i++){
5423                 if(includeHidden || !this.isHidden(i)){
5424                     this.totalWidth += this.getColumnWidth(i);
5425                 }
5426             }
5427         }
5428         return this.totalWidth;
5429     },
5430
5431     /**
5432      * Returns the header for the specified column.
5433      * @param {Number} col The column index
5434      * @return {String}
5435      */
5436     getColumnHeader : function(col){
5437         return this.config[col].header;
5438     },
5439
5440     /**
5441      * Sets the header for a column.
5442      * @param {Number} col The column index
5443      * @param {String} header The new header
5444      */
5445     setColumnHeader : function(col, header){
5446         this.config[col].header = header;
5447         this.fireEvent("headerchange", this, col, header);
5448     },
5449
5450     /**
5451      * Returns the tooltip for the specified column.
5452      * @param {Number} col The column index
5453      * @return {String}
5454      */
5455     getColumnTooltip : function(col){
5456             return this.config[col].tooltip;
5457     },
5458     /**
5459      * Sets the tooltip for a column.
5460      * @param {Number} col The column index
5461      * @param {String} tooltip The new tooltip
5462      */
5463     setColumnTooltip : function(col, tooltip){
5464             this.config[col].tooltip = tooltip;
5465     },
5466
5467     /**
5468      * Returns the dataIndex for the specified column.
5469      * @param {Number} col The column index
5470      * @return {Number}
5471      */
5472     getDataIndex : function(col){
5473         return this.config[col].dataIndex;
5474     },
5475
5476     /**
5477      * Sets the dataIndex for a column.
5478      * @param {Number} col The column index
5479      * @param {Number} dataIndex The new dataIndex
5480      */
5481     setDataIndex : function(col, dataIndex){
5482         this.config[col].dataIndex = dataIndex;
5483     },
5484
5485     
5486     
5487     /**
5488      * Returns true if the cell is editable.
5489      * @param {Number} colIndex The column index
5490      * @param {Number} rowIndex The row index - this is nto actually used..?
5491      * @return {Boolean}
5492      */
5493     isCellEditable : function(colIndex, rowIndex){
5494         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5495     },
5496
5497     /**
5498      * Returns the editor defined for the cell/column.
5499      * return false or null to disable editing.
5500      * @param {Number} colIndex The column index
5501      * @param {Number} rowIndex The row index
5502      * @return {Object}
5503      */
5504     getCellEditor : function(colIndex, rowIndex){
5505         return this.config[colIndex].editor;
5506     },
5507
5508     /**
5509      * Sets if a column is editable.
5510      * @param {Number} col The column index
5511      * @param {Boolean} editable True if the column is editable
5512      */
5513     setEditable : function(col, editable){
5514         this.config[col].editable = editable;
5515     },
5516
5517
5518     /**
5519      * Returns true if the column is hidden.
5520      * @param {Number} colIndex The column index
5521      * @return {Boolean}
5522      */
5523     isHidden : function(colIndex){
5524         return this.config[colIndex].hidden;
5525     },
5526
5527
5528     /**
5529      * Returns true if the column width cannot be changed
5530      */
5531     isFixed : function(colIndex){
5532         return this.config[colIndex].fixed;
5533     },
5534
5535     /**
5536      * Returns true if the column can be resized
5537      * @return {Boolean}
5538      */
5539     isResizable : function(colIndex){
5540         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5541     },
5542     /**
5543      * Sets if a column is hidden.
5544      * @param {Number} colIndex The column index
5545      * @param {Boolean} hidden True if the column is hidden
5546      */
5547     setHidden : function(colIndex, hidden){
5548         this.config[colIndex].hidden = hidden;
5549         this.totalWidth = null;
5550         this.fireEvent("hiddenchange", this, colIndex, hidden);
5551     },
5552
5553     /**
5554      * Sets the editor for a column.
5555      * @param {Number} col The column index
5556      * @param {Object} editor The editor object
5557      */
5558     setEditor : function(col, editor){
5559         this.config[col].editor = editor;
5560     }
5561 });
5562
5563 Roo.grid.ColumnModel.defaultRenderer = function(value)
5564 {
5565     if(typeof value == "object") {
5566         return value;
5567     }
5568         if(typeof value == "string" && value.length < 1){
5569             return "&#160;";
5570         }
5571     
5572         return String.format("{0}", value);
5573 };
5574
5575 // Alias for backwards compatibility
5576 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5577 /*
5578  * Based on:
5579  * Ext JS Library 1.1.1
5580  * Copyright(c) 2006-2007, Ext JS, LLC.
5581  *
5582  * Originally Released Under LGPL - original licence link has changed is not relivant.
5583  *
5584  * Fork - LGPL
5585  * <script type="text/javascript">
5586  */
5587  
5588 /**
5589  * @class Roo.LoadMask
5590  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5591  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5592  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5593  * element's UpdateManager load indicator and will be destroyed after the initial load.
5594  * @constructor
5595  * Create a new LoadMask
5596  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5597  * @param {Object} config The config object
5598  */
5599 Roo.LoadMask = function(el, config){
5600     this.el = Roo.get(el);
5601     Roo.apply(this, config);
5602     if(this.store){
5603         this.store.on('beforeload', this.onBeforeLoad, this);
5604         this.store.on('load', this.onLoad, this);
5605         this.store.on('loadexception', this.onLoadException, this);
5606         this.removeMask = false;
5607     }else{
5608         var um = this.el.getUpdateManager();
5609         um.showLoadIndicator = false; // disable the default indicator
5610         um.on('beforeupdate', this.onBeforeLoad, this);
5611         um.on('update', this.onLoad, this);
5612         um.on('failure', this.onLoad, this);
5613         this.removeMask = true;
5614     }
5615 };
5616
5617 Roo.LoadMask.prototype = {
5618     /**
5619      * @cfg {Boolean} removeMask
5620      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5621      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5622      */
5623     /**
5624      * @cfg {String} msg
5625      * The text to display in a centered loading message box (defaults to 'Loading...')
5626      */
5627     msg : 'Loading...',
5628     /**
5629      * @cfg {String} msgCls
5630      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5631      */
5632     msgCls : 'x-mask-loading',
5633
5634     /**
5635      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5636      * @type Boolean
5637      */
5638     disabled: false,
5639
5640     /**
5641      * Disables the mask to prevent it from being displayed
5642      */
5643     disable : function(){
5644        this.disabled = true;
5645     },
5646
5647     /**
5648      * Enables the mask so that it can be displayed
5649      */
5650     enable : function(){
5651         this.disabled = false;
5652     },
5653     
5654     onLoadException : function()
5655     {
5656         Roo.log(arguments);
5657         
5658         if (typeof(arguments[3]) != 'undefined') {
5659             Roo.MessageBox.alert("Error loading",arguments[3]);
5660         } 
5661         /*
5662         try {
5663             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5664                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5665             }   
5666         } catch(e) {
5667             
5668         }
5669         */
5670     
5671         
5672         
5673         this.el.unmask(this.removeMask);
5674     },
5675     // private
5676     onLoad : function()
5677     {
5678         this.el.unmask(this.removeMask);
5679     },
5680
5681     // private
5682     onBeforeLoad : function(){
5683         if(!this.disabled){
5684             this.el.mask(this.msg, this.msgCls);
5685         }
5686     },
5687
5688     // private
5689     destroy : function(){
5690         if(this.store){
5691             this.store.un('beforeload', this.onBeforeLoad, this);
5692             this.store.un('load', this.onLoad, this);
5693             this.store.un('loadexception', this.onLoadException, this);
5694         }else{
5695             var um = this.el.getUpdateManager();
5696             um.un('beforeupdate', this.onBeforeLoad, this);
5697             um.un('update', this.onLoad, this);
5698             um.un('failure', this.onLoad, this);
5699         }
5700     }
5701 };/*
5702  * - LGPL
5703  *
5704  * table
5705  * 
5706  */
5707
5708 /**
5709  * @class Roo.bootstrap.Table
5710  * @extends Roo.bootstrap.Component
5711  * Bootstrap Table class
5712  * @cfg {String} cls table class
5713  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5714  * @cfg {String} bgcolor Specifies the background color for a table
5715  * @cfg {Number} border Specifies whether the table cells should have borders or not
5716  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5717  * @cfg {Number} cellspacing Specifies the space between cells
5718  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5719  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5720  * @cfg {String} sortable Specifies that the table should be sortable
5721  * @cfg {String} summary Specifies a summary of the content of a table
5722  * @cfg {Number} width Specifies the width of a table
5723  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5724  * 
5725  * @cfg {boolean} striped Should the rows be alternative striped
5726  * @cfg {boolean} bordered Add borders to the table
5727  * @cfg {boolean} hover Add hover highlighting
5728  * @cfg {boolean} condensed Format condensed
5729  * @cfg {boolean} responsive Format condensed
5730  * @cfg {Boolean} loadMask (true|false) default false
5731  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5732  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5733  * @cfg {Boolean} rowSelection (true|false) default false
5734  * @cfg {Boolean} cellSelection (true|false) default false
5735  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5736  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5737  
5738  * 
5739  * @constructor
5740  * Create a new Table
5741  * @param {Object} config The config object
5742  */
5743
5744 Roo.bootstrap.Table = function(config){
5745     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5746     
5747   
5748     
5749     // BC...
5750     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5751     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5752     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5753     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5754     
5755     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5756     if (this.sm) {
5757         this.sm.grid = this;
5758         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5759         this.sm = this.selModel;
5760         this.sm.xmodule = this.xmodule || false;
5761     }
5762     
5763     if (this.cm && typeof(this.cm.config) == 'undefined') {
5764         this.colModel = new Roo.grid.ColumnModel(this.cm);
5765         this.cm = this.colModel;
5766         this.cm.xmodule = this.xmodule || false;
5767     }
5768     if (this.store) {
5769         this.store= Roo.factory(this.store, Roo.data);
5770         this.ds = this.store;
5771         this.ds.xmodule = this.xmodule || false;
5772          
5773     }
5774     if (this.footer && this.store) {
5775         this.footer.dataSource = this.ds;
5776         this.footer = Roo.factory(this.footer);
5777     }
5778     
5779     /** @private */
5780     this.addEvents({
5781         /**
5782          * @event cellclick
5783          * Fires when a cell is clicked
5784          * @param {Roo.bootstrap.Table} this
5785          * @param {Roo.Element} el
5786          * @param {Number} rowIndex
5787          * @param {Number} columnIndex
5788          * @param {Roo.EventObject} e
5789          */
5790         "cellclick" : true,
5791         /**
5792          * @event celldblclick
5793          * Fires when a cell is double clicked
5794          * @param {Roo.bootstrap.Table} this
5795          * @param {Roo.Element} el
5796          * @param {Number} rowIndex
5797          * @param {Number} columnIndex
5798          * @param {Roo.EventObject} e
5799          */
5800         "celldblclick" : true,
5801         /**
5802          * @event rowclick
5803          * Fires when a row is clicked
5804          * @param {Roo.bootstrap.Table} this
5805          * @param {Roo.Element} el
5806          * @param {Number} rowIndex
5807          * @param {Roo.EventObject} e
5808          */
5809         "rowclick" : true,
5810         /**
5811          * @event rowdblclick
5812          * Fires when a row is double clicked
5813          * @param {Roo.bootstrap.Table} this
5814          * @param {Roo.Element} el
5815          * @param {Number} rowIndex
5816          * @param {Roo.EventObject} e
5817          */
5818         "rowdblclick" : true,
5819         /**
5820          * @event mouseover
5821          * Fires when a mouseover occur
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Roo.Element} el
5824          * @param {Number} rowIndex
5825          * @param {Number} columnIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "mouseover" : true,
5829         /**
5830          * @event mouseout
5831          * Fires when a mouseout occur
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Roo.Element} el
5834          * @param {Number} rowIndex
5835          * @param {Number} columnIndex
5836          * @param {Roo.EventObject} e
5837          */
5838         "mouseout" : true,
5839         /**
5840          * @event rowclass
5841          * Fires when a row is rendered, so you can change add a style to it.
5842          * @param {Roo.bootstrap.Table} this
5843          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5844          */
5845         'rowclass' : true,
5846           /**
5847          * @event rowsrendered
5848          * Fires when all the  rows have been rendered
5849          * @param {Roo.bootstrap.Table} this
5850          */
5851         'rowsrendered' : true,
5852         /**
5853          * @event contextmenu
5854          * The raw contextmenu event for the entire grid.
5855          * @param {Roo.EventObject} e
5856          */
5857         "contextmenu" : true,
5858         /**
5859          * @event rowcontextmenu
5860          * Fires when a row is right clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Number} rowIndex
5863          * @param {Roo.EventObject} e
5864          */
5865         "rowcontextmenu" : true,
5866         /**
5867          * @event cellcontextmenu
5868          * Fires when a cell is right clicked
5869          * @param {Roo.bootstrap.Table} this
5870          * @param {Number} rowIndex
5871          * @param {Number} cellIndex
5872          * @param {Roo.EventObject} e
5873          */
5874          "cellcontextmenu" : true,
5875          /**
5876          * @event headercontextmenu
5877          * Fires when a header is right clicked
5878          * @param {Roo.bootstrap.Table} this
5879          * @param {Number} columnIndex
5880          * @param {Roo.EventObject} e
5881          */
5882         "headercontextmenu" : true
5883     });
5884 };
5885
5886 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5887     
5888     cls: false,
5889     align: false,
5890     bgcolor: false,
5891     border: false,
5892     cellpadding: false,
5893     cellspacing: false,
5894     frame: false,
5895     rules: false,
5896     sortable: false,
5897     summary: false,
5898     width: false,
5899     striped : false,
5900     scrollBody : false,
5901     bordered: false,
5902     hover:  false,
5903     condensed : false,
5904     responsive : false,
5905     sm : false,
5906     cm : false,
5907     store : false,
5908     loadMask : false,
5909     footerShow : true,
5910     headerShow : true,
5911   
5912     rowSelection : false,
5913     cellSelection : false,
5914     layout : false,
5915     
5916     // Roo.Element - the tbody
5917     mainBody: false,
5918     // Roo.Element - thead element
5919     mainHead: false,
5920     
5921     container: false, // used by gridpanel...
5922     
5923     getAutoCreate : function()
5924     {
5925         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5926         
5927         cfg = {
5928             tag: 'table',
5929             cls : 'table',
5930             cn : []
5931         };
5932         if (this.scrollBody) {
5933             cfg.cls += ' table-body-fixed';
5934         }    
5935         if (this.striped) {
5936             cfg.cls += ' table-striped';
5937         }
5938         
5939         if (this.hover) {
5940             cfg.cls += ' table-hover';
5941         }
5942         if (this.bordered) {
5943             cfg.cls += ' table-bordered';
5944         }
5945         if (this.condensed) {
5946             cfg.cls += ' table-condensed';
5947         }
5948         if (this.responsive) {
5949             cfg.cls += ' table-responsive';
5950         }
5951         
5952         if (this.cls) {
5953             cfg.cls+=  ' ' +this.cls;
5954         }
5955         
5956         // this lot should be simplifed...
5957         
5958         if (this.align) {
5959             cfg.align=this.align;
5960         }
5961         if (this.bgcolor) {
5962             cfg.bgcolor=this.bgcolor;
5963         }
5964         if (this.border) {
5965             cfg.border=this.border;
5966         }
5967         if (this.cellpadding) {
5968             cfg.cellpadding=this.cellpadding;
5969         }
5970         if (this.cellspacing) {
5971             cfg.cellspacing=this.cellspacing;
5972         }
5973         if (this.frame) {
5974             cfg.frame=this.frame;
5975         }
5976         if (this.rules) {
5977             cfg.rules=this.rules;
5978         }
5979         if (this.sortable) {
5980             cfg.sortable=this.sortable;
5981         }
5982         if (this.summary) {
5983             cfg.summary=this.summary;
5984         }
5985         if (this.width) {
5986             cfg.width=this.width;
5987         }
5988         if (this.layout) {
5989             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5990         }
5991         
5992         if(this.store || this.cm){
5993             if(this.headerShow){
5994                 cfg.cn.push(this.renderHeader());
5995             }
5996             
5997             cfg.cn.push(this.renderBody());
5998             
5999             if(this.footerShow){
6000                 cfg.cn.push(this.renderFooter());
6001             }
6002             // where does this come from?
6003             //cfg.cls+=  ' TableGrid';
6004         }
6005         
6006         return { cn : [ cfg ] };
6007     },
6008     
6009     initEvents : function()
6010     {   
6011         if(!this.store || !this.cm){
6012             return;
6013         }
6014         if (this.selModel) {
6015             this.selModel.initEvents();
6016         }
6017         
6018         
6019         //Roo.log('initEvents with ds!!!!');
6020         
6021         this.mainBody = this.el.select('tbody', true).first();
6022         this.mainHead = this.el.select('thead', true).first();
6023         
6024         
6025         
6026         
6027         var _this = this;
6028         
6029         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6030             e.on('click', _this.sort, _this);
6031         });
6032         
6033         this.mainBody.on("click", this.onClick, this);
6034         this.mainBody.on("dblclick", this.onDblClick, this);
6035         
6036         // why is this done????? = it breaks dialogs??
6037         //this.parent().el.setStyle('position', 'relative');
6038         
6039         
6040         if (this.footer) {
6041             this.footer.parentId = this.id;
6042             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6043         } 
6044         
6045         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6046         
6047         this.store.on('load', this.onLoad, this);
6048         this.store.on('beforeload', this.onBeforeLoad, this);
6049         this.store.on('update', this.onUpdate, this);
6050         this.store.on('add', this.onAdd, this);
6051         this.store.on("clear", this.clear, this);
6052         
6053         this.el.on("contextmenu", this.onContextMenu, this);
6054         
6055         this.mainBody.on('scroll', this.onBodyScroll, this);
6056         
6057         
6058     },
6059     
6060     onContextMenu : function(e, t)
6061     {
6062         this.processEvent("contextmenu", e);
6063     },
6064     
6065     processEvent : function(name, e)
6066     {
6067         if (name != 'touchstart' ) {
6068             this.fireEvent(name, e);    
6069         }
6070         
6071         var t = e.getTarget();
6072         
6073         var cell = Roo.get(t);
6074         
6075         if(!cell){
6076             return;
6077         }
6078         
6079         if(cell.findParent('tfoot', false, true)){
6080             return;
6081         }
6082         
6083         if(cell.findParent('thead', false, true)){
6084             
6085             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6086                 cell = Roo.get(t).findParent('th', false, true);
6087                 if (!cell) {
6088                     Roo.log("failed to find th in thead?");
6089                     Roo.log(e.getTarget());
6090                     return;
6091                 }
6092             }
6093             
6094             var cellIndex = cell.dom.cellIndex;
6095             
6096             var ename = name == 'touchstart' ? 'click' : name;
6097             this.fireEvent("header" + ename, this, cellIndex, e);
6098             
6099             return;
6100         }
6101         
6102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6103             cell = Roo.get(t).findParent('td', false, true);
6104             if (!cell) {
6105                 Roo.log("failed to find th in tbody?");
6106                 Roo.log(e.getTarget());
6107                 return;
6108             }
6109         }
6110         
6111         var row = cell.findParent('tr', false, true);
6112         var cellIndex = cell.dom.cellIndex;
6113         var rowIndex = row.dom.rowIndex - 1;
6114         
6115         if(row !== false){
6116             
6117             this.fireEvent("row" + name, this, rowIndex, e);
6118             
6119             if(cell !== false){
6120             
6121                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6122             }
6123         }
6124         
6125     },
6126     
6127     onMouseover : function(e, el)
6128     {
6129         var cell = Roo.get(el);
6130         
6131         if(!cell){
6132             return;
6133         }
6134         
6135         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6136             cell = cell.findParent('td', false, true);
6137         }
6138         
6139         var row = cell.findParent('tr', false, true);
6140         var cellIndex = cell.dom.cellIndex;
6141         var rowIndex = row.dom.rowIndex - 1; // start from 0
6142         
6143         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6144         
6145     },
6146     
6147     onMouseout : function(e, el)
6148     {
6149         var cell = Roo.get(el);
6150         
6151         if(!cell){
6152             return;
6153         }
6154         
6155         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156             cell = cell.findParent('td', false, true);
6157         }
6158         
6159         var row = cell.findParent('tr', false, true);
6160         var cellIndex = cell.dom.cellIndex;
6161         var rowIndex = row.dom.rowIndex - 1; // start from 0
6162         
6163         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6164         
6165     },
6166     
6167     onClick : function(e, el)
6168     {
6169         var cell = Roo.get(el);
6170         
6171         if(!cell || (!this.cellSelection && !this.rowSelection)){
6172             return;
6173         }
6174         
6175         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176             cell = cell.findParent('td', false, true);
6177         }
6178         
6179         if(!cell || typeof(cell) == 'undefined'){
6180             return;
6181         }
6182         
6183         var row = cell.findParent('tr', false, true);
6184         
6185         if(!row || typeof(row) == 'undefined'){
6186             return;
6187         }
6188         
6189         var cellIndex = cell.dom.cellIndex;
6190         var rowIndex = this.getRowIndex(row);
6191         
6192         // why??? - should these not be based on SelectionModel?
6193         if(this.cellSelection){
6194             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6195         }
6196         
6197         if(this.rowSelection){
6198             this.fireEvent('rowclick', this, row, rowIndex, e);
6199         }
6200         
6201         
6202     },
6203         
6204     onDblClick : function(e,el)
6205     {
6206         var cell = Roo.get(el);
6207         
6208         if(!cell || (!this.cellSelection && !this.rowSelection)){
6209             return;
6210         }
6211         
6212         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213             cell = cell.findParent('td', false, true);
6214         }
6215         
6216         if(!cell || typeof(cell) == 'undefined'){
6217             return;
6218         }
6219         
6220         var row = cell.findParent('tr', false, true);
6221         
6222         if(!row || typeof(row) == 'undefined'){
6223             return;
6224         }
6225         
6226         var cellIndex = cell.dom.cellIndex;
6227         var rowIndex = this.getRowIndex(row);
6228         
6229         if(this.cellSelection){
6230             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6231         }
6232         
6233         if(this.rowSelection){
6234             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6235         }
6236     },
6237     
6238     sort : function(e,el)
6239     {
6240         var col = Roo.get(el);
6241         
6242         if(!col.hasClass('sortable')){
6243             return;
6244         }
6245         
6246         var sort = col.attr('sort');
6247         var dir = 'ASC';
6248         
6249         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6250             dir = 'DESC';
6251         }
6252         
6253         this.store.sortInfo = {field : sort, direction : dir};
6254         
6255         if (this.footer) {
6256             Roo.log("calling footer first");
6257             this.footer.onClick('first');
6258         } else {
6259         
6260             this.store.load({ params : { start : 0 } });
6261         }
6262     },
6263     
6264     renderHeader : function()
6265     {
6266         var header = {
6267             tag: 'thead',
6268             cn : []
6269         };
6270         
6271         var cm = this.cm;
6272         this.totalWidth = 0;
6273         
6274         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6275             
6276             var config = cm.config[i];
6277             
6278             var c = {
6279                 tag: 'th',
6280                 style : '',
6281                 html: cm.getColumnHeader(i)
6282             };
6283             
6284             var hh = '';
6285             
6286             if(typeof(config.sortable) != 'undefined' && config.sortable){
6287                 c.cls = 'sortable';
6288                 c.html = '<i class="glyphicon"></i>' + c.html;
6289             }
6290             
6291             if(typeof(config.lgHeader) != 'undefined'){
6292                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6293             }
6294             
6295             if(typeof(config.mdHeader) != 'undefined'){
6296                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6297             }
6298             
6299             if(typeof(config.smHeader) != 'undefined'){
6300                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6301             }
6302             
6303             if(typeof(config.xsHeader) != 'undefined'){
6304                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6305             }
6306             
6307             if(hh.length){
6308                 c.html = hh;
6309             }
6310             
6311             if(typeof(config.tooltip) != 'undefined'){
6312                 c.tooltip = config.tooltip;
6313             }
6314             
6315             if(typeof(config.colspan) != 'undefined'){
6316                 c.colspan = config.colspan;
6317             }
6318             
6319             if(typeof(config.hidden) != 'undefined' && config.hidden){
6320                 c.style += ' display:none;';
6321             }
6322             
6323             if(typeof(config.dataIndex) != 'undefined'){
6324                 c.sort = config.dataIndex;
6325             }
6326             
6327            
6328             
6329             if(typeof(config.align) != 'undefined' && config.align.length){
6330                 c.style += ' text-align:' + config.align + ';';
6331             }
6332             
6333             if(typeof(config.width) != 'undefined'){
6334                 c.style += ' width:' + config.width + 'px;';
6335                 this.totalWidth += config.width;
6336             } else {
6337                 this.totalWidth += 100; // assume minimum of 100 per column?
6338             }
6339             
6340             if(typeof(config.cls) != 'undefined'){
6341                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6342             }
6343             
6344             ['xs','sm','md','lg'].map(function(size){
6345                 
6346                 if(typeof(config[size]) == 'undefined'){
6347                     return;
6348                 }
6349                 
6350                 if (!config[size]) { // 0 = hidden
6351                     c.cls += ' hidden-' + size;
6352                     return;
6353                 }
6354                 
6355                 c.cls += ' col-' + size + '-' + config[size];
6356
6357             });
6358             
6359             header.cn.push(c)
6360         }
6361         
6362         return header;
6363     },
6364     
6365     renderBody : function()
6366     {
6367         var body = {
6368             tag: 'tbody',
6369             cn : [
6370                 {
6371                     tag: 'tr',
6372                     cn : [
6373                         {
6374                             tag : 'td',
6375                             colspan :  this.cm.getColumnCount()
6376                         }
6377                     ]
6378                 }
6379             ]
6380         };
6381         
6382         return body;
6383     },
6384     
6385     renderFooter : function()
6386     {
6387         var footer = {
6388             tag: 'tfoot',
6389             cn : [
6390                 {
6391                     tag: 'tr',
6392                     cn : [
6393                         {
6394                             tag : 'td',
6395                             colspan :  this.cm.getColumnCount()
6396                         }
6397                     ]
6398                 }
6399             ]
6400         };
6401         
6402         return footer;
6403     },
6404     
6405     
6406     
6407     onLoad : function()
6408     {
6409 //        Roo.log('ds onload');
6410         this.clear();
6411         
6412         var _this = this;
6413         var cm = this.cm;
6414         var ds = this.store;
6415         
6416         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6418             if (_this.store.sortInfo) {
6419                     
6420                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6421                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6422                 }
6423                 
6424                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6425                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6426                 }
6427             }
6428         });
6429         
6430         var tbody =  this.mainBody;
6431               
6432         if(ds.getCount() > 0){
6433             ds.data.each(function(d,rowIndex){
6434                 var row =  this.renderRow(cm, ds, rowIndex);
6435                 
6436                 tbody.createChild(row);
6437                 
6438                 var _this = this;
6439                 
6440                 if(row.cellObjects.length){
6441                     Roo.each(row.cellObjects, function(r){
6442                         _this.renderCellObject(r);
6443                     })
6444                 }
6445                 
6446             }, this);
6447         }
6448         
6449         Roo.each(this.el.select('tbody td', true).elements, function(e){
6450             e.on('mouseover', _this.onMouseover, _this);
6451         });
6452         
6453         Roo.each(this.el.select('tbody td', true).elements, function(e){
6454             e.on('mouseout', _this.onMouseout, _this);
6455         });
6456         this.fireEvent('rowsrendered', this);
6457         //if(this.loadMask){
6458         //    this.maskEl.hide();
6459         //}
6460         
6461         this.autoSize();
6462     },
6463     
6464     
6465     onUpdate : function(ds,record)
6466     {
6467         this.refreshRow(record);
6468         this.autoSize();
6469     },
6470     
6471     onRemove : function(ds, record, index, isUpdate){
6472         if(isUpdate !== true){
6473             this.fireEvent("beforerowremoved", this, index, record);
6474         }
6475         var bt = this.mainBody.dom;
6476         
6477         var rows = this.el.select('tbody > tr', true).elements;
6478         
6479         if(typeof(rows[index]) != 'undefined'){
6480             bt.removeChild(rows[index].dom);
6481         }
6482         
6483 //        if(bt.rows[index]){
6484 //            bt.removeChild(bt.rows[index]);
6485 //        }
6486         
6487         if(isUpdate !== true){
6488             //this.stripeRows(index);
6489             //this.syncRowHeights(index, index);
6490             //this.layout();
6491             this.fireEvent("rowremoved", this, index, record);
6492         }
6493     },
6494     
6495     onAdd : function(ds, records, rowIndex)
6496     {
6497         //Roo.log('on Add called');
6498         // - note this does not handle multiple adding very well..
6499         var bt = this.mainBody.dom;
6500         for (var i =0 ; i < records.length;i++) {
6501             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6502             //Roo.log(records[i]);
6503             //Roo.log(this.store.getAt(rowIndex+i));
6504             this.insertRow(this.store, rowIndex + i, false);
6505             return;
6506         }
6507         
6508     },
6509     
6510     
6511     refreshRow : function(record){
6512         var ds = this.store, index;
6513         if(typeof record == 'number'){
6514             index = record;
6515             record = ds.getAt(index);
6516         }else{
6517             index = ds.indexOf(record);
6518         }
6519         this.insertRow(ds, index, true);
6520         this.autoSize();
6521         this.onRemove(ds, record, index+1, true);
6522         this.autoSize();
6523         //this.syncRowHeights(index, index);
6524         //this.layout();
6525         this.fireEvent("rowupdated", this, index, record);
6526     },
6527     
6528     insertRow : function(dm, rowIndex, isUpdate){
6529         
6530         if(!isUpdate){
6531             this.fireEvent("beforerowsinserted", this, rowIndex);
6532         }
6533             //var s = this.getScrollState();
6534         var row = this.renderRow(this.cm, this.store, rowIndex);
6535         // insert before rowIndex..
6536         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6537         
6538         var _this = this;
6539                 
6540         if(row.cellObjects.length){
6541             Roo.each(row.cellObjects, function(r){
6542                 _this.renderCellObject(r);
6543             })
6544         }
6545             
6546         if(!isUpdate){
6547             this.fireEvent("rowsinserted", this, rowIndex);
6548             //this.syncRowHeights(firstRow, lastRow);
6549             //this.stripeRows(firstRow);
6550             //this.layout();
6551         }
6552         
6553     },
6554     
6555     
6556     getRowDom : function(rowIndex)
6557     {
6558         var rows = this.el.select('tbody > tr', true).elements;
6559         
6560         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6561         
6562     },
6563     // returns the object tree for a tr..
6564   
6565     
6566     renderRow : function(cm, ds, rowIndex) 
6567     {
6568         
6569         var d = ds.getAt(rowIndex);
6570         
6571         var row = {
6572             tag : 'tr',
6573             cn : []
6574         };
6575             
6576         var cellObjects = [];
6577         
6578         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6579             var config = cm.config[i];
6580             
6581             var renderer = cm.getRenderer(i);
6582             var value = '';
6583             var id = false;
6584             
6585             if(typeof(renderer) !== 'undefined'){
6586                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6587             }
6588             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6589             // and are rendered into the cells after the row is rendered - using the id for the element.
6590             
6591             if(typeof(value) === 'object'){
6592                 id = Roo.id();
6593                 cellObjects.push({
6594                     container : id,
6595                     cfg : value 
6596                 })
6597             }
6598             
6599             var rowcfg = {
6600                 record: d,
6601                 rowIndex : rowIndex,
6602                 colIndex : i,
6603                 rowClass : ''
6604             };
6605
6606             this.fireEvent('rowclass', this, rowcfg);
6607             
6608             var td = {
6609                 tag: 'td',
6610                 cls : rowcfg.rowClass,
6611                 style: '',
6612                 html: (typeof(value) === 'object') ? '' : value
6613             };
6614             
6615             if (id) {
6616                 td.id = id;
6617             }
6618             
6619             if(typeof(config.colspan) != 'undefined'){
6620                 td.colspan = config.colspan;
6621             }
6622             
6623             if(typeof(config.hidden) != 'undefined' && config.hidden){
6624                 td.style += ' display:none;';
6625             }
6626             
6627             if(typeof(config.align) != 'undefined' && config.align.length){
6628                 td.style += ' text-align:' + config.align + ';';
6629             }
6630             
6631             if(typeof(config.width) != 'undefined'){
6632                 td.style += ' width:' +  config.width + 'px;';
6633             }
6634             
6635             if(typeof(config.cursor) != 'undefined'){
6636                 td.style += ' cursor:' +  config.cursor + ';';
6637             }
6638             
6639             if(typeof(config.cls) != 'undefined'){
6640                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6641             }
6642             
6643             ['xs','sm','md','lg'].map(function(size){
6644                 
6645                 if(typeof(config[size]) == 'undefined'){
6646                     return;
6647                 }
6648                 
6649                 if (!config[size]) { // 0 = hidden
6650                     td.cls += ' hidden-' + size;
6651                     return;
6652                 }
6653                 
6654                 td.cls += ' col-' + size + '-' + config[size];
6655
6656             });
6657              
6658             row.cn.push(td);
6659            
6660         }
6661         
6662         row.cellObjects = cellObjects;
6663         
6664         return row;
6665           
6666     },
6667     
6668     
6669     
6670     onBeforeLoad : function()
6671     {
6672         //Roo.log('ds onBeforeLoad');
6673         
6674         //this.clear();
6675         
6676         //if(this.loadMask){
6677         //    this.maskEl.show();
6678         //}
6679     },
6680      /**
6681      * Remove all rows
6682      */
6683     clear : function()
6684     {
6685         this.el.select('tbody', true).first().dom.innerHTML = '';
6686     },
6687     /**
6688      * Show or hide a row.
6689      * @param {Number} rowIndex to show or hide
6690      * @param {Boolean} state hide
6691      */
6692     setRowVisibility : function(rowIndex, state)
6693     {
6694         var bt = this.mainBody.dom;
6695         
6696         var rows = this.el.select('tbody > tr', true).elements;
6697         
6698         if(typeof(rows[rowIndex]) == 'undefined'){
6699             return;
6700         }
6701         rows[rowIndex].dom.style.display = state ? '' : 'none';
6702     },
6703     
6704     
6705     getSelectionModel : function(){
6706         if(!this.selModel){
6707             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6708         }
6709         return this.selModel;
6710     },
6711     /*
6712      * Render the Roo.bootstrap object from renderder
6713      */
6714     renderCellObject : function(r)
6715     {
6716         var _this = this;
6717         
6718         var t = r.cfg.render(r.container);
6719         
6720         if(r.cfg.cn){
6721             Roo.each(r.cfg.cn, function(c){
6722                 var child = {
6723                     container: t.getChildContainer(),
6724                     cfg: c
6725                 };
6726                 _this.renderCellObject(child);
6727             })
6728         }
6729     },
6730     
6731     getRowIndex : function(row)
6732     {
6733         var rowIndex = -1;
6734         
6735         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6736             if(el != row){
6737                 return;
6738             }
6739             
6740             rowIndex = index;
6741         });
6742         
6743         return rowIndex;
6744     },
6745      /**
6746      * Returns the grid's underlying element = used by panel.Grid
6747      * @return {Element} The element
6748      */
6749     getGridEl : function(){
6750         return this.el;
6751     },
6752      /**
6753      * Forces a resize - used by panel.Grid
6754      * @return {Element} The element
6755      */
6756     autoSize : function()
6757     {
6758         //var ctr = Roo.get(this.container.dom.parentElement);
6759         var ctr = Roo.get(this.el.dom);
6760         
6761         var thd = this.getGridEl().select('thead',true).first();
6762         var tbd = this.getGridEl().select('tbody', true).first();
6763         var tfd = this.getGridEl().select('tfoot', true).first();
6764         
6765         var cw = ctr.getWidth();
6766         
6767         if (tbd) {
6768             
6769             tbd.setSize(ctr.getWidth(),
6770                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6771             );
6772             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6773             cw -= barsize;
6774         }
6775         cw = Math.max(cw, this.totalWidth);
6776         this.getGridEl().select('tr',true).setWidth(cw);
6777         // resize 'expandable coloumn?
6778         
6779         return; // we doe not have a view in this design..
6780         
6781     },
6782     onBodyScroll: function()
6783     {
6784         
6785         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6786         this.mainHead.setStyle({
6787                     'position' : 'relative',
6788                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6789         });
6790         
6791         
6792     }
6793 });
6794
6795  
6796
6797  /*
6798  * - LGPL
6799  *
6800  * table cell
6801  * 
6802  */
6803
6804 /**
6805  * @class Roo.bootstrap.TableCell
6806  * @extends Roo.bootstrap.Component
6807  * Bootstrap TableCell class
6808  * @cfg {String} html cell contain text
6809  * @cfg {String} cls cell class
6810  * @cfg {String} tag cell tag (td|th) default td
6811  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6812  * @cfg {String} align Aligns the content in a cell
6813  * @cfg {String} axis Categorizes cells
6814  * @cfg {String} bgcolor Specifies the background color of a cell
6815  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6816  * @cfg {Number} colspan Specifies the number of columns a cell should span
6817  * @cfg {String} headers Specifies one or more header cells a cell is related to
6818  * @cfg {Number} height Sets the height of a cell
6819  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6820  * @cfg {Number} rowspan Sets the number of rows a cell should span
6821  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6822  * @cfg {String} valign Vertical aligns the content in a cell
6823  * @cfg {Number} width Specifies the width of a cell
6824  * 
6825  * @constructor
6826  * Create a new TableCell
6827  * @param {Object} config The config object
6828  */
6829
6830 Roo.bootstrap.TableCell = function(config){
6831     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6832 };
6833
6834 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6835     
6836     html: false,
6837     cls: false,
6838     tag: false,
6839     abbr: false,
6840     align: false,
6841     axis: false,
6842     bgcolor: false,
6843     charoff: false,
6844     colspan: false,
6845     headers: false,
6846     height: false,
6847     nowrap: false,
6848     rowspan: false,
6849     scope: false,
6850     valign: false,
6851     width: false,
6852     
6853     
6854     getAutoCreate : function(){
6855         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6856         
6857         cfg = {
6858             tag: 'td'
6859         };
6860         
6861         if(this.tag){
6862             cfg.tag = this.tag;
6863         }
6864         
6865         if (this.html) {
6866             cfg.html=this.html
6867         }
6868         if (this.cls) {
6869             cfg.cls=this.cls
6870         }
6871         if (this.abbr) {
6872             cfg.abbr=this.abbr
6873         }
6874         if (this.align) {
6875             cfg.align=this.align
6876         }
6877         if (this.axis) {
6878             cfg.axis=this.axis
6879         }
6880         if (this.bgcolor) {
6881             cfg.bgcolor=this.bgcolor
6882         }
6883         if (this.charoff) {
6884             cfg.charoff=this.charoff
6885         }
6886         if (this.colspan) {
6887             cfg.colspan=this.colspan
6888         }
6889         if (this.headers) {
6890             cfg.headers=this.headers
6891         }
6892         if (this.height) {
6893             cfg.height=this.height
6894         }
6895         if (this.nowrap) {
6896             cfg.nowrap=this.nowrap
6897         }
6898         if (this.rowspan) {
6899             cfg.rowspan=this.rowspan
6900         }
6901         if (this.scope) {
6902             cfg.scope=this.scope
6903         }
6904         if (this.valign) {
6905             cfg.valign=this.valign
6906         }
6907         if (this.width) {
6908             cfg.width=this.width
6909         }
6910         
6911         
6912         return cfg;
6913     }
6914    
6915 });
6916
6917  
6918
6919  /*
6920  * - LGPL
6921  *
6922  * table row
6923  * 
6924  */
6925
6926 /**
6927  * @class Roo.bootstrap.TableRow
6928  * @extends Roo.bootstrap.Component
6929  * Bootstrap TableRow class
6930  * @cfg {String} cls row class
6931  * @cfg {String} align Aligns the content in a table row
6932  * @cfg {String} bgcolor Specifies a background color for a table row
6933  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6934  * @cfg {String} valign Vertical aligns the content in a table row
6935  * 
6936  * @constructor
6937  * Create a new TableRow
6938  * @param {Object} config The config object
6939  */
6940
6941 Roo.bootstrap.TableRow = function(config){
6942     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6943 };
6944
6945 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6946     
6947     cls: false,
6948     align: false,
6949     bgcolor: false,
6950     charoff: false,
6951     valign: false,
6952     
6953     getAutoCreate : function(){
6954         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6955         
6956         cfg = {
6957             tag: 'tr'
6958         };
6959             
6960         if(this.cls){
6961             cfg.cls = this.cls;
6962         }
6963         if(this.align){
6964             cfg.align = this.align;
6965         }
6966         if(this.bgcolor){
6967             cfg.bgcolor = this.bgcolor;
6968         }
6969         if(this.charoff){
6970             cfg.charoff = this.charoff;
6971         }
6972         if(this.valign){
6973             cfg.valign = this.valign;
6974         }
6975         
6976         return cfg;
6977     }
6978    
6979 });
6980
6981  
6982
6983  /*
6984  * - LGPL
6985  *
6986  * table body
6987  * 
6988  */
6989
6990 /**
6991  * @class Roo.bootstrap.TableBody
6992  * @extends Roo.bootstrap.Component
6993  * Bootstrap TableBody class
6994  * @cfg {String} cls element class
6995  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6996  * @cfg {String} align Aligns the content inside the element
6997  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6998  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6999  * 
7000  * @constructor
7001  * Create a new TableBody
7002  * @param {Object} config The config object
7003  */
7004
7005 Roo.bootstrap.TableBody = function(config){
7006     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7007 };
7008
7009 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7010     
7011     cls: false,
7012     tag: false,
7013     align: false,
7014     charoff: false,
7015     valign: false,
7016     
7017     getAutoCreate : function(){
7018         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7019         
7020         cfg = {
7021             tag: 'tbody'
7022         };
7023             
7024         if (this.cls) {
7025             cfg.cls=this.cls
7026         }
7027         if(this.tag){
7028             cfg.tag = this.tag;
7029         }
7030         
7031         if(this.align){
7032             cfg.align = this.align;
7033         }
7034         if(this.charoff){
7035             cfg.charoff = this.charoff;
7036         }
7037         if(this.valign){
7038             cfg.valign = this.valign;
7039         }
7040         
7041         return cfg;
7042     }
7043     
7044     
7045 //    initEvents : function()
7046 //    {
7047 //        
7048 //        if(!this.store){
7049 //            return;
7050 //        }
7051 //        
7052 //        this.store = Roo.factory(this.store, Roo.data);
7053 //        this.store.on('load', this.onLoad, this);
7054 //        
7055 //        this.store.load();
7056 //        
7057 //    },
7058 //    
7059 //    onLoad: function () 
7060 //    {   
7061 //        this.fireEvent('load', this);
7062 //    }
7063 //    
7064 //   
7065 });
7066
7067  
7068
7069  /*
7070  * Based on:
7071  * Ext JS Library 1.1.1
7072  * Copyright(c) 2006-2007, Ext JS, LLC.
7073  *
7074  * Originally Released Under LGPL - original licence link has changed is not relivant.
7075  *
7076  * Fork - LGPL
7077  * <script type="text/javascript">
7078  */
7079
7080 // as we use this in bootstrap.
7081 Roo.namespace('Roo.form');
7082  /**
7083  * @class Roo.form.Action
7084  * Internal Class used to handle form actions
7085  * @constructor
7086  * @param {Roo.form.BasicForm} el The form element or its id
7087  * @param {Object} config Configuration options
7088  */
7089
7090  
7091  
7092 // define the action interface
7093 Roo.form.Action = function(form, options){
7094     this.form = form;
7095     this.options = options || {};
7096 };
7097 /**
7098  * Client Validation Failed
7099  * @const 
7100  */
7101 Roo.form.Action.CLIENT_INVALID = 'client';
7102 /**
7103  * Server Validation Failed
7104  * @const 
7105  */
7106 Roo.form.Action.SERVER_INVALID = 'server';
7107  /**
7108  * Connect to Server Failed
7109  * @const 
7110  */
7111 Roo.form.Action.CONNECT_FAILURE = 'connect';
7112 /**
7113  * Reading Data from Server Failed
7114  * @const 
7115  */
7116 Roo.form.Action.LOAD_FAILURE = 'load';
7117
7118 Roo.form.Action.prototype = {
7119     type : 'default',
7120     failureType : undefined,
7121     response : undefined,
7122     result : undefined,
7123
7124     // interface method
7125     run : function(options){
7126
7127     },
7128
7129     // interface method
7130     success : function(response){
7131
7132     },
7133
7134     // interface method
7135     handleResponse : function(response){
7136
7137     },
7138
7139     // default connection failure
7140     failure : function(response){
7141         
7142         this.response = response;
7143         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7144         this.form.afterAction(this, false);
7145     },
7146
7147     processResponse : function(response){
7148         this.response = response;
7149         if(!response.responseText){
7150             return true;
7151         }
7152         this.result = this.handleResponse(response);
7153         return this.result;
7154     },
7155
7156     // utility functions used internally
7157     getUrl : function(appendParams){
7158         var url = this.options.url || this.form.url || this.form.el.dom.action;
7159         if(appendParams){
7160             var p = this.getParams();
7161             if(p){
7162                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7163             }
7164         }
7165         return url;
7166     },
7167
7168     getMethod : function(){
7169         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7170     },
7171
7172     getParams : function(){
7173         var bp = this.form.baseParams;
7174         var p = this.options.params;
7175         if(p){
7176             if(typeof p == "object"){
7177                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7178             }else if(typeof p == 'string' && bp){
7179                 p += '&' + Roo.urlEncode(bp);
7180             }
7181         }else if(bp){
7182             p = Roo.urlEncode(bp);
7183         }
7184         return p;
7185     },
7186
7187     createCallback : function(){
7188         return {
7189             success: this.success,
7190             failure: this.failure,
7191             scope: this,
7192             timeout: (this.form.timeout*1000),
7193             upload: this.form.fileUpload ? this.success : undefined
7194         };
7195     }
7196 };
7197
7198 Roo.form.Action.Submit = function(form, options){
7199     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7200 };
7201
7202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7203     type : 'submit',
7204
7205     haveProgress : false,
7206     uploadComplete : false,
7207     
7208     // uploadProgress indicator.
7209     uploadProgress : function()
7210     {
7211         if (!this.form.progressUrl) {
7212             return;
7213         }
7214         
7215         if (!this.haveProgress) {
7216             Roo.MessageBox.progress("Uploading", "Uploading");
7217         }
7218         if (this.uploadComplete) {
7219            Roo.MessageBox.hide();
7220            return;
7221         }
7222         
7223         this.haveProgress = true;
7224    
7225         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7226         
7227         var c = new Roo.data.Connection();
7228         c.request({
7229             url : this.form.progressUrl,
7230             params: {
7231                 id : uid
7232             },
7233             method: 'GET',
7234             success : function(req){
7235                //console.log(data);
7236                 var rdata = false;
7237                 var edata;
7238                 try  {
7239                    rdata = Roo.decode(req.responseText)
7240                 } catch (e) {
7241                     Roo.log("Invalid data from server..");
7242                     Roo.log(edata);
7243                     return;
7244                 }
7245                 if (!rdata || !rdata.success) {
7246                     Roo.log(rdata);
7247                     Roo.MessageBox.alert(Roo.encode(rdata));
7248                     return;
7249                 }
7250                 var data = rdata.data;
7251                 
7252                 if (this.uploadComplete) {
7253                    Roo.MessageBox.hide();
7254                    return;
7255                 }
7256                    
7257                 if (data){
7258                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7259                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7260                     );
7261                 }
7262                 this.uploadProgress.defer(2000,this);
7263             },
7264        
7265             failure: function(data) {
7266                 Roo.log('progress url failed ');
7267                 Roo.log(data);
7268             },
7269             scope : this
7270         });
7271            
7272     },
7273     
7274     
7275     run : function()
7276     {
7277         // run get Values on the form, so it syncs any secondary forms.
7278         this.form.getValues();
7279         
7280         var o = this.options;
7281         var method = this.getMethod();
7282         var isPost = method == 'POST';
7283         if(o.clientValidation === false || this.form.isValid()){
7284             
7285             if (this.form.progressUrl) {
7286                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7287                     (new Date() * 1) + '' + Math.random());
7288                     
7289             } 
7290             
7291             
7292             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7293                 form:this.form.el.dom,
7294                 url:this.getUrl(!isPost),
7295                 method: method,
7296                 params:isPost ? this.getParams() : null,
7297                 isUpload: this.form.fileUpload
7298             }));
7299             
7300             this.uploadProgress();
7301
7302         }else if (o.clientValidation !== false){ // client validation failed
7303             this.failureType = Roo.form.Action.CLIENT_INVALID;
7304             this.form.afterAction(this, false);
7305         }
7306     },
7307
7308     success : function(response)
7309     {
7310         this.uploadComplete= true;
7311         if (this.haveProgress) {
7312             Roo.MessageBox.hide();
7313         }
7314         
7315         
7316         var result = this.processResponse(response);
7317         if(result === true || result.success){
7318             this.form.afterAction(this, true);
7319             return;
7320         }
7321         if(result.errors){
7322             this.form.markInvalid(result.errors);
7323             this.failureType = Roo.form.Action.SERVER_INVALID;
7324         }
7325         this.form.afterAction(this, false);
7326     },
7327     failure : function(response)
7328     {
7329         this.uploadComplete= true;
7330         if (this.haveProgress) {
7331             Roo.MessageBox.hide();
7332         }
7333         
7334         this.response = response;
7335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336         this.form.afterAction(this, false);
7337     },
7338     
7339     handleResponse : function(response){
7340         if(this.form.errorReader){
7341             var rs = this.form.errorReader.read(response);
7342             var errors = [];
7343             if(rs.records){
7344                 for(var i = 0, len = rs.records.length; i < len; i++) {
7345                     var r = rs.records[i];
7346                     errors[i] = r.data;
7347                 }
7348             }
7349             if(errors.length < 1){
7350                 errors = null;
7351             }
7352             return {
7353                 success : rs.success,
7354                 errors : errors
7355             };
7356         }
7357         var ret = false;
7358         try {
7359             ret = Roo.decode(response.responseText);
7360         } catch (e) {
7361             ret = {
7362                 success: false,
7363                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7364                 errors : []
7365             };
7366         }
7367         return ret;
7368         
7369     }
7370 });
7371
7372
7373 Roo.form.Action.Load = function(form, options){
7374     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7375     this.reader = this.form.reader;
7376 };
7377
7378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7379     type : 'load',
7380
7381     run : function(){
7382         
7383         Roo.Ajax.request(Roo.apply(
7384                 this.createCallback(), {
7385                     method:this.getMethod(),
7386                     url:this.getUrl(false),
7387                     params:this.getParams()
7388         }));
7389     },
7390
7391     success : function(response){
7392         
7393         var result = this.processResponse(response);
7394         if(result === true || !result.success || !result.data){
7395             this.failureType = Roo.form.Action.LOAD_FAILURE;
7396             this.form.afterAction(this, false);
7397             return;
7398         }
7399         this.form.clearInvalid();
7400         this.form.setValues(result.data);
7401         this.form.afterAction(this, true);
7402     },
7403
7404     handleResponse : function(response){
7405         if(this.form.reader){
7406             var rs = this.form.reader.read(response);
7407             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7408             return {
7409                 success : rs.success,
7410                 data : data
7411             };
7412         }
7413         return Roo.decode(response.responseText);
7414     }
7415 });
7416
7417 Roo.form.Action.ACTION_TYPES = {
7418     'load' : Roo.form.Action.Load,
7419     'submit' : Roo.form.Action.Submit
7420 };/*
7421  * - LGPL
7422  *
7423  * form
7424  *
7425  */
7426
7427 /**
7428  * @class Roo.bootstrap.Form
7429  * @extends Roo.bootstrap.Component
7430  * Bootstrap Form class
7431  * @cfg {String} method  GET | POST (default POST)
7432  * @cfg {String} labelAlign top | left (default top)
7433  * @cfg {String} align left  | right - for navbars
7434  * @cfg {Boolean} loadMask load mask when submit (default true)
7435
7436  *
7437  * @constructor
7438  * Create a new Form
7439  * @param {Object} config The config object
7440  */
7441
7442
7443 Roo.bootstrap.Form = function(config){
7444     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7445     this.addEvents({
7446         /**
7447          * @event clientvalidation
7448          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7449          * @param {Form} this
7450          * @param {Boolean} valid true if the form has passed client-side validation
7451          */
7452         clientvalidation: true,
7453         /**
7454          * @event beforeaction
7455          * Fires before any action is performed. Return false to cancel the action.
7456          * @param {Form} this
7457          * @param {Action} action The action to be performed
7458          */
7459         beforeaction: true,
7460         /**
7461          * @event actionfailed
7462          * Fires when an action fails.
7463          * @param {Form} this
7464          * @param {Action} action The action that failed
7465          */
7466         actionfailed : true,
7467         /**
7468          * @event actioncomplete
7469          * Fires when an action is completed.
7470          * @param {Form} this
7471          * @param {Action} action The action that completed
7472          */
7473         actioncomplete : true
7474     });
7475
7476 };
7477
7478 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7479
7480      /**
7481      * @cfg {String} method
7482      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7483      */
7484     method : 'POST',
7485     /**
7486      * @cfg {String} url
7487      * The URL to use for form actions if one isn't supplied in the action options.
7488      */
7489     /**
7490      * @cfg {Boolean} fileUpload
7491      * Set to true if this form is a file upload.
7492      */
7493
7494     /**
7495      * @cfg {Object} baseParams
7496      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7497      */
7498
7499     /**
7500      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7501      */
7502     timeout: 30,
7503     /**
7504      * @cfg {Sting} align (left|right) for navbar forms
7505      */
7506     align : 'left',
7507
7508     // private
7509     activeAction : null,
7510
7511     /**
7512      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7513      * element by passing it or its id or mask the form itself by passing in true.
7514      * @type Mixed
7515      */
7516     waitMsgTarget : false,
7517
7518     loadMask : true,
7519
7520     getAutoCreate : function(){
7521
7522         var cfg = {
7523             tag: 'form',
7524             method : this.method || 'POST',
7525             id : this.id || Roo.id(),
7526             cls : ''
7527         };
7528         if (this.parent().xtype.match(/^Nav/)) {
7529             cfg.cls = 'navbar-form navbar-' + this.align;
7530
7531         }
7532
7533         if (this.labelAlign == 'left' ) {
7534             cfg.cls += ' form-horizontal';
7535         }
7536
7537
7538         return cfg;
7539     },
7540     initEvents : function()
7541     {
7542         this.el.on('submit', this.onSubmit, this);
7543         // this was added as random key presses on the form where triggering form submit.
7544         this.el.on('keypress', function(e) {
7545             if (e.getCharCode() != 13) {
7546                 return true;
7547             }
7548             // we might need to allow it for textareas.. and some other items.
7549             // check e.getTarget().
7550
7551             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7552                 return true;
7553             }
7554
7555             Roo.log("keypress blocked");
7556
7557             e.preventDefault();
7558             return false;
7559         });
7560
7561     },
7562     // private
7563     onSubmit : function(e){
7564         e.stopEvent();
7565     },
7566
7567      /**
7568      * Returns true if client-side validation on the form is successful.
7569      * @return Boolean
7570      */
7571     isValid : function(){
7572         var items = this.getItems();
7573         var valid = true;
7574         items.each(function(f){
7575            if(!f.validate()){
7576                valid = false;
7577
7578            }
7579         });
7580         return valid;
7581     },
7582     /**
7583      * Returns true if any fields in this form have changed since their original load.
7584      * @return Boolean
7585      */
7586     isDirty : function(){
7587         var dirty = false;
7588         var items = this.getItems();
7589         items.each(function(f){
7590            if(f.isDirty()){
7591                dirty = true;
7592                return false;
7593            }
7594            return true;
7595         });
7596         return dirty;
7597     },
7598      /**
7599      * Performs a predefined action (submit or load) or custom actions you define on this form.
7600      * @param {String} actionName The name of the action type
7601      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7602      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7603      * accept other config options):
7604      * <pre>
7605 Property          Type             Description
7606 ----------------  ---------------  ----------------------------------------------------------------------------------
7607 url               String           The url for the action (defaults to the form's url)
7608 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7609 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7610 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7611                                    validate the form on the client (defaults to false)
7612      * </pre>
7613      * @return {BasicForm} this
7614      */
7615     doAction : function(action, options){
7616         if(typeof action == 'string'){
7617             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7618         }
7619         if(this.fireEvent('beforeaction', this, action) !== false){
7620             this.beforeAction(action);
7621             action.run.defer(100, action);
7622         }
7623         return this;
7624     },
7625
7626     // private
7627     beforeAction : function(action){
7628         var o = action.options;
7629
7630         if(this.loadMask){
7631             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7632         }
7633         // not really supported yet.. ??
7634
7635         //if(this.waitMsgTarget === true){
7636         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7637         //}else if(this.waitMsgTarget){
7638         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7639         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7640         //}else {
7641         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7642        // }
7643
7644     },
7645
7646     // private
7647     afterAction : function(action, success){
7648         this.activeAction = null;
7649         var o = action.options;
7650
7651         //if(this.waitMsgTarget === true){
7652             this.el.unmask();
7653         //}else if(this.waitMsgTarget){
7654         //    this.waitMsgTarget.unmask();
7655         //}else{
7656         //    Roo.MessageBox.updateProgress(1);
7657         //    Roo.MessageBox.hide();
7658        // }
7659         //
7660         if(success){
7661             if(o.reset){
7662                 this.reset();
7663             }
7664             Roo.callback(o.success, o.scope, [this, action]);
7665             this.fireEvent('actioncomplete', this, action);
7666
7667         }else{
7668
7669             // failure condition..
7670             // we have a scenario where updates need confirming.
7671             // eg. if a locking scenario exists..
7672             // we look for { errors : { needs_confirm : true }} in the response.
7673             if (
7674                 (typeof(action.result) != 'undefined')  &&
7675                 (typeof(action.result.errors) != 'undefined')  &&
7676                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7677            ){
7678                 var _t = this;
7679                 Roo.log("not supported yet");
7680                  /*
7681
7682                 Roo.MessageBox.confirm(
7683                     "Change requires confirmation",
7684                     action.result.errorMsg,
7685                     function(r) {
7686                         if (r != 'yes') {
7687                             return;
7688                         }
7689                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7690                     }
7691
7692                 );
7693                 */
7694
7695
7696                 return;
7697             }
7698
7699             Roo.callback(o.failure, o.scope, [this, action]);
7700             // show an error message if no failed handler is set..
7701             if (!this.hasListener('actionfailed')) {
7702                 Roo.log("need to add dialog support");
7703                 /*
7704                 Roo.MessageBox.alert("Error",
7705                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7706                         action.result.errorMsg :
7707                         "Saving Failed, please check your entries or try again"
7708                 );
7709                 */
7710             }
7711
7712             this.fireEvent('actionfailed', this, action);
7713         }
7714
7715     },
7716     /**
7717      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7718      * @param {String} id The value to search for
7719      * @return Field
7720      */
7721     findField : function(id){
7722         var items = this.getItems();
7723         var field = items.get(id);
7724         if(!field){
7725              items.each(function(f){
7726                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7727                     field = f;
7728                     return false;
7729                 }
7730                 return true;
7731             });
7732         }
7733         return field || null;
7734     },
7735      /**
7736      * Mark fields in this form invalid in bulk.
7737      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7738      * @return {BasicForm} this
7739      */
7740     markInvalid : function(errors){
7741         if(errors instanceof Array){
7742             for(var i = 0, len = errors.length; i < len; i++){
7743                 var fieldError = errors[i];
7744                 var f = this.findField(fieldError.id);
7745                 if(f){
7746                     f.markInvalid(fieldError.msg);
7747                 }
7748             }
7749         }else{
7750             var field, id;
7751             for(id in errors){
7752                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7753                     field.markInvalid(errors[id]);
7754                 }
7755             }
7756         }
7757         //Roo.each(this.childForms || [], function (f) {
7758         //    f.markInvalid(errors);
7759         //});
7760
7761         return this;
7762     },
7763
7764     /**
7765      * Set values for fields in this form in bulk.
7766      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7767      * @return {BasicForm} this
7768      */
7769     setValues : function(values){
7770         if(values instanceof Array){ // array of objects
7771             for(var i = 0, len = values.length; i < len; i++){
7772                 var v = values[i];
7773                 var f = this.findField(v.id);
7774                 if(f){
7775                     f.setValue(v.value);
7776                     if(this.trackResetOnLoad){
7777                         f.originalValue = f.getValue();
7778                     }
7779                 }
7780             }
7781         }else{ // object hash
7782             var field, id;
7783             for(id in values){
7784                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7785
7786                     if (field.setFromData &&
7787                         field.valueField &&
7788                         field.displayField &&
7789                         // combos' with local stores can
7790                         // be queried via setValue()
7791                         // to set their value..
7792                         (field.store && !field.store.isLocal)
7793                         ) {
7794                         // it's a combo
7795                         var sd = { };
7796                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7797                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7798                         field.setFromData(sd);
7799
7800                     } else {
7801                         field.setValue(values[id]);
7802                     }
7803
7804
7805                     if(this.trackResetOnLoad){
7806                         field.originalValue = field.getValue();
7807                     }
7808                 }
7809             }
7810         }
7811
7812         //Roo.each(this.childForms || [], function (f) {
7813         //    f.setValues(values);
7814         //});
7815
7816         return this;
7817     },
7818
7819     /**
7820      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7821      * they are returned as an array.
7822      * @param {Boolean} asString
7823      * @return {Object}
7824      */
7825     getValues : function(asString){
7826         //if (this.childForms) {
7827             // copy values from the child forms
7828         //    Roo.each(this.childForms, function (f) {
7829         //        this.setValues(f.getValues());
7830         //    }, this);
7831         //}
7832
7833
7834
7835         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7836         if(asString === true){
7837             return fs;
7838         }
7839         return Roo.urlDecode(fs);
7840     },
7841
7842     /**
7843      * Returns the fields in this form as an object with key/value pairs.
7844      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7845      * @return {Object}
7846      */
7847     getFieldValues : function(with_hidden)
7848     {
7849         var items = this.getItems();
7850         var ret = {};
7851         items.each(function(f){
7852             if (!f.getName()) {
7853                 return;
7854             }
7855             var v = f.getValue();
7856             if (f.inputType =='radio') {
7857                 if (typeof(ret[f.getName()]) == 'undefined') {
7858                     ret[f.getName()] = ''; // empty..
7859                 }
7860
7861                 if (!f.el.dom.checked) {
7862                     return;
7863
7864                 }
7865                 v = f.el.dom.value;
7866
7867             }
7868
7869             // not sure if this supported any more..
7870             if ((typeof(v) == 'object') && f.getRawValue) {
7871                 v = f.getRawValue() ; // dates..
7872             }
7873             // combo boxes where name != hiddenName...
7874             if (f.name != f.getName()) {
7875                 ret[f.name] = f.getRawValue();
7876             }
7877             ret[f.getName()] = v;
7878         });
7879
7880         return ret;
7881     },
7882
7883     /**
7884      * Clears all invalid messages in this form.
7885      * @return {BasicForm} this
7886      */
7887     clearInvalid : function(){
7888         var items = this.getItems();
7889
7890         items.each(function(f){
7891            f.clearInvalid();
7892         });
7893
7894
7895
7896         return this;
7897     },
7898
7899     /**
7900      * Resets this form.
7901      * @return {BasicForm} this
7902      */
7903     reset : function(){
7904         var items = this.getItems();
7905         items.each(function(f){
7906             f.reset();
7907         });
7908
7909         Roo.each(this.childForms || [], function (f) {
7910             f.reset();
7911         });
7912
7913
7914         return this;
7915     },
7916     getItems : function()
7917     {
7918         var r=new Roo.util.MixedCollection(false, function(o){
7919             return o.id || (o.id = Roo.id());
7920         });
7921         var iter = function(el) {
7922             if (el.inputEl) {
7923                 r.add(el);
7924             }
7925             if (!el.items) {
7926                 return;
7927             }
7928             Roo.each(el.items,function(e) {
7929                 iter(e);
7930             });
7931
7932
7933         };
7934
7935         iter(this);
7936         return r;
7937
7938
7939
7940
7941     }
7942
7943 });
7944 /*
7945  * Based on:
7946  * Ext JS Library 1.1.1
7947  * Copyright(c) 2006-2007, Ext JS, LLC.
7948  *
7949  * Originally Released Under LGPL - original licence link has changed is not relivant.
7950  *
7951  * Fork - LGPL
7952  * <script type="text/javascript">
7953  */
7954 /**
7955  * @class Roo.form.VTypes
7956  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7957  * @singleton
7958  */
7959 Roo.form.VTypes = function(){
7960     // closure these in so they are only created once.
7961     var alpha = /^[a-zA-Z_]+$/;
7962     var alphanum = /^[a-zA-Z0-9_]+$/;
7963     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7964     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7965
7966     // All these messages and functions are configurable
7967     return {
7968         /**
7969          * The function used to validate email addresses
7970          * @param {String} value The email address
7971          */
7972         'email' : function(v){
7973             return email.test(v);
7974         },
7975         /**
7976          * The error text to display when the email validation function returns false
7977          * @type String
7978          */
7979         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7980         /**
7981          * The keystroke filter mask to be applied on email input
7982          * @type RegExp
7983          */
7984         'emailMask' : /[a-z0-9_\.\-@]/i,
7985
7986         /**
7987          * The function used to validate URLs
7988          * @param {String} value The URL
7989          */
7990         'url' : function(v){
7991             return url.test(v);
7992         },
7993         /**
7994          * The error text to display when the url validation function returns false
7995          * @type String
7996          */
7997         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7998         
7999         /**
8000          * The function used to validate alpha values
8001          * @param {String} value The value
8002          */
8003         'alpha' : function(v){
8004             return alpha.test(v);
8005         },
8006         /**
8007          * The error text to display when the alpha validation function returns false
8008          * @type String
8009          */
8010         'alphaText' : 'This field should only contain letters and _',
8011         /**
8012          * The keystroke filter mask to be applied on alpha input
8013          * @type RegExp
8014          */
8015         'alphaMask' : /[a-z_]/i,
8016
8017         /**
8018          * The function used to validate alphanumeric values
8019          * @param {String} value The value
8020          */
8021         'alphanum' : function(v){
8022             return alphanum.test(v);
8023         },
8024         /**
8025          * The error text to display when the alphanumeric validation function returns false
8026          * @type String
8027          */
8028         'alphanumText' : 'This field should only contain letters, numbers and _',
8029         /**
8030          * The keystroke filter mask to be applied on alphanumeric input
8031          * @type RegExp
8032          */
8033         'alphanumMask' : /[a-z0-9_]/i
8034     };
8035 }();/*
8036  * - LGPL
8037  *
8038  * Input
8039  * 
8040  */
8041
8042 /**
8043  * @class Roo.bootstrap.Input
8044  * @extends Roo.bootstrap.Component
8045  * Bootstrap Input class
8046  * @cfg {Boolean} disabled is it disabled
8047  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8048  * @cfg {String} name name of the input
8049  * @cfg {string} fieldLabel - the label associated
8050  * @cfg {string} placeholder - placeholder to put in text.
8051  * @cfg {string}  before - input group add on before
8052  * @cfg {string} after - input group add on after
8053  * @cfg {string} size - (lg|sm) or leave empty..
8054  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8055  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8056  * @cfg {Number} md colspan out of 12 for computer-sized screens
8057  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8058  * @cfg {string} value default value of the input
8059  * @cfg {Number} labelWidth set the width of label (0-12)
8060  * @cfg {String} labelAlign (top|left)
8061  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8062  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8063  * @cfg {String} indicatorpos (left|right) default left
8064
8065  * @cfg {String} align (left|center|right) Default left
8066  * @cfg {Boolean} forceFeedback (true|false) Default false
8067  * 
8068  * 
8069  * 
8070  * 
8071  * @constructor
8072  * Create a new Input
8073  * @param {Object} config The config object
8074  */
8075
8076 Roo.bootstrap.Input = function(config){
8077     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8078    
8079         this.addEvents({
8080             /**
8081              * @event focus
8082              * Fires when this field receives input focus.
8083              * @param {Roo.form.Field} this
8084              */
8085             focus : true,
8086             /**
8087              * @event blur
8088              * Fires when this field loses input focus.
8089              * @param {Roo.form.Field} this
8090              */
8091             blur : true,
8092             /**
8093              * @event specialkey
8094              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8095              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8096              * @param {Roo.form.Field} this
8097              * @param {Roo.EventObject} e The event object
8098              */
8099             specialkey : true,
8100             /**
8101              * @event change
8102              * Fires just before the field blurs if the field value has changed.
8103              * @param {Roo.form.Field} this
8104              * @param {Mixed} newValue The new value
8105              * @param {Mixed} oldValue The original value
8106              */
8107             change : true,
8108             /**
8109              * @event invalid
8110              * Fires after the field has been marked as invalid.
8111              * @param {Roo.form.Field} this
8112              * @param {String} msg The validation message
8113              */
8114             invalid : true,
8115             /**
8116              * @event valid
8117              * Fires after the field has been validated with no errors.
8118              * @param {Roo.form.Field} this
8119              */
8120             valid : true,
8121              /**
8122              * @event keyup
8123              * Fires after the key up
8124              * @param {Roo.form.Field} this
8125              * @param {Roo.EventObject}  e The event Object
8126              */
8127             keyup : true
8128         });
8129 };
8130
8131 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8132      /**
8133      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8134       automatic validation (defaults to "keyup").
8135      */
8136     validationEvent : "keyup",
8137      /**
8138      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8139      */
8140     validateOnBlur : true,
8141     /**
8142      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8143      */
8144     validationDelay : 250,
8145      /**
8146      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8147      */
8148     focusClass : "x-form-focus",  // not needed???
8149     
8150        
8151     /**
8152      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8153      */
8154     invalidClass : "has-warning",
8155     
8156     /**
8157      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8158      */
8159     validClass : "has-success",
8160     
8161     /**
8162      * @cfg {Boolean} hasFeedback (true|false) default true
8163      */
8164     hasFeedback : true,
8165     
8166     /**
8167      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8168      */
8169     invalidFeedbackClass : "glyphicon-warning-sign",
8170     
8171     /**
8172      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8173      */
8174     validFeedbackClass : "glyphicon-ok",
8175     
8176     /**
8177      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8178      */
8179     selectOnFocus : false,
8180     
8181      /**
8182      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8183      */
8184     maskRe : null,
8185        /**
8186      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8187      */
8188     vtype : null,
8189     
8190       /**
8191      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8192      */
8193     disableKeyFilter : false,
8194     
8195        /**
8196      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8197      */
8198     disabled : false,
8199      /**
8200      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8201      */
8202     allowBlank : true,
8203     /**
8204      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8205      */
8206     blankText : "This field is required",
8207     
8208      /**
8209      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8210      */
8211     minLength : 0,
8212     /**
8213      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8214      */
8215     maxLength : Number.MAX_VALUE,
8216     /**
8217      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8218      */
8219     minLengthText : "The minimum length for this field is {0}",
8220     /**
8221      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8222      */
8223     maxLengthText : "The maximum length for this field is {0}",
8224   
8225     
8226     /**
8227      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8228      * If available, this function will be called only after the basic validators all return true, and will be passed the
8229      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8230      */
8231     validator : null,
8232     /**
8233      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8234      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8235      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8236      */
8237     regex : null,
8238     /**
8239      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8240      */
8241     regexText : "",
8242     
8243     autocomplete: false,
8244     
8245     
8246     fieldLabel : '',
8247     inputType : 'text',
8248     
8249     name : false,
8250     placeholder: false,
8251     before : false,
8252     after : false,
8253     size : false,
8254     hasFocus : false,
8255     preventMark: false,
8256     isFormField : true,
8257     value : '',
8258     labelWidth : 2,
8259     labelAlign : false,
8260     readOnly : false,
8261     align : false,
8262     formatedValue : false,
8263     forceFeedback : false,
8264     
8265     indicatorpos : 'left',
8266     
8267     parentLabelAlign : function()
8268     {
8269         var parent = this;
8270         while (parent.parent()) {
8271             parent = parent.parent();
8272             if (typeof(parent.labelAlign) !='undefined') {
8273                 return parent.labelAlign;
8274             }
8275         }
8276         return 'left';
8277         
8278     },
8279     
8280     getAutoCreate : function()
8281     {
8282         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8283         
8284         var id = Roo.id();
8285         
8286         var cfg = {};
8287         
8288         if(this.inputType != 'hidden'){
8289             cfg.cls = 'form-group' //input-group
8290         }
8291         
8292         var input =  {
8293             tag: 'input',
8294             id : id,
8295             type : this.inputType,
8296             value : this.value,
8297             cls : 'form-control',
8298             placeholder : this.placeholder || '',
8299             autocomplete : this.autocomplete || 'new-password'
8300         };
8301         
8302         if(this.align){
8303             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8304         }
8305         
8306         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8307             input.maxLength = this.maxLength;
8308         }
8309         
8310         if (this.disabled) {
8311             input.disabled=true;
8312         }
8313         
8314         if (this.readOnly) {
8315             input.readonly=true;
8316         }
8317         
8318         if (this.name) {
8319             input.name = this.name;
8320         }
8321         
8322         if (this.size) {
8323             input.cls += ' input-' + this.size;
8324         }
8325         
8326         var settings=this;
8327         ['xs','sm','md','lg'].map(function(size){
8328             if (settings[size]) {
8329                 cfg.cls += ' col-' + size + '-' + settings[size];
8330             }
8331         });
8332         
8333         var inputblock = input;
8334         
8335         var feedback = {
8336             tag: 'span',
8337             cls: 'glyphicon form-control-feedback'
8338         };
8339             
8340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8341             
8342             inputblock = {
8343                 cls : 'has-feedback',
8344                 cn :  [
8345                     input,
8346                     feedback
8347                 ] 
8348             };  
8349         }
8350         
8351         if (this.before || this.after) {
8352             
8353             inputblock = {
8354                 cls : 'input-group',
8355                 cn :  [] 
8356             };
8357             
8358             if (this.before && typeof(this.before) == 'string') {
8359                 
8360                 inputblock.cn.push({
8361                     tag :'span',
8362                     cls : 'roo-input-before input-group-addon',
8363                     html : this.before
8364                 });
8365             }
8366             if (this.before && typeof(this.before) == 'object') {
8367                 this.before = Roo.factory(this.before);
8368                 
8369                 inputblock.cn.push({
8370                     tag :'span',
8371                     cls : 'roo-input-before input-group-' +
8372                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8373                 });
8374             }
8375             
8376             inputblock.cn.push(input);
8377             
8378             if (this.after && typeof(this.after) == 'string') {
8379                 inputblock.cn.push({
8380                     tag :'span',
8381                     cls : 'roo-input-after input-group-addon',
8382                     html : this.after
8383                 });
8384             }
8385             if (this.after && typeof(this.after) == 'object') {
8386                 this.after = Roo.factory(this.after);
8387                 
8388                 inputblock.cn.push({
8389                     tag :'span',
8390                     cls : 'roo-input-after input-group-' +
8391                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8392                 });
8393             }
8394             
8395             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8396                 inputblock.cls += ' has-feedback';
8397                 inputblock.cn.push(feedback);
8398             }
8399         };
8400         
8401         if (align ==='left' && this.fieldLabel.length) {
8402             
8403             cfg.cn = [
8404                 {
8405                     tag : 'i',
8406                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8407                     tooltip : 'This field is required'
8408                 },
8409                 {
8410                     tag: 'label',
8411                     'for' :  id,
8412                     cls : 'control-label col-sm-' + this.labelWidth,
8413                     html : this.fieldLabel
8414
8415                 },
8416                 {
8417                     cls : "col-sm-" + (12 - this.labelWidth), 
8418                     cn: [
8419                         inputblock
8420                     ]
8421                 }
8422
8423             ];
8424             
8425             if(this.indicatorpos == 'right'){
8426                 cfg.cn = [
8427                     {
8428                         tag: 'label',
8429                         'for' :  id,
8430                         cls : 'control-label col-sm-' + this.labelWidth,
8431                         html : this.fieldLabel
8432
8433                     },
8434                     {
8435                         tag : 'i',
8436                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8437                         tooltip : 'This field is required'
8438                     },
8439                     {
8440                         cls : "col-sm-" + (12 - this.labelWidth), 
8441                         cn: [
8442                             inputblock
8443                         ]
8444                     }
8445
8446                 ];
8447             }
8448             
8449         } else if ( this.fieldLabel.length) {
8450                 
8451             cfg.cn = [
8452                 {
8453                     tag : 'i',
8454                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8455                     tooltip : 'This field is required'
8456                 },
8457                 {
8458                     tag: 'label',
8459                    //cls : 'input-group-addon',
8460                     html : this.fieldLabel
8461
8462                 },
8463
8464                inputblock
8465
8466            ];
8467            
8468            if(this.indicatorpos == 'right'){
8469                 
8470                 cfg.cn = [
8471                     {
8472                         tag: 'label',
8473                        //cls : 'input-group-addon',
8474                         html : this.fieldLabel
8475
8476                     },
8477                     {
8478                         tag : 'i',
8479                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8480                         tooltip : 'This field is required'
8481                     },
8482
8483                    inputblock
8484
8485                ];
8486
8487             }
8488
8489         } else {
8490             
8491             cfg.cn = [
8492
8493                     inputblock
8494
8495             ];
8496                 
8497                 
8498         };
8499         
8500         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8501            cfg.cls += ' navbar-form';
8502         }
8503         
8504         if (this.parentType === 'NavGroup') {
8505            cfg.cls += ' navbar-form';
8506            cfg.tag = 'li';
8507         }
8508         
8509         return cfg;
8510         
8511     },
8512     /**
8513      * return the real input element.
8514      */
8515     inputEl: function ()
8516     {
8517         return this.el.select('input.form-control',true).first();
8518     },
8519     
8520     tooltipEl : function()
8521     {
8522         return this.inputEl();
8523     },
8524     
8525     indicatorEl : function()
8526     {
8527         var indicator = this.el.select('i.roo-required-indicator',true).first();
8528         
8529         if(!indicator){
8530             return false;
8531         }
8532         
8533         return indicator;
8534         
8535     },
8536     
8537     setDisabled : function(v)
8538     {
8539         var i  = this.inputEl().dom;
8540         if (!v) {
8541             i.removeAttribute('disabled');
8542             return;
8543             
8544         }
8545         i.setAttribute('disabled','true');
8546     },
8547     initEvents : function()
8548     {
8549           
8550         this.inputEl().on("keydown" , this.fireKey,  this);
8551         this.inputEl().on("focus", this.onFocus,  this);
8552         this.inputEl().on("blur", this.onBlur,  this);
8553         
8554         this.inputEl().relayEvent('keyup', this);
8555         
8556         this.indicator = this.indicatorEl();
8557         
8558         if(this.indicator){
8559             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8560             this.indicator.hide();
8561         }
8562  
8563         // reference to original value for reset
8564         this.originalValue = this.getValue();
8565         //Roo.form.TextField.superclass.initEvents.call(this);
8566         if(this.validationEvent == 'keyup'){
8567             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8568             this.inputEl().on('keyup', this.filterValidation, this);
8569         }
8570         else if(this.validationEvent !== false){
8571             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8572         }
8573         
8574         if(this.selectOnFocus){
8575             this.on("focus", this.preFocus, this);
8576             
8577         }
8578         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8579             this.inputEl().on("keypress", this.filterKeys, this);
8580         } else {
8581             this.inputEl().relayEvent('keypress', this);
8582         }
8583        /* if(this.grow){
8584             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8585             this.el.on("click", this.autoSize,  this);
8586         }
8587         */
8588         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8589             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8590         }
8591         
8592         if (typeof(this.before) == 'object') {
8593             this.before.render(this.el.select('.roo-input-before',true).first());
8594         }
8595         if (typeof(this.after) == 'object') {
8596             this.after.render(this.el.select('.roo-input-after',true).first());
8597         }
8598         
8599         
8600     },
8601     filterValidation : function(e){
8602         if(!e.isNavKeyPress()){
8603             this.validationTask.delay(this.validationDelay);
8604         }
8605     },
8606      /**
8607      * Validates the field value
8608      * @return {Boolean} True if the value is valid, else false
8609      */
8610     validate : function(){
8611         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8612         if(this.disabled || this.validateValue(this.getRawValue())){
8613             this.markValid();
8614             return true;
8615         }
8616         
8617         this.markInvalid();
8618         return false;
8619     },
8620     
8621     
8622     /**
8623      * Validates a value according to the field's validation rules and marks the field as invalid
8624      * if the validation fails
8625      * @param {Mixed} value The value to validate
8626      * @return {Boolean} True if the value is valid, else false
8627      */
8628     validateValue : function(value){
8629         if(value.length < 1)  { // if it's blank
8630             if(this.allowBlank){
8631                 return true;
8632             }
8633             return false;
8634         }
8635         
8636         if(value.length < this.minLength){
8637             return false;
8638         }
8639         if(value.length > this.maxLength){
8640             return false;
8641         }
8642         if(this.vtype){
8643             var vt = Roo.form.VTypes;
8644             if(!vt[this.vtype](value, this)){
8645                 return false;
8646             }
8647         }
8648         if(typeof this.validator == "function"){
8649             var msg = this.validator(value);
8650             if(msg !== true){
8651                 return false;
8652             }
8653         }
8654         
8655         if(this.regex && !this.regex.test(value)){
8656             return false;
8657         }
8658         
8659         return true;
8660     },
8661
8662     
8663     
8664      // private
8665     fireKey : function(e){
8666         //Roo.log('field ' + e.getKey());
8667         if(e.isNavKeyPress()){
8668             this.fireEvent("specialkey", this, e);
8669         }
8670     },
8671     focus : function (selectText){
8672         if(this.rendered){
8673             this.inputEl().focus();
8674             if(selectText === true){
8675                 this.inputEl().dom.select();
8676             }
8677         }
8678         return this;
8679     } ,
8680     
8681     onFocus : function(){
8682         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8683            // this.el.addClass(this.focusClass);
8684         }
8685         if(!this.hasFocus){
8686             this.hasFocus = true;
8687             this.startValue = this.getValue();
8688             this.fireEvent("focus", this);
8689         }
8690     },
8691     
8692     beforeBlur : Roo.emptyFn,
8693
8694     
8695     // private
8696     onBlur : function(){
8697         this.beforeBlur();
8698         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8699             //this.el.removeClass(this.focusClass);
8700         }
8701         this.hasFocus = false;
8702         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8703             this.validate();
8704         }
8705         var v = this.getValue();
8706         if(String(v) !== String(this.startValue)){
8707             this.fireEvent('change', this, v, this.startValue);
8708         }
8709         this.fireEvent("blur", this);
8710     },
8711     
8712     /**
8713      * Resets the current field value to the originally loaded value and clears any validation messages
8714      */
8715     reset : function(){
8716         this.setValue(this.originalValue);
8717         this.validate();
8718     },
8719      /**
8720      * Returns the name of the field
8721      * @return {Mixed} name The name field
8722      */
8723     getName: function(){
8724         return this.name;
8725     },
8726      /**
8727      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8728      * @return {Mixed} value The field value
8729      */
8730     getValue : function(){
8731         
8732         var v = this.inputEl().getValue();
8733         
8734         return v;
8735     },
8736     /**
8737      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8738      * @return {Mixed} value The field value
8739      */
8740     getRawValue : function(){
8741         var v = this.inputEl().getValue();
8742         
8743         return v;
8744     },
8745     
8746     /**
8747      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8748      * @param {Mixed} value The value to set
8749      */
8750     setRawValue : function(v){
8751         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8752     },
8753     
8754     selectText : function(start, end){
8755         var v = this.getRawValue();
8756         if(v.length > 0){
8757             start = start === undefined ? 0 : start;
8758             end = end === undefined ? v.length : end;
8759             var d = this.inputEl().dom;
8760             if(d.setSelectionRange){
8761                 d.setSelectionRange(start, end);
8762             }else if(d.createTextRange){
8763                 var range = d.createTextRange();
8764                 range.moveStart("character", start);
8765                 range.moveEnd("character", v.length-end);
8766                 range.select();
8767             }
8768         }
8769     },
8770     
8771     /**
8772      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8773      * @param {Mixed} value The value to set
8774      */
8775     setValue : function(v){
8776         this.value = v;
8777         if(this.rendered){
8778             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8779             this.validate();
8780         }
8781     },
8782     
8783     /*
8784     processValue : function(value){
8785         if(this.stripCharsRe){
8786             var newValue = value.replace(this.stripCharsRe, '');
8787             if(newValue !== value){
8788                 this.setRawValue(newValue);
8789                 return newValue;
8790             }
8791         }
8792         return value;
8793     },
8794   */
8795     preFocus : function(){
8796         
8797         if(this.selectOnFocus){
8798             this.inputEl().dom.select();
8799         }
8800     },
8801     filterKeys : function(e){
8802         var k = e.getKey();
8803         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8804             return;
8805         }
8806         var c = e.getCharCode(), cc = String.fromCharCode(c);
8807         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8808             return;
8809         }
8810         if(!this.maskRe.test(cc)){
8811             e.stopEvent();
8812         }
8813     },
8814      /**
8815      * Clear any invalid styles/messages for this field
8816      */
8817     clearInvalid : function(){
8818         
8819         if(!this.el || this.preventMark){ // not rendered
8820             return;
8821         }
8822         
8823         if(this.indicator){
8824             this.indicator.hide();
8825         }
8826         
8827         this.el.removeClass(this.invalidClass);
8828         
8829         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8830             
8831             var feedback = this.el.select('.form-control-feedback', true).first();
8832             
8833             if(feedback){
8834                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8835             }
8836             
8837         }
8838         
8839         this.fireEvent('valid', this);
8840     },
8841     
8842      /**
8843      * Mark this field as valid
8844      */
8845     markValid : function()
8846     {
8847         if(!this.el  || this.preventMark){ // not rendered
8848             return;
8849         }
8850         
8851         this.el.removeClass([this.invalidClass, this.validClass]);
8852         
8853         var feedback = this.el.select('.form-control-feedback', true).first();
8854             
8855         if(feedback){
8856             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8857         }
8858
8859         if(this.disabled){
8860             return;
8861         }
8862         
8863         if(this.allowBlank && !this.getRawValue().length){
8864             return;
8865         }
8866         
8867         if(this.indicator){
8868             this.indicator.hide();
8869         }
8870         
8871         this.el.addClass(this.validClass);
8872         
8873         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8874             
8875             var feedback = this.el.select('.form-control-feedback', true).first();
8876             
8877             if(feedback){
8878                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8879                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8880             }
8881             
8882         }
8883         
8884         this.fireEvent('valid', this);
8885     },
8886     
8887      /**
8888      * Mark this field as invalid
8889      * @param {String} msg The validation message
8890      */
8891     markInvalid : function(msg)
8892     {
8893         if(!this.el  || this.preventMark){ // not rendered
8894             return;
8895         }
8896         
8897         this.el.removeClass([this.invalidClass, this.validClass]);
8898         
8899         var feedback = this.el.select('.form-control-feedback', true).first();
8900             
8901         if(feedback){
8902             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8903         }
8904
8905         if(this.disabled){
8906             return;
8907         }
8908         
8909         if(this.allowBlank && !this.getRawValue().length){
8910             return;
8911         }
8912         
8913         if(this.indicator){
8914             this.indicator.show();
8915         }
8916         
8917         this.el.addClass(this.invalidClass);
8918         
8919         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8920             
8921             var feedback = this.el.select('.form-control-feedback', true).first();
8922             
8923             if(feedback){
8924                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8925                 
8926                 if(this.getValue().length || this.forceFeedback){
8927                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8928                 }
8929                 
8930             }
8931             
8932         }
8933         
8934         this.fireEvent('invalid', this, msg);
8935     },
8936     // private
8937     SafariOnKeyDown : function(event)
8938     {
8939         // this is a workaround for a password hang bug on chrome/ webkit.
8940         if (this.inputEl().dom.type != 'password') {
8941             return;
8942         }
8943         
8944         var isSelectAll = false;
8945         
8946         if(this.inputEl().dom.selectionEnd > 0){
8947             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8948         }
8949         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8950             event.preventDefault();
8951             this.setValue('');
8952             return;
8953         }
8954         
8955         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8956             
8957             event.preventDefault();
8958             // this is very hacky as keydown always get's upper case.
8959             //
8960             var cc = String.fromCharCode(event.getCharCode());
8961             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8962             
8963         }
8964     },
8965     adjustWidth : function(tag, w){
8966         tag = tag.toLowerCase();
8967         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8968             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8969                 if(tag == 'input'){
8970                     return w + 2;
8971                 }
8972                 if(tag == 'textarea'){
8973                     return w-2;
8974                 }
8975             }else if(Roo.isOpera){
8976                 if(tag == 'input'){
8977                     return w + 2;
8978                 }
8979                 if(tag == 'textarea'){
8980                     return w-2;
8981                 }
8982             }
8983         }
8984         return w;
8985     }
8986     
8987 });
8988
8989  
8990 /*
8991  * - LGPL
8992  *
8993  * Input
8994  * 
8995  */
8996
8997 /**
8998  * @class Roo.bootstrap.TextArea
8999  * @extends Roo.bootstrap.Input
9000  * Bootstrap TextArea class
9001  * @cfg {Number} cols Specifies the visible width of a text area
9002  * @cfg {Number} rows Specifies the visible number of lines in a text area
9003  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9004  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9005  * @cfg {string} html text
9006  * 
9007  * @constructor
9008  * Create a new TextArea
9009  * @param {Object} config The config object
9010  */
9011
9012 Roo.bootstrap.TextArea = function(config){
9013     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9014    
9015 };
9016
9017 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9018      
9019     cols : false,
9020     rows : 5,
9021     readOnly : false,
9022     warp : 'soft',
9023     resize : false,
9024     value: false,
9025     html: false,
9026     
9027     getAutoCreate : function(){
9028         
9029         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9030         
9031         var id = Roo.id();
9032         
9033         var cfg = {};
9034         
9035         var input =  {
9036             tag: 'textarea',
9037             id : id,
9038             warp : this.warp,
9039             rows : this.rows,
9040             value : this.value || '',
9041             html: this.html || '',
9042             cls : 'form-control',
9043             placeholder : this.placeholder || '' 
9044             
9045         };
9046         
9047         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9048             input.maxLength = this.maxLength;
9049         }
9050         
9051         if(this.resize){
9052             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9053         }
9054         
9055         if(this.cols){
9056             input.cols = this.cols;
9057         }
9058         
9059         if (this.readOnly) {
9060             input.readonly = true;
9061         }
9062         
9063         if (this.name) {
9064             input.name = this.name;
9065         }
9066         
9067         if (this.size) {
9068             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9069         }
9070         
9071         var settings=this;
9072         ['xs','sm','md','lg'].map(function(size){
9073             if (settings[size]) {
9074                 cfg.cls += ' col-' + size + '-' + settings[size];
9075             }
9076         });
9077         
9078         var inputblock = input;
9079         
9080         if(this.hasFeedback && !this.allowBlank){
9081             
9082             var feedback = {
9083                 tag: 'span',
9084                 cls: 'glyphicon form-control-feedback'
9085             };
9086
9087             inputblock = {
9088                 cls : 'has-feedback',
9089                 cn :  [
9090                     input,
9091                     feedback
9092                 ] 
9093             };  
9094         }
9095         
9096         
9097         if (this.before || this.after) {
9098             
9099             inputblock = {
9100                 cls : 'input-group',
9101                 cn :  [] 
9102             };
9103             if (this.before) {
9104                 inputblock.cn.push({
9105                     tag :'span',
9106                     cls : 'input-group-addon',
9107                     html : this.before
9108                 });
9109             }
9110             
9111             inputblock.cn.push(input);
9112             
9113             if(this.hasFeedback && !this.allowBlank){
9114                 inputblock.cls += ' has-feedback';
9115                 inputblock.cn.push(feedback);
9116             }
9117             
9118             if (this.after) {
9119                 inputblock.cn.push({
9120                     tag :'span',
9121                     cls : 'input-group-addon',
9122                     html : this.after
9123                 });
9124             }
9125             
9126         }
9127         
9128         if (align ==='left' && this.fieldLabel.length) {
9129 //                Roo.log("left and has label");
9130                 cfg.cn = [
9131                     
9132                     {
9133                         tag: 'label',
9134                         'for' :  id,
9135                         cls : 'control-label col-sm-' + this.labelWidth,
9136                         html : this.fieldLabel
9137                         
9138                     },
9139                     {
9140                         cls : "col-sm-" + (12 - this.labelWidth), 
9141                         cn: [
9142                             inputblock
9143                         ]
9144                     }
9145                     
9146                 ];
9147         } else if ( this.fieldLabel.length) {
9148 //                Roo.log(" label");
9149                  cfg.cn = [
9150                    
9151                     {
9152                         tag: 'label',
9153                         //cls : 'input-group-addon',
9154                         html : this.fieldLabel
9155                         
9156                     },
9157                     
9158                     inputblock
9159                     
9160                 ];
9161
9162         } else {
9163             
9164 //                   Roo.log(" no label && no align");
9165                 cfg.cn = [
9166                     
9167                         inputblock
9168                     
9169                 ];
9170                 
9171                 
9172         }
9173         
9174         if (this.disabled) {
9175             input.disabled=true;
9176         }
9177         
9178         return cfg;
9179         
9180     },
9181     /**
9182      * return the real textarea element.
9183      */
9184     inputEl: function ()
9185     {
9186         return this.el.select('textarea.form-control',true).first();
9187     },
9188     
9189     /**
9190      * Clear any invalid styles/messages for this field
9191      */
9192     clearInvalid : function()
9193     {
9194         
9195         if(!this.el || this.preventMark){ // not rendered
9196             return;
9197         }
9198         
9199         var label = this.el.select('label', true).first();
9200         var icon = this.el.select('i.fa-star', true).first();
9201         
9202         if(label && icon){
9203             icon.remove();
9204         }
9205         
9206         this.el.removeClass(this.invalidClass);
9207         
9208         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9209             
9210             var feedback = this.el.select('.form-control-feedback', true).first();
9211             
9212             if(feedback){
9213                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9214             }
9215             
9216         }
9217         
9218         this.fireEvent('valid', this);
9219     },
9220     
9221      /**
9222      * Mark this field as valid
9223      */
9224     markValid : function()
9225     {
9226         if(!this.el  || this.preventMark){ // not rendered
9227             return;
9228         }
9229         
9230         this.el.removeClass([this.invalidClass, this.validClass]);
9231         
9232         var feedback = this.el.select('.form-control-feedback', true).first();
9233             
9234         if(feedback){
9235             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9236         }
9237
9238         if(this.disabled || this.allowBlank){
9239             return;
9240         }
9241         
9242         var label = this.el.select('label', true).first();
9243         var icon = this.el.select('i.fa-star', true).first();
9244         
9245         if(label && icon){
9246             icon.remove();
9247         }
9248         
9249         this.el.addClass(this.validClass);
9250         
9251         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9252             
9253             var feedback = this.el.select('.form-control-feedback', true).first();
9254             
9255             if(feedback){
9256                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9257                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9258             }
9259             
9260         }
9261         
9262         this.fireEvent('valid', this);
9263     },
9264     
9265      /**
9266      * Mark this field as invalid
9267      * @param {String} msg The validation message
9268      */
9269     markInvalid : function(msg)
9270     {
9271         if(!this.el  || this.preventMark){ // not rendered
9272             return;
9273         }
9274         
9275         this.el.removeClass([this.invalidClass, this.validClass]);
9276         
9277         var feedback = this.el.select('.form-control-feedback', true).first();
9278             
9279         if(feedback){
9280             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9281         }
9282
9283         if(this.disabled || this.allowBlank){
9284             return;
9285         }
9286         
9287         var label = this.el.select('label', true).first();
9288         var icon = this.el.select('i.fa-star', true).first();
9289         
9290         if(!this.getValue().length && label && !icon){
9291             this.el.createChild({
9292                 tag : 'i',
9293                 cls : 'text-danger fa fa-lg fa-star',
9294                 tooltip : 'This field is required',
9295                 style : 'margin-right:5px;'
9296             }, label, true);
9297         }
9298
9299         this.el.addClass(this.invalidClass);
9300         
9301         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9302             
9303             var feedback = this.el.select('.form-control-feedback', true).first();
9304             
9305             if(feedback){
9306                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9307                 
9308                 if(this.getValue().length || this.forceFeedback){
9309                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9310                 }
9311                 
9312             }
9313             
9314         }
9315         
9316         this.fireEvent('invalid', this, msg);
9317     }
9318 });
9319
9320  
9321 /*
9322  * - LGPL
9323  *
9324  * trigger field - base class for combo..
9325  * 
9326  */
9327  
9328 /**
9329  * @class Roo.bootstrap.TriggerField
9330  * @extends Roo.bootstrap.Input
9331  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9332  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9333  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9334  * for which you can provide a custom implementation.  For example:
9335  * <pre><code>
9336 var trigger = new Roo.bootstrap.TriggerField();
9337 trigger.onTriggerClick = myTriggerFn;
9338 trigger.applyTo('my-field');
9339 </code></pre>
9340  *
9341  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9342  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9343  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9344  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9345  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9346
9347  * @constructor
9348  * Create a new TriggerField.
9349  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9350  * to the base TextField)
9351  */
9352 Roo.bootstrap.TriggerField = function(config){
9353     this.mimicing = false;
9354     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9355 };
9356
9357 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9358     /**
9359      * @cfg {String} triggerClass A CSS class to apply to the trigger
9360      */
9361      /**
9362      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9363      */
9364     hideTrigger:false,
9365
9366     /**
9367      * @cfg {Boolean} removable (true|false) special filter default false
9368      */
9369     removable : false,
9370     
9371     /** @cfg {Boolean} grow @hide */
9372     /** @cfg {Number} growMin @hide */
9373     /** @cfg {Number} growMax @hide */
9374
9375     /**
9376      * @hide 
9377      * @method
9378      */
9379     autoSize: Roo.emptyFn,
9380     // private
9381     monitorTab : true,
9382     // private
9383     deferHeight : true,
9384
9385     
9386     actionMode : 'wrap',
9387     
9388     caret : false,
9389     
9390     
9391     getAutoCreate : function(){
9392        
9393         var align = this.labelAlign || this.parentLabelAlign();
9394         
9395         var id = Roo.id();
9396         
9397         var cfg = {
9398             cls: 'form-group' //input-group
9399         };
9400         
9401         
9402         var input =  {
9403             tag: 'input',
9404             id : id,
9405             type : this.inputType,
9406             cls : 'form-control',
9407             autocomplete: 'new-password',
9408             placeholder : this.placeholder || '' 
9409             
9410         };
9411         if (this.name) {
9412             input.name = this.name;
9413         }
9414         if (this.size) {
9415             input.cls += ' input-' + this.size;
9416         }
9417         
9418         if (this.disabled) {
9419             input.disabled=true;
9420         }
9421         
9422         var inputblock = input;
9423         
9424         if(this.hasFeedback && !this.allowBlank){
9425             
9426             var feedback = {
9427                 tag: 'span',
9428                 cls: 'glyphicon form-control-feedback'
9429             };
9430             
9431             if(this.removable && !this.editable && !this.tickable){
9432                 inputblock = {
9433                     cls : 'has-feedback',
9434                     cn :  [
9435                         inputblock,
9436                         {
9437                             tag: 'button',
9438                             html : 'x',
9439                             cls : 'roo-combo-removable-btn close'
9440                         },
9441                         feedback
9442                     ] 
9443                 };
9444             } else {
9445                 inputblock = {
9446                     cls : 'has-feedback',
9447                     cn :  [
9448                         inputblock,
9449                         feedback
9450                     ] 
9451                 };
9452             }
9453
9454         } else {
9455             if(this.removable && !this.editable && !this.tickable){
9456                 inputblock = {
9457                     cls : 'roo-removable',
9458                     cn :  [
9459                         inputblock,
9460                         {
9461                             tag: 'button',
9462                             html : 'x',
9463                             cls : 'roo-combo-removable-btn close'
9464                         }
9465                     ] 
9466                 };
9467             }
9468         }
9469         
9470         if (this.before || this.after) {
9471             
9472             inputblock = {
9473                 cls : 'input-group',
9474                 cn :  [] 
9475             };
9476             if (this.before) {
9477                 inputblock.cn.push({
9478                     tag :'span',
9479                     cls : 'input-group-addon',
9480                     html : this.before
9481                 });
9482             }
9483             
9484             inputblock.cn.push(input);
9485             
9486             if(this.hasFeedback && !this.allowBlank){
9487                 inputblock.cls += ' has-feedback';
9488                 inputblock.cn.push(feedback);
9489             }
9490             
9491             if (this.after) {
9492                 inputblock.cn.push({
9493                     tag :'span',
9494                     cls : 'input-group-addon',
9495                     html : this.after
9496                 });
9497             }
9498             
9499         };
9500         
9501         var box = {
9502             tag: 'div',
9503             cn: [
9504                 {
9505                     tag: 'input',
9506                     type : 'hidden',
9507                     cls: 'form-hidden-field'
9508                 },
9509                 inputblock
9510             ]
9511             
9512         };
9513         
9514         if(this.multiple){
9515             box = {
9516                 tag: 'div',
9517                 cn: [
9518                     {
9519                         tag: 'input',
9520                         type : 'hidden',
9521                         cls: 'form-hidden-field'
9522                     },
9523                     {
9524                         tag: 'ul',
9525                         cls: 'roo-select2-choices',
9526                         cn:[
9527                             {
9528                                 tag: 'li',
9529                                 cls: 'roo-select2-search-field',
9530                                 cn: [
9531
9532                                     inputblock
9533                                 ]
9534                             }
9535                         ]
9536                     }
9537                 ]
9538             }
9539         };
9540         
9541         var combobox = {
9542             cls: 'roo-select2-container input-group',
9543             cn: [
9544                 box
9545 //                {
9546 //                    tag: 'ul',
9547 //                    cls: 'typeahead typeahead-long dropdown-menu',
9548 //                    style: 'display:none'
9549 //                }
9550             ]
9551         };
9552         
9553         if(!this.multiple && this.showToggleBtn){
9554             
9555             var caret = {
9556                         tag: 'span',
9557                         cls: 'caret'
9558              };
9559             if (this.caret != false) {
9560                 caret = {
9561                      tag: 'i',
9562                      cls: 'fa fa-' + this.caret
9563                 };
9564                 
9565             }
9566             
9567             combobox.cn.push({
9568                 tag :'span',
9569                 cls : 'input-group-addon btn dropdown-toggle',
9570                 cn : [
9571                     caret,
9572                     {
9573                         tag: 'span',
9574                         cls: 'combobox-clear',
9575                         cn  : [
9576                             {
9577                                 tag : 'i',
9578                                 cls: 'icon-remove'
9579                             }
9580                         ]
9581                     }
9582                 ]
9583
9584             })
9585         }
9586         
9587         if(this.multiple){
9588             combobox.cls += ' roo-select2-container-multi';
9589         }
9590         
9591         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9592             
9593 //                Roo.log("left and has label");
9594             cfg.cn = [
9595                 {
9596                     tag : 'i',
9597                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9598                     tooltip : 'This field is required'
9599                 },
9600                 {
9601                     tag: 'label',
9602                     'for' :  id,
9603                     cls : 'control-label col-sm-' + this.labelWidth,
9604                     html : this.fieldLabel
9605
9606                 },
9607                 {
9608                     cls : "col-sm-" + (12 - this.labelWidth), 
9609                     cn: [
9610                         combobox
9611                     ]
9612                 }
9613
9614             ];
9615             
9616             if(this.indicatorpos == 'right'){
9617                 cfg.cn = [
9618                     {
9619                         tag: 'label',
9620                         'for' :  id,
9621                         cls : 'control-label col-sm-' + this.labelWidth,
9622                         html : this.fieldLabel
9623
9624                     },
9625                     {
9626                         tag : 'i',
9627                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9628                         tooltip : 'This field is required'
9629                     },
9630                     {
9631                         cls : "col-sm-" + (12 - this.labelWidth), 
9632                         cn: [
9633                             combobox
9634                         ]
9635                     }
9636
9637                 ];
9638             }
9639             
9640         } else if ( this.fieldLabel.length) {
9641 //                Roo.log(" label");
9642             cfg.cn = [
9643                 {
9644                    tag : 'i',
9645                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9646                    tooltip : 'This field is required'
9647                },
9648                {
9649                    tag: 'label',
9650                    //cls : 'input-group-addon',
9651                    html : this.fieldLabel
9652
9653                },
9654
9655                combobox
9656
9657             ];
9658             
9659             if(this.indicatorpos == 'right'){
9660                 
9661                 cfg.cn = [
9662                     {
9663                        tag: 'label',
9664                        //cls : 'input-group-addon',
9665                        html : this.fieldLabel
9666
9667                     },
9668                     {
9669                        tag : 'i',
9670                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9671                        tooltip : 'This field is required'
9672                     },
9673                     
9674                     combobox
9675
9676                 ];
9677
9678             }
9679
9680         } else {
9681             
9682 //                Roo.log(" no label && no align");
9683                 cfg = combobox
9684                      
9685                 
9686         }
9687          
9688         var settings=this;
9689         ['xs','sm','md','lg'].map(function(size){
9690             if (settings[size]) {
9691                 cfg.cls += ' col-' + size + '-' + settings[size];
9692             }
9693         });
9694         
9695         return cfg;
9696         
9697     },
9698     
9699     
9700     
9701     // private
9702     onResize : function(w, h){
9703 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9704 //        if(typeof w == 'number'){
9705 //            var x = w - this.trigger.getWidth();
9706 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9707 //            this.trigger.setStyle('left', x+'px');
9708 //        }
9709     },
9710
9711     // private
9712     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9713
9714     // private
9715     getResizeEl : function(){
9716         return this.inputEl();
9717     },
9718
9719     // private
9720     getPositionEl : function(){
9721         return this.inputEl();
9722     },
9723
9724     // private
9725     alignErrorIcon : function(){
9726         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9727     },
9728
9729     // private
9730     initEvents : function(){
9731         
9732         this.createList();
9733         
9734         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9735         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9736         if(!this.multiple && this.showToggleBtn){
9737             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9738             if(this.hideTrigger){
9739                 this.trigger.setDisplayed(false);
9740             }
9741             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9742         }
9743         
9744         if(this.multiple){
9745             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9746         }
9747         
9748         if(this.removable && !this.editable && !this.tickable){
9749             var close = this.closeTriggerEl();
9750             
9751             if(close){
9752                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9753                 close.on('click', this.removeBtnClick, this, close);
9754             }
9755         }
9756         
9757         //this.trigger.addClassOnOver('x-form-trigger-over');
9758         //this.trigger.addClassOnClick('x-form-trigger-click');
9759         
9760         //if(!this.width){
9761         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9762         //}
9763     },
9764     
9765     closeTriggerEl : function()
9766     {
9767         var close = this.el.select('.roo-combo-removable-btn', true).first();
9768         return close ? close : false;
9769     },
9770     
9771     removeBtnClick : function(e, h, el)
9772     {
9773         e.preventDefault();
9774         
9775         if(this.fireEvent("remove", this) !== false){
9776             this.reset();
9777             this.fireEvent("afterremove", this)
9778         }
9779     },
9780     
9781     createList : function()
9782     {
9783         this.list = Roo.get(document.body).createChild({
9784             tag: 'ul',
9785             cls: 'typeahead typeahead-long dropdown-menu',
9786             style: 'display:none'
9787         });
9788         
9789         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9790         
9791     },
9792
9793     // private
9794     initTrigger : function(){
9795        
9796     },
9797
9798     // private
9799     onDestroy : function(){
9800         if(this.trigger){
9801             this.trigger.removeAllListeners();
9802           //  this.trigger.remove();
9803         }
9804         //if(this.wrap){
9805         //    this.wrap.remove();
9806         //}
9807         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9808     },
9809
9810     // private
9811     onFocus : function(){
9812         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9813         /*
9814         if(!this.mimicing){
9815             this.wrap.addClass('x-trigger-wrap-focus');
9816             this.mimicing = true;
9817             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9818             if(this.monitorTab){
9819                 this.el.on("keydown", this.checkTab, this);
9820             }
9821         }
9822         */
9823     },
9824
9825     // private
9826     checkTab : function(e){
9827         if(e.getKey() == e.TAB){
9828             this.triggerBlur();
9829         }
9830     },
9831
9832     // private
9833     onBlur : function(){
9834         // do nothing
9835     },
9836
9837     // private
9838     mimicBlur : function(e, t){
9839         /*
9840         if(!this.wrap.contains(t) && this.validateBlur()){
9841             this.triggerBlur();
9842         }
9843         */
9844     },
9845
9846     // private
9847     triggerBlur : function(){
9848         this.mimicing = false;
9849         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9850         if(this.monitorTab){
9851             this.el.un("keydown", this.checkTab, this);
9852         }
9853         //this.wrap.removeClass('x-trigger-wrap-focus');
9854         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9855     },
9856
9857     // private
9858     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9859     validateBlur : function(e, t){
9860         return true;
9861     },
9862
9863     // private
9864     onDisable : function(){
9865         this.inputEl().dom.disabled = true;
9866         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9867         //if(this.wrap){
9868         //    this.wrap.addClass('x-item-disabled');
9869         //}
9870     },
9871
9872     // private
9873     onEnable : function(){
9874         this.inputEl().dom.disabled = false;
9875         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9876         //if(this.wrap){
9877         //    this.el.removeClass('x-item-disabled');
9878         //}
9879     },
9880
9881     // private
9882     onShow : function(){
9883         var ae = this.getActionEl();
9884         
9885         if(ae){
9886             ae.dom.style.display = '';
9887             ae.dom.style.visibility = 'visible';
9888         }
9889     },
9890
9891     // private
9892     
9893     onHide : function(){
9894         var ae = this.getActionEl();
9895         ae.dom.style.display = 'none';
9896     },
9897
9898     /**
9899      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9900      * by an implementing function.
9901      * @method
9902      * @param {EventObject} e
9903      */
9904     onTriggerClick : Roo.emptyFn
9905 });
9906  /*
9907  * Based on:
9908  * Ext JS Library 1.1.1
9909  * Copyright(c) 2006-2007, Ext JS, LLC.
9910  *
9911  * Originally Released Under LGPL - original licence link has changed is not relivant.
9912  *
9913  * Fork - LGPL
9914  * <script type="text/javascript">
9915  */
9916
9917
9918 /**
9919  * @class Roo.data.SortTypes
9920  * @singleton
9921  * Defines the default sorting (casting?) comparison functions used when sorting data.
9922  */
9923 Roo.data.SortTypes = {
9924     /**
9925      * Default sort that does nothing
9926      * @param {Mixed} s The value being converted
9927      * @return {Mixed} The comparison value
9928      */
9929     none : function(s){
9930         return s;
9931     },
9932     
9933     /**
9934      * The regular expression used to strip tags
9935      * @type {RegExp}
9936      * @property
9937      */
9938     stripTagsRE : /<\/?[^>]+>/gi,
9939     
9940     /**
9941      * Strips all HTML tags to sort on text only
9942      * @param {Mixed} s The value being converted
9943      * @return {String} The comparison value
9944      */
9945     asText : function(s){
9946         return String(s).replace(this.stripTagsRE, "");
9947     },
9948     
9949     /**
9950      * Strips all HTML tags to sort on text only - Case insensitive
9951      * @param {Mixed} s The value being converted
9952      * @return {String} The comparison value
9953      */
9954     asUCText : function(s){
9955         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9956     },
9957     
9958     /**
9959      * Case insensitive string
9960      * @param {Mixed} s The value being converted
9961      * @return {String} The comparison value
9962      */
9963     asUCString : function(s) {
9964         return String(s).toUpperCase();
9965     },
9966     
9967     /**
9968      * Date sorting
9969      * @param {Mixed} s The value being converted
9970      * @return {Number} The comparison value
9971      */
9972     asDate : function(s) {
9973         if(!s){
9974             return 0;
9975         }
9976         if(s instanceof Date){
9977             return s.getTime();
9978         }
9979         return Date.parse(String(s));
9980     },
9981     
9982     /**
9983      * Float sorting
9984      * @param {Mixed} s The value being converted
9985      * @return {Float} The comparison value
9986      */
9987     asFloat : function(s) {
9988         var val = parseFloat(String(s).replace(/,/g, ""));
9989         if(isNaN(val)) {
9990             val = 0;
9991         }
9992         return val;
9993     },
9994     
9995     /**
9996      * Integer sorting
9997      * @param {Mixed} s The value being converted
9998      * @return {Number} The comparison value
9999      */
10000     asInt : function(s) {
10001         var val = parseInt(String(s).replace(/,/g, ""));
10002         if(isNaN(val)) {
10003             val = 0;
10004         }
10005         return val;
10006     }
10007 };/*
10008  * Based on:
10009  * Ext JS Library 1.1.1
10010  * Copyright(c) 2006-2007, Ext JS, LLC.
10011  *
10012  * Originally Released Under LGPL - original licence link has changed is not relivant.
10013  *
10014  * Fork - LGPL
10015  * <script type="text/javascript">
10016  */
10017
10018 /**
10019 * @class Roo.data.Record
10020  * Instances of this class encapsulate both record <em>definition</em> information, and record
10021  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10022  * to access Records cached in an {@link Roo.data.Store} object.<br>
10023  * <p>
10024  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10025  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10026  * objects.<br>
10027  * <p>
10028  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10029  * @constructor
10030  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10031  * {@link #create}. The parameters are the same.
10032  * @param {Array} data An associative Array of data values keyed by the field name.
10033  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10034  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10035  * not specified an integer id is generated.
10036  */
10037 Roo.data.Record = function(data, id){
10038     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10039     this.data = data;
10040 };
10041
10042 /**
10043  * Generate a constructor for a specific record layout.
10044  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10045  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10046  * Each field definition object may contain the following properties: <ul>
10047  * <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,
10048  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10049  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10050  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10051  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10052  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10053  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10054  * this may be omitted.</p></li>
10055  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10056  * <ul><li>auto (Default, implies no conversion)</li>
10057  * <li>string</li>
10058  * <li>int</li>
10059  * <li>float</li>
10060  * <li>boolean</li>
10061  * <li>date</li></ul></p></li>
10062  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10063  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10064  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10065  * by the Reader into an object that will be stored in the Record. It is passed the
10066  * following parameters:<ul>
10067  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10068  * </ul></p></li>
10069  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10070  * </ul>
10071  * <br>usage:<br><pre><code>
10072 var TopicRecord = Roo.data.Record.create(
10073     {name: 'title', mapping: 'topic_title'},
10074     {name: 'author', mapping: 'username'},
10075     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10076     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10077     {name: 'lastPoster', mapping: 'user2'},
10078     {name: 'excerpt', mapping: 'post_text'}
10079 );
10080
10081 var myNewRecord = new TopicRecord({
10082     title: 'Do my job please',
10083     author: 'noobie',
10084     totalPosts: 1,
10085     lastPost: new Date(),
10086     lastPoster: 'Animal',
10087     excerpt: 'No way dude!'
10088 });
10089 myStore.add(myNewRecord);
10090 </code></pre>
10091  * @method create
10092  * @static
10093  */
10094 Roo.data.Record.create = function(o){
10095     var f = function(){
10096         f.superclass.constructor.apply(this, arguments);
10097     };
10098     Roo.extend(f, Roo.data.Record);
10099     var p = f.prototype;
10100     p.fields = new Roo.util.MixedCollection(false, function(field){
10101         return field.name;
10102     });
10103     for(var i = 0, len = o.length; i < len; i++){
10104         p.fields.add(new Roo.data.Field(o[i]));
10105     }
10106     f.getField = function(name){
10107         return p.fields.get(name);  
10108     };
10109     return f;
10110 };
10111
10112 Roo.data.Record.AUTO_ID = 1000;
10113 Roo.data.Record.EDIT = 'edit';
10114 Roo.data.Record.REJECT = 'reject';
10115 Roo.data.Record.COMMIT = 'commit';
10116
10117 Roo.data.Record.prototype = {
10118     /**
10119      * Readonly flag - true if this record has been modified.
10120      * @type Boolean
10121      */
10122     dirty : false,
10123     editing : false,
10124     error: null,
10125     modified: null,
10126
10127     // private
10128     join : function(store){
10129         this.store = store;
10130     },
10131
10132     /**
10133      * Set the named field to the specified value.
10134      * @param {String} name The name of the field to set.
10135      * @param {Object} value The value to set the field to.
10136      */
10137     set : function(name, value){
10138         if(this.data[name] == value){
10139             return;
10140         }
10141         this.dirty = true;
10142         if(!this.modified){
10143             this.modified = {};
10144         }
10145         if(typeof this.modified[name] == 'undefined'){
10146             this.modified[name] = this.data[name];
10147         }
10148         this.data[name] = value;
10149         if(!this.editing && this.store){
10150             this.store.afterEdit(this);
10151         }       
10152     },
10153
10154     /**
10155      * Get the value of the named field.
10156      * @param {String} name The name of the field to get the value of.
10157      * @return {Object} The value of the field.
10158      */
10159     get : function(name){
10160         return this.data[name]; 
10161     },
10162
10163     // private
10164     beginEdit : function(){
10165         this.editing = true;
10166         this.modified = {}; 
10167     },
10168
10169     // private
10170     cancelEdit : function(){
10171         this.editing = false;
10172         delete this.modified;
10173     },
10174
10175     // private
10176     endEdit : function(){
10177         this.editing = false;
10178         if(this.dirty && this.store){
10179             this.store.afterEdit(this);
10180         }
10181     },
10182
10183     /**
10184      * Usually called by the {@link Roo.data.Store} which owns the Record.
10185      * Rejects all changes made to the Record since either creation, or the last commit operation.
10186      * Modified fields are reverted to their original values.
10187      * <p>
10188      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10189      * of reject operations.
10190      */
10191     reject : function(){
10192         var m = this.modified;
10193         for(var n in m){
10194             if(typeof m[n] != "function"){
10195                 this.data[n] = m[n];
10196             }
10197         }
10198         this.dirty = false;
10199         delete this.modified;
10200         this.editing = false;
10201         if(this.store){
10202             this.store.afterReject(this);
10203         }
10204     },
10205
10206     /**
10207      * Usually called by the {@link Roo.data.Store} which owns the Record.
10208      * Commits all changes made to the Record since either creation, or the last commit operation.
10209      * <p>
10210      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10211      * of commit operations.
10212      */
10213     commit : function(){
10214         this.dirty = false;
10215         delete this.modified;
10216         this.editing = false;
10217         if(this.store){
10218             this.store.afterCommit(this);
10219         }
10220     },
10221
10222     // private
10223     hasError : function(){
10224         return this.error != null;
10225     },
10226
10227     // private
10228     clearError : function(){
10229         this.error = null;
10230     },
10231
10232     /**
10233      * Creates a copy of this record.
10234      * @param {String} id (optional) A new record id if you don't want to use this record's id
10235      * @return {Record}
10236      */
10237     copy : function(newId) {
10238         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10239     }
10240 };/*
10241  * Based on:
10242  * Ext JS Library 1.1.1
10243  * Copyright(c) 2006-2007, Ext JS, LLC.
10244  *
10245  * Originally Released Under LGPL - original licence link has changed is not relivant.
10246  *
10247  * Fork - LGPL
10248  * <script type="text/javascript">
10249  */
10250
10251
10252
10253 /**
10254  * @class Roo.data.Store
10255  * @extends Roo.util.Observable
10256  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10257  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10258  * <p>
10259  * 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
10260  * has no knowledge of the format of the data returned by the Proxy.<br>
10261  * <p>
10262  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10263  * instances from the data object. These records are cached and made available through accessor functions.
10264  * @constructor
10265  * Creates a new Store.
10266  * @param {Object} config A config object containing the objects needed for the Store to access data,
10267  * and read the data into Records.
10268  */
10269 Roo.data.Store = function(config){
10270     this.data = new Roo.util.MixedCollection(false);
10271     this.data.getKey = function(o){
10272         return o.id;
10273     };
10274     this.baseParams = {};
10275     // private
10276     this.paramNames = {
10277         "start" : "start",
10278         "limit" : "limit",
10279         "sort" : "sort",
10280         "dir" : "dir",
10281         "multisort" : "_multisort"
10282     };
10283
10284     if(config && config.data){
10285         this.inlineData = config.data;
10286         delete config.data;
10287     }
10288
10289     Roo.apply(this, config);
10290     
10291     if(this.reader){ // reader passed
10292         this.reader = Roo.factory(this.reader, Roo.data);
10293         this.reader.xmodule = this.xmodule || false;
10294         if(!this.recordType){
10295             this.recordType = this.reader.recordType;
10296         }
10297         if(this.reader.onMetaChange){
10298             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10299         }
10300     }
10301
10302     if(this.recordType){
10303         this.fields = this.recordType.prototype.fields;
10304     }
10305     this.modified = [];
10306
10307     this.addEvents({
10308         /**
10309          * @event datachanged
10310          * Fires when the data cache has changed, and a widget which is using this Store
10311          * as a Record cache should refresh its view.
10312          * @param {Store} this
10313          */
10314         datachanged : true,
10315         /**
10316          * @event metachange
10317          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10318          * @param {Store} this
10319          * @param {Object} meta The JSON metadata
10320          */
10321         metachange : true,
10322         /**
10323          * @event add
10324          * Fires when Records have been added to the Store
10325          * @param {Store} this
10326          * @param {Roo.data.Record[]} records The array of Records added
10327          * @param {Number} index The index at which the record(s) were added
10328          */
10329         add : true,
10330         /**
10331          * @event remove
10332          * Fires when a Record has been removed from the Store
10333          * @param {Store} this
10334          * @param {Roo.data.Record} record The Record that was removed
10335          * @param {Number} index The index at which the record was removed
10336          */
10337         remove : true,
10338         /**
10339          * @event update
10340          * Fires when a Record has been updated
10341          * @param {Store} this
10342          * @param {Roo.data.Record} record The Record that was updated
10343          * @param {String} operation The update operation being performed.  Value may be one of:
10344          * <pre><code>
10345  Roo.data.Record.EDIT
10346  Roo.data.Record.REJECT
10347  Roo.data.Record.COMMIT
10348          * </code></pre>
10349          */
10350         update : true,
10351         /**
10352          * @event clear
10353          * Fires when the data cache has been cleared.
10354          * @param {Store} this
10355          */
10356         clear : true,
10357         /**
10358          * @event beforeload
10359          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10360          * the load action will be canceled.
10361          * @param {Store} this
10362          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10363          */
10364         beforeload : true,
10365         /**
10366          * @event beforeloadadd
10367          * Fires after a new set of Records has been loaded.
10368          * @param {Store} this
10369          * @param {Roo.data.Record[]} records The Records that were loaded
10370          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10371          */
10372         beforeloadadd : true,
10373         /**
10374          * @event load
10375          * Fires after a new set of Records has been loaded, before they are added to the store.
10376          * @param {Store} this
10377          * @param {Roo.data.Record[]} records The Records that were loaded
10378          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10379          * @params {Object} return from reader
10380          */
10381         load : true,
10382         /**
10383          * @event loadexception
10384          * Fires if an exception occurs in the Proxy during loading.
10385          * Called with the signature of the Proxy's "loadexception" event.
10386          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10387          * 
10388          * @param {Proxy} 
10389          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10390          * @param {Object} load options 
10391          * @param {Object} jsonData from your request (normally this contains the Exception)
10392          */
10393         loadexception : true
10394     });
10395     
10396     if(this.proxy){
10397         this.proxy = Roo.factory(this.proxy, Roo.data);
10398         this.proxy.xmodule = this.xmodule || false;
10399         this.relayEvents(this.proxy,  ["loadexception"]);
10400     }
10401     this.sortToggle = {};
10402     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10403
10404     Roo.data.Store.superclass.constructor.call(this);
10405
10406     if(this.inlineData){
10407         this.loadData(this.inlineData);
10408         delete this.inlineData;
10409     }
10410 };
10411
10412 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10413      /**
10414     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10415     * without a remote query - used by combo/forms at present.
10416     */
10417     
10418     /**
10419     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10420     */
10421     /**
10422     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10423     */
10424     /**
10425     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10426     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10427     */
10428     /**
10429     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10430     * on any HTTP request
10431     */
10432     /**
10433     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10434     */
10435     /**
10436     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10437     */
10438     multiSort: false,
10439     /**
10440     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10441     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10442     */
10443     remoteSort : false,
10444
10445     /**
10446     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10447      * loaded or when a record is removed. (defaults to false).
10448     */
10449     pruneModifiedRecords : false,
10450
10451     // private
10452     lastOptions : null,
10453
10454     /**
10455      * Add Records to the Store and fires the add event.
10456      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10457      */
10458     add : function(records){
10459         records = [].concat(records);
10460         for(var i = 0, len = records.length; i < len; i++){
10461             records[i].join(this);
10462         }
10463         var index = this.data.length;
10464         this.data.addAll(records);
10465         this.fireEvent("add", this, records, index);
10466     },
10467
10468     /**
10469      * Remove a Record from the Store and fires the remove event.
10470      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10471      */
10472     remove : function(record){
10473         var index = this.data.indexOf(record);
10474         this.data.removeAt(index);
10475         if(this.pruneModifiedRecords){
10476             this.modified.remove(record);
10477         }
10478         this.fireEvent("remove", this, record, index);
10479     },
10480
10481     /**
10482      * Remove all Records from the Store and fires the clear event.
10483      */
10484     removeAll : function(){
10485         this.data.clear();
10486         if(this.pruneModifiedRecords){
10487             this.modified = [];
10488         }
10489         this.fireEvent("clear", this);
10490     },
10491
10492     /**
10493      * Inserts Records to the Store at the given index and fires the add event.
10494      * @param {Number} index The start index at which to insert the passed Records.
10495      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10496      */
10497     insert : function(index, records){
10498         records = [].concat(records);
10499         for(var i = 0, len = records.length; i < len; i++){
10500             this.data.insert(index, records[i]);
10501             records[i].join(this);
10502         }
10503         this.fireEvent("add", this, records, index);
10504     },
10505
10506     /**
10507      * Get the index within the cache of the passed Record.
10508      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10509      * @return {Number} The index of the passed Record. Returns -1 if not found.
10510      */
10511     indexOf : function(record){
10512         return this.data.indexOf(record);
10513     },
10514
10515     /**
10516      * Get the index within the cache of the Record with the passed id.
10517      * @param {String} id The id of the Record to find.
10518      * @return {Number} The index of the Record. Returns -1 if not found.
10519      */
10520     indexOfId : function(id){
10521         return this.data.indexOfKey(id);
10522     },
10523
10524     /**
10525      * Get the Record with the specified id.
10526      * @param {String} id The id of the Record to find.
10527      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10528      */
10529     getById : function(id){
10530         return this.data.key(id);
10531     },
10532
10533     /**
10534      * Get the Record at the specified index.
10535      * @param {Number} index The index of the Record to find.
10536      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10537      */
10538     getAt : function(index){
10539         return this.data.itemAt(index);
10540     },
10541
10542     /**
10543      * Returns a range of Records between specified indices.
10544      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10545      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10546      * @return {Roo.data.Record[]} An array of Records
10547      */
10548     getRange : function(start, end){
10549         return this.data.getRange(start, end);
10550     },
10551
10552     // private
10553     storeOptions : function(o){
10554         o = Roo.apply({}, o);
10555         delete o.callback;
10556         delete o.scope;
10557         this.lastOptions = o;
10558     },
10559
10560     /**
10561      * Loads the Record cache from the configured Proxy using the configured Reader.
10562      * <p>
10563      * If using remote paging, then the first load call must specify the <em>start</em>
10564      * and <em>limit</em> properties in the options.params property to establish the initial
10565      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10566      * <p>
10567      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10568      * and this call will return before the new data has been loaded. Perform any post-processing
10569      * in a callback function, or in a "load" event handler.</strong>
10570      * <p>
10571      * @param {Object} options An object containing properties which control loading options:<ul>
10572      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10573      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10574      * passed the following arguments:<ul>
10575      * <li>r : Roo.data.Record[]</li>
10576      * <li>options: Options object from the load call</li>
10577      * <li>success: Boolean success indicator</li></ul></li>
10578      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10579      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10580      * </ul>
10581      */
10582     load : function(options){
10583         options = options || {};
10584         if(this.fireEvent("beforeload", this, options) !== false){
10585             this.storeOptions(options);
10586             var p = Roo.apply(options.params || {}, this.baseParams);
10587             // if meta was not loaded from remote source.. try requesting it.
10588             if (!this.reader.metaFromRemote) {
10589                 p._requestMeta = 1;
10590             }
10591             if(this.sortInfo && this.remoteSort){
10592                 var pn = this.paramNames;
10593                 p[pn["sort"]] = this.sortInfo.field;
10594                 p[pn["dir"]] = this.sortInfo.direction;
10595             }
10596             if (this.multiSort) {
10597                 var pn = this.paramNames;
10598                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10599             }
10600             
10601             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10602         }
10603     },
10604
10605     /**
10606      * Reloads the Record cache from the configured Proxy using the configured Reader and
10607      * the options from the last load operation performed.
10608      * @param {Object} options (optional) An object containing properties which may override the options
10609      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10610      * the most recently used options are reused).
10611      */
10612     reload : function(options){
10613         this.load(Roo.applyIf(options||{}, this.lastOptions));
10614     },
10615
10616     // private
10617     // Called as a callback by the Reader during a load operation.
10618     loadRecords : function(o, options, success){
10619         if(!o || success === false){
10620             if(success !== false){
10621                 this.fireEvent("load", this, [], options, o);
10622             }
10623             if(options.callback){
10624                 options.callback.call(options.scope || this, [], options, false);
10625             }
10626             return;
10627         }
10628         // if data returned failure - throw an exception.
10629         if (o.success === false) {
10630             // show a message if no listener is registered.
10631             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10632                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10633             }
10634             // loadmask wil be hooked into this..
10635             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10636             return;
10637         }
10638         var r = o.records, t = o.totalRecords || r.length;
10639         
10640         this.fireEvent("beforeloadadd", this, r, options, o);
10641         
10642         if(!options || options.add !== true){
10643             if(this.pruneModifiedRecords){
10644                 this.modified = [];
10645             }
10646             for(var i = 0, len = r.length; i < len; i++){
10647                 r[i].join(this);
10648             }
10649             if(this.snapshot){
10650                 this.data = this.snapshot;
10651                 delete this.snapshot;
10652             }
10653             this.data.clear();
10654             this.data.addAll(r);
10655             this.totalLength = t;
10656             this.applySort();
10657             this.fireEvent("datachanged", this);
10658         }else{
10659             this.totalLength = Math.max(t, this.data.length+r.length);
10660             this.add(r);
10661         }
10662         this.fireEvent("load", this, r, options, o);
10663         if(options.callback){
10664             options.callback.call(options.scope || this, r, options, true);
10665         }
10666     },
10667
10668
10669     /**
10670      * Loads data from a passed data block. A Reader which understands the format of the data
10671      * must have been configured in the constructor.
10672      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10673      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10674      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10675      */
10676     loadData : function(o, append){
10677         var r = this.reader.readRecords(o);
10678         this.loadRecords(r, {add: append}, true);
10679     },
10680
10681     /**
10682      * Gets the number of cached records.
10683      * <p>
10684      * <em>If using paging, this may not be the total size of the dataset. If the data object
10685      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10686      * the data set size</em>
10687      */
10688     getCount : function(){
10689         return this.data.length || 0;
10690     },
10691
10692     /**
10693      * Gets the total number of records in the dataset as returned by the server.
10694      * <p>
10695      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10696      * the dataset size</em>
10697      */
10698     getTotalCount : function(){
10699         return this.totalLength || 0;
10700     },
10701
10702     /**
10703      * Returns the sort state of the Store as an object with two properties:
10704      * <pre><code>
10705  field {String} The name of the field by which the Records are sorted
10706  direction {String} The sort order, "ASC" or "DESC"
10707      * </code></pre>
10708      */
10709     getSortState : function(){
10710         return this.sortInfo;
10711     },
10712
10713     // private
10714     applySort : function(){
10715         if(this.sortInfo && !this.remoteSort){
10716             var s = this.sortInfo, f = s.field;
10717             var st = this.fields.get(f).sortType;
10718             var fn = function(r1, r2){
10719                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10720                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10721             };
10722             this.data.sort(s.direction, fn);
10723             if(this.snapshot && this.snapshot != this.data){
10724                 this.snapshot.sort(s.direction, fn);
10725             }
10726         }
10727     },
10728
10729     /**
10730      * Sets the default sort column and order to be used by the next load operation.
10731      * @param {String} fieldName The name of the field to sort by.
10732      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10733      */
10734     setDefaultSort : function(field, dir){
10735         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10736     },
10737
10738     /**
10739      * Sort the Records.
10740      * If remote sorting is used, the sort is performed on the server, and the cache is
10741      * reloaded. If local sorting is used, the cache is sorted internally.
10742      * @param {String} fieldName The name of the field to sort by.
10743      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10744      */
10745     sort : function(fieldName, dir){
10746         var f = this.fields.get(fieldName);
10747         if(!dir){
10748             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10749             
10750             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10751                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10752             }else{
10753                 dir = f.sortDir;
10754             }
10755         }
10756         this.sortToggle[f.name] = dir;
10757         this.sortInfo = {field: f.name, direction: dir};
10758         if(!this.remoteSort){
10759             this.applySort();
10760             this.fireEvent("datachanged", this);
10761         }else{
10762             this.load(this.lastOptions);
10763         }
10764     },
10765
10766     /**
10767      * Calls the specified function for each of the Records in the cache.
10768      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10769      * Returning <em>false</em> aborts and exits the iteration.
10770      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10771      */
10772     each : function(fn, scope){
10773         this.data.each(fn, scope);
10774     },
10775
10776     /**
10777      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10778      * (e.g., during paging).
10779      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10780      */
10781     getModifiedRecords : function(){
10782         return this.modified;
10783     },
10784
10785     // private
10786     createFilterFn : function(property, value, anyMatch){
10787         if(!value.exec){ // not a regex
10788             value = String(value);
10789             if(value.length == 0){
10790                 return false;
10791             }
10792             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10793         }
10794         return function(r){
10795             return value.test(r.data[property]);
10796         };
10797     },
10798
10799     /**
10800      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10801      * @param {String} property A field on your records
10802      * @param {Number} start The record index to start at (defaults to 0)
10803      * @param {Number} end The last record index to include (defaults to length - 1)
10804      * @return {Number} The sum
10805      */
10806     sum : function(property, start, end){
10807         var rs = this.data.items, v = 0;
10808         start = start || 0;
10809         end = (end || end === 0) ? end : rs.length-1;
10810
10811         for(var i = start; i <= end; i++){
10812             v += (rs[i].data[property] || 0);
10813         }
10814         return v;
10815     },
10816
10817     /**
10818      * Filter the records by a specified property.
10819      * @param {String} field A field on your records
10820      * @param {String/RegExp} value Either a string that the field
10821      * should start with or a RegExp to test against the field
10822      * @param {Boolean} anyMatch True to match any part not just the beginning
10823      */
10824     filter : function(property, value, anyMatch){
10825         var fn = this.createFilterFn(property, value, anyMatch);
10826         return fn ? this.filterBy(fn) : this.clearFilter();
10827     },
10828
10829     /**
10830      * Filter by a function. The specified function will be called with each
10831      * record in this data source. If the function returns true the record is included,
10832      * otherwise it is filtered.
10833      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10834      * @param {Object} scope (optional) The scope of the function (defaults to this)
10835      */
10836     filterBy : function(fn, scope){
10837         this.snapshot = this.snapshot || this.data;
10838         this.data = this.queryBy(fn, scope||this);
10839         this.fireEvent("datachanged", this);
10840     },
10841
10842     /**
10843      * Query the records by a specified property.
10844      * @param {String} field A field on your records
10845      * @param {String/RegExp} value Either a string that the field
10846      * should start with or a RegExp to test against the field
10847      * @param {Boolean} anyMatch True to match any part not just the beginning
10848      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10849      */
10850     query : function(property, value, anyMatch){
10851         var fn = this.createFilterFn(property, value, anyMatch);
10852         return fn ? this.queryBy(fn) : this.data.clone();
10853     },
10854
10855     /**
10856      * Query by a function. The specified function will be called with each
10857      * record in this data source. If the function returns true the record is included
10858      * in the results.
10859      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10860      * @param {Object} scope (optional) The scope of the function (defaults to this)
10861       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10862      **/
10863     queryBy : function(fn, scope){
10864         var data = this.snapshot || this.data;
10865         return data.filterBy(fn, scope||this);
10866     },
10867
10868     /**
10869      * Collects unique values for a particular dataIndex from this store.
10870      * @param {String} dataIndex The property to collect
10871      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10872      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10873      * @return {Array} An array of the unique values
10874      **/
10875     collect : function(dataIndex, allowNull, bypassFilter){
10876         var d = (bypassFilter === true && this.snapshot) ?
10877                 this.snapshot.items : this.data.items;
10878         var v, sv, r = [], l = {};
10879         for(var i = 0, len = d.length; i < len; i++){
10880             v = d[i].data[dataIndex];
10881             sv = String(v);
10882             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10883                 l[sv] = true;
10884                 r[r.length] = v;
10885             }
10886         }
10887         return r;
10888     },
10889
10890     /**
10891      * Revert to a view of the Record cache with no filtering applied.
10892      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10893      */
10894     clearFilter : function(suppressEvent){
10895         if(this.snapshot && this.snapshot != this.data){
10896             this.data = this.snapshot;
10897             delete this.snapshot;
10898             if(suppressEvent !== true){
10899                 this.fireEvent("datachanged", this);
10900             }
10901         }
10902     },
10903
10904     // private
10905     afterEdit : function(record){
10906         if(this.modified.indexOf(record) == -1){
10907             this.modified.push(record);
10908         }
10909         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10910     },
10911     
10912     // private
10913     afterReject : function(record){
10914         this.modified.remove(record);
10915         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10916     },
10917
10918     // private
10919     afterCommit : function(record){
10920         this.modified.remove(record);
10921         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10922     },
10923
10924     /**
10925      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10926      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10927      */
10928     commitChanges : function(){
10929         var m = this.modified.slice(0);
10930         this.modified = [];
10931         for(var i = 0, len = m.length; i < len; i++){
10932             m[i].commit();
10933         }
10934     },
10935
10936     /**
10937      * Cancel outstanding changes on all changed records.
10938      */
10939     rejectChanges : function(){
10940         var m = this.modified.slice(0);
10941         this.modified = [];
10942         for(var i = 0, len = m.length; i < len; i++){
10943             m[i].reject();
10944         }
10945     },
10946
10947     onMetaChange : function(meta, rtype, o){
10948         this.recordType = rtype;
10949         this.fields = rtype.prototype.fields;
10950         delete this.snapshot;
10951         this.sortInfo = meta.sortInfo || this.sortInfo;
10952         this.modified = [];
10953         this.fireEvent('metachange', this, this.reader.meta);
10954     },
10955     
10956     moveIndex : function(data, type)
10957     {
10958         var index = this.indexOf(data);
10959         
10960         var newIndex = index + type;
10961         
10962         this.remove(data);
10963         
10964         this.insert(newIndex, data);
10965         
10966     }
10967 });/*
10968  * Based on:
10969  * Ext JS Library 1.1.1
10970  * Copyright(c) 2006-2007, Ext JS, LLC.
10971  *
10972  * Originally Released Under LGPL - original licence link has changed is not relivant.
10973  *
10974  * Fork - LGPL
10975  * <script type="text/javascript">
10976  */
10977
10978 /**
10979  * @class Roo.data.SimpleStore
10980  * @extends Roo.data.Store
10981  * Small helper class to make creating Stores from Array data easier.
10982  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10983  * @cfg {Array} fields An array of field definition objects, or field name strings.
10984  * @cfg {Array} data The multi-dimensional array of data
10985  * @constructor
10986  * @param {Object} config
10987  */
10988 Roo.data.SimpleStore = function(config){
10989     Roo.data.SimpleStore.superclass.constructor.call(this, {
10990         isLocal : true,
10991         reader: new Roo.data.ArrayReader({
10992                 id: config.id
10993             },
10994             Roo.data.Record.create(config.fields)
10995         ),
10996         proxy : new Roo.data.MemoryProxy(config.data)
10997     });
10998     this.load();
10999 };
11000 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11001  * Based on:
11002  * Ext JS Library 1.1.1
11003  * Copyright(c) 2006-2007, Ext JS, LLC.
11004  *
11005  * Originally Released Under LGPL - original licence link has changed is not relivant.
11006  *
11007  * Fork - LGPL
11008  * <script type="text/javascript">
11009  */
11010
11011 /**
11012 /**
11013  * @extends Roo.data.Store
11014  * @class Roo.data.JsonStore
11015  * Small helper class to make creating Stores for JSON data easier. <br/>
11016 <pre><code>
11017 var store = new Roo.data.JsonStore({
11018     url: 'get-images.php',
11019     root: 'images',
11020     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11021 });
11022 </code></pre>
11023  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11024  * JsonReader and HttpProxy (unless inline data is provided).</b>
11025  * @cfg {Array} fields An array of field definition objects, or field name strings.
11026  * @constructor
11027  * @param {Object} config
11028  */
11029 Roo.data.JsonStore = function(c){
11030     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11031         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11032         reader: new Roo.data.JsonReader(c, c.fields)
11033     }));
11034 };
11035 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11036  * Based on:
11037  * Ext JS Library 1.1.1
11038  * Copyright(c) 2006-2007, Ext JS, LLC.
11039  *
11040  * Originally Released Under LGPL - original licence link has changed is not relivant.
11041  *
11042  * Fork - LGPL
11043  * <script type="text/javascript">
11044  */
11045
11046  
11047 Roo.data.Field = function(config){
11048     if(typeof config == "string"){
11049         config = {name: config};
11050     }
11051     Roo.apply(this, config);
11052     
11053     if(!this.type){
11054         this.type = "auto";
11055     }
11056     
11057     var st = Roo.data.SortTypes;
11058     // named sortTypes are supported, here we look them up
11059     if(typeof this.sortType == "string"){
11060         this.sortType = st[this.sortType];
11061     }
11062     
11063     // set default sortType for strings and dates
11064     if(!this.sortType){
11065         switch(this.type){
11066             case "string":
11067                 this.sortType = st.asUCString;
11068                 break;
11069             case "date":
11070                 this.sortType = st.asDate;
11071                 break;
11072             default:
11073                 this.sortType = st.none;
11074         }
11075     }
11076
11077     // define once
11078     var stripRe = /[\$,%]/g;
11079
11080     // prebuilt conversion function for this field, instead of
11081     // switching every time we're reading a value
11082     if(!this.convert){
11083         var cv, dateFormat = this.dateFormat;
11084         switch(this.type){
11085             case "":
11086             case "auto":
11087             case undefined:
11088                 cv = function(v){ return v; };
11089                 break;
11090             case "string":
11091                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11092                 break;
11093             case "int":
11094                 cv = function(v){
11095                     return v !== undefined && v !== null && v !== '' ?
11096                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11097                     };
11098                 break;
11099             case "float":
11100                 cv = function(v){
11101                     return v !== undefined && v !== null && v !== '' ?
11102                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11103                     };
11104                 break;
11105             case "bool":
11106             case "boolean":
11107                 cv = function(v){ return v === true || v === "true" || v == 1; };
11108                 break;
11109             case "date":
11110                 cv = function(v){
11111                     if(!v){
11112                         return '';
11113                     }
11114                     if(v instanceof Date){
11115                         return v;
11116                     }
11117                     if(dateFormat){
11118                         if(dateFormat == "timestamp"){
11119                             return new Date(v*1000);
11120                         }
11121                         return Date.parseDate(v, dateFormat);
11122                     }
11123                     var parsed = Date.parse(v);
11124                     return parsed ? new Date(parsed) : null;
11125                 };
11126              break;
11127             
11128         }
11129         this.convert = cv;
11130     }
11131 };
11132
11133 Roo.data.Field.prototype = {
11134     dateFormat: null,
11135     defaultValue: "",
11136     mapping: null,
11137     sortType : null,
11138     sortDir : "ASC"
11139 };/*
11140  * Based on:
11141  * Ext JS Library 1.1.1
11142  * Copyright(c) 2006-2007, Ext JS, LLC.
11143  *
11144  * Originally Released Under LGPL - original licence link has changed is not relivant.
11145  *
11146  * Fork - LGPL
11147  * <script type="text/javascript">
11148  */
11149  
11150 // Base class for reading structured data from a data source.  This class is intended to be
11151 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11152
11153 /**
11154  * @class Roo.data.DataReader
11155  * Base class for reading structured data from a data source.  This class is intended to be
11156  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11157  */
11158
11159 Roo.data.DataReader = function(meta, recordType){
11160     
11161     this.meta = meta;
11162     
11163     this.recordType = recordType instanceof Array ? 
11164         Roo.data.Record.create(recordType) : recordType;
11165 };
11166
11167 Roo.data.DataReader.prototype = {
11168      /**
11169      * Create an empty record
11170      * @param {Object} data (optional) - overlay some values
11171      * @return {Roo.data.Record} record created.
11172      */
11173     newRow :  function(d) {
11174         var da =  {};
11175         this.recordType.prototype.fields.each(function(c) {
11176             switch( c.type) {
11177                 case 'int' : da[c.name] = 0; break;
11178                 case 'date' : da[c.name] = new Date(); break;
11179                 case 'float' : da[c.name] = 0.0; break;
11180                 case 'boolean' : da[c.name] = false; break;
11181                 default : da[c.name] = ""; break;
11182             }
11183             
11184         });
11185         return new this.recordType(Roo.apply(da, d));
11186     }
11187     
11188 };/*
11189  * Based on:
11190  * Ext JS Library 1.1.1
11191  * Copyright(c) 2006-2007, Ext JS, LLC.
11192  *
11193  * Originally Released Under LGPL - original licence link has changed is not relivant.
11194  *
11195  * Fork - LGPL
11196  * <script type="text/javascript">
11197  */
11198
11199 /**
11200  * @class Roo.data.DataProxy
11201  * @extends Roo.data.Observable
11202  * This class is an abstract base class for implementations which provide retrieval of
11203  * unformatted data objects.<br>
11204  * <p>
11205  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11206  * (of the appropriate type which knows how to parse the data object) to provide a block of
11207  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11208  * <p>
11209  * Custom implementations must implement the load method as described in
11210  * {@link Roo.data.HttpProxy#load}.
11211  */
11212 Roo.data.DataProxy = function(){
11213     this.addEvents({
11214         /**
11215          * @event beforeload
11216          * Fires before a network request is made to retrieve a data object.
11217          * @param {Object} This DataProxy object.
11218          * @param {Object} params The params parameter to the load function.
11219          */
11220         beforeload : true,
11221         /**
11222          * @event load
11223          * Fires before the load method's callback is called.
11224          * @param {Object} This DataProxy object.
11225          * @param {Object} o The data object.
11226          * @param {Object} arg The callback argument object passed to the load function.
11227          */
11228         load : true,
11229         /**
11230          * @event loadexception
11231          * Fires if an Exception occurs during data retrieval.
11232          * @param {Object} This DataProxy object.
11233          * @param {Object} o The data object.
11234          * @param {Object} arg The callback argument object passed to the load function.
11235          * @param {Object} e The Exception.
11236          */
11237         loadexception : true
11238     });
11239     Roo.data.DataProxy.superclass.constructor.call(this);
11240 };
11241
11242 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11243
11244     /**
11245      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11246      */
11247 /*
11248  * Based on:
11249  * Ext JS Library 1.1.1
11250  * Copyright(c) 2006-2007, Ext JS, LLC.
11251  *
11252  * Originally Released Under LGPL - original licence link has changed is not relivant.
11253  *
11254  * Fork - LGPL
11255  * <script type="text/javascript">
11256  */
11257 /**
11258  * @class Roo.data.MemoryProxy
11259  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11260  * to the Reader when its load method is called.
11261  * @constructor
11262  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11263  */
11264 Roo.data.MemoryProxy = function(data){
11265     if (data.data) {
11266         data = data.data;
11267     }
11268     Roo.data.MemoryProxy.superclass.constructor.call(this);
11269     this.data = data;
11270 };
11271
11272 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11273     
11274     /**
11275      * Load data from the requested source (in this case an in-memory
11276      * data object passed to the constructor), read the data object into
11277      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11278      * process that block using the passed callback.
11279      * @param {Object} params This parameter is not used by the MemoryProxy class.
11280      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11281      * object into a block of Roo.data.Records.
11282      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11283      * The function must be passed <ul>
11284      * <li>The Record block object</li>
11285      * <li>The "arg" argument from the load function</li>
11286      * <li>A boolean success indicator</li>
11287      * </ul>
11288      * @param {Object} scope The scope in which to call the callback
11289      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11290      */
11291     load : function(params, reader, callback, scope, arg){
11292         params = params || {};
11293         var result;
11294         try {
11295             result = reader.readRecords(this.data);
11296         }catch(e){
11297             this.fireEvent("loadexception", this, arg, null, e);
11298             callback.call(scope, null, arg, false);
11299             return;
11300         }
11301         callback.call(scope, result, arg, true);
11302     },
11303     
11304     // private
11305     update : function(params, records){
11306         
11307     }
11308 });/*
11309  * Based on:
11310  * Ext JS Library 1.1.1
11311  * Copyright(c) 2006-2007, Ext JS, LLC.
11312  *
11313  * Originally Released Under LGPL - original licence link has changed is not relivant.
11314  *
11315  * Fork - LGPL
11316  * <script type="text/javascript">
11317  */
11318 /**
11319  * @class Roo.data.HttpProxy
11320  * @extends Roo.data.DataProxy
11321  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11322  * configured to reference a certain URL.<br><br>
11323  * <p>
11324  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11325  * from which the running page was served.<br><br>
11326  * <p>
11327  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11328  * <p>
11329  * Be aware that to enable the browser to parse an XML document, the server must set
11330  * the Content-Type header in the HTTP response to "text/xml".
11331  * @constructor
11332  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11333  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11334  * will be used to make the request.
11335  */
11336 Roo.data.HttpProxy = function(conn){
11337     Roo.data.HttpProxy.superclass.constructor.call(this);
11338     // is conn a conn config or a real conn?
11339     this.conn = conn;
11340     this.useAjax = !conn || !conn.events;
11341   
11342 };
11343
11344 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11345     // thse are take from connection...
11346     
11347     /**
11348      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11349      */
11350     /**
11351      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11352      * extra parameters to each request made by this object. (defaults to undefined)
11353      */
11354     /**
11355      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11356      *  to each request made by this object. (defaults to undefined)
11357      */
11358     /**
11359      * @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)
11360      */
11361     /**
11362      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11363      */
11364      /**
11365      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11366      * @type Boolean
11367      */
11368   
11369
11370     /**
11371      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11372      * @type Boolean
11373      */
11374     /**
11375      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11376      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11377      * a finer-grained basis than the DataProxy events.
11378      */
11379     getConnection : function(){
11380         return this.useAjax ? Roo.Ajax : this.conn;
11381     },
11382
11383     /**
11384      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11385      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11386      * process that block using the passed callback.
11387      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11388      * for the request to the remote server.
11389      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11390      * object into a block of Roo.data.Records.
11391      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11392      * The function must be passed <ul>
11393      * <li>The Record block object</li>
11394      * <li>The "arg" argument from the load function</li>
11395      * <li>A boolean success indicator</li>
11396      * </ul>
11397      * @param {Object} scope The scope in which to call the callback
11398      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11399      */
11400     load : function(params, reader, callback, scope, arg){
11401         if(this.fireEvent("beforeload", this, params) !== false){
11402             var  o = {
11403                 params : params || {},
11404                 request: {
11405                     callback : callback,
11406                     scope : scope,
11407                     arg : arg
11408                 },
11409                 reader: reader,
11410                 callback : this.loadResponse,
11411                 scope: this
11412             };
11413             if(this.useAjax){
11414                 Roo.applyIf(o, this.conn);
11415                 if(this.activeRequest){
11416                     Roo.Ajax.abort(this.activeRequest);
11417                 }
11418                 this.activeRequest = Roo.Ajax.request(o);
11419             }else{
11420                 this.conn.request(o);
11421             }
11422         }else{
11423             callback.call(scope||this, null, arg, false);
11424         }
11425     },
11426
11427     // private
11428     loadResponse : function(o, success, response){
11429         delete this.activeRequest;
11430         if(!success){
11431             this.fireEvent("loadexception", this, o, response);
11432             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11433             return;
11434         }
11435         var result;
11436         try {
11437             result = o.reader.read(response);
11438         }catch(e){
11439             this.fireEvent("loadexception", this, o, response, e);
11440             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11441             return;
11442         }
11443         
11444         this.fireEvent("load", this, o, o.request.arg);
11445         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11446     },
11447
11448     // private
11449     update : function(dataSet){
11450
11451     },
11452
11453     // private
11454     updateResponse : function(dataSet){
11455
11456     }
11457 });/*
11458  * Based on:
11459  * Ext JS Library 1.1.1
11460  * Copyright(c) 2006-2007, Ext JS, LLC.
11461  *
11462  * Originally Released Under LGPL - original licence link has changed is not relivant.
11463  *
11464  * Fork - LGPL
11465  * <script type="text/javascript">
11466  */
11467
11468 /**
11469  * @class Roo.data.ScriptTagProxy
11470  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11471  * other than the originating domain of the running page.<br><br>
11472  * <p>
11473  * <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
11474  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11475  * <p>
11476  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11477  * source code that is used as the source inside a &lt;script> tag.<br><br>
11478  * <p>
11479  * In order for the browser to process the returned data, the server must wrap the data object
11480  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11481  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11482  * depending on whether the callback name was passed:
11483  * <p>
11484  * <pre><code>
11485 boolean scriptTag = false;
11486 String cb = request.getParameter("callback");
11487 if (cb != null) {
11488     scriptTag = true;
11489     response.setContentType("text/javascript");
11490 } else {
11491     response.setContentType("application/x-json");
11492 }
11493 Writer out = response.getWriter();
11494 if (scriptTag) {
11495     out.write(cb + "(");
11496 }
11497 out.print(dataBlock.toJsonString());
11498 if (scriptTag) {
11499     out.write(");");
11500 }
11501 </pre></code>
11502  *
11503  * @constructor
11504  * @param {Object} config A configuration object.
11505  */
11506 Roo.data.ScriptTagProxy = function(config){
11507     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11508     Roo.apply(this, config);
11509     this.head = document.getElementsByTagName("head")[0];
11510 };
11511
11512 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11513
11514 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11515     /**
11516      * @cfg {String} url The URL from which to request the data object.
11517      */
11518     /**
11519      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11520      */
11521     timeout : 30000,
11522     /**
11523      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11524      * the server the name of the callback function set up by the load call to process the returned data object.
11525      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11526      * javascript output which calls this named function passing the data object as its only parameter.
11527      */
11528     callbackParam : "callback",
11529     /**
11530      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11531      * name to the request.
11532      */
11533     nocache : true,
11534
11535     /**
11536      * Load data from the configured URL, read the data object into
11537      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11538      * process that block using the passed callback.
11539      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11540      * for the request to the remote server.
11541      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11542      * object into a block of Roo.data.Records.
11543      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11544      * The function must be passed <ul>
11545      * <li>The Record block object</li>
11546      * <li>The "arg" argument from the load function</li>
11547      * <li>A boolean success indicator</li>
11548      * </ul>
11549      * @param {Object} scope The scope in which to call the callback
11550      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11551      */
11552     load : function(params, reader, callback, scope, arg){
11553         if(this.fireEvent("beforeload", this, params) !== false){
11554
11555             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11556
11557             var url = this.url;
11558             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11559             if(this.nocache){
11560                 url += "&_dc=" + (new Date().getTime());
11561             }
11562             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11563             var trans = {
11564                 id : transId,
11565                 cb : "stcCallback"+transId,
11566                 scriptId : "stcScript"+transId,
11567                 params : params,
11568                 arg : arg,
11569                 url : url,
11570                 callback : callback,
11571                 scope : scope,
11572                 reader : reader
11573             };
11574             var conn = this;
11575
11576             window[trans.cb] = function(o){
11577                 conn.handleResponse(o, trans);
11578             };
11579
11580             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11581
11582             if(this.autoAbort !== false){
11583                 this.abort();
11584             }
11585
11586             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11587
11588             var script = document.createElement("script");
11589             script.setAttribute("src", url);
11590             script.setAttribute("type", "text/javascript");
11591             script.setAttribute("id", trans.scriptId);
11592             this.head.appendChild(script);
11593
11594             this.trans = trans;
11595         }else{
11596             callback.call(scope||this, null, arg, false);
11597         }
11598     },
11599
11600     // private
11601     isLoading : function(){
11602         return this.trans ? true : false;
11603     },
11604
11605     /**
11606      * Abort the current server request.
11607      */
11608     abort : function(){
11609         if(this.isLoading()){
11610             this.destroyTrans(this.trans);
11611         }
11612     },
11613
11614     // private
11615     destroyTrans : function(trans, isLoaded){
11616         this.head.removeChild(document.getElementById(trans.scriptId));
11617         clearTimeout(trans.timeoutId);
11618         if(isLoaded){
11619             window[trans.cb] = undefined;
11620             try{
11621                 delete window[trans.cb];
11622             }catch(e){}
11623         }else{
11624             // if hasn't been loaded, wait for load to remove it to prevent script error
11625             window[trans.cb] = function(){
11626                 window[trans.cb] = undefined;
11627                 try{
11628                     delete window[trans.cb];
11629                 }catch(e){}
11630             };
11631         }
11632     },
11633
11634     // private
11635     handleResponse : function(o, trans){
11636         this.trans = false;
11637         this.destroyTrans(trans, true);
11638         var result;
11639         try {
11640             result = trans.reader.readRecords(o);
11641         }catch(e){
11642             this.fireEvent("loadexception", this, o, trans.arg, e);
11643             trans.callback.call(trans.scope||window, null, trans.arg, false);
11644             return;
11645         }
11646         this.fireEvent("load", this, o, trans.arg);
11647         trans.callback.call(trans.scope||window, result, trans.arg, true);
11648     },
11649
11650     // private
11651     handleFailure : function(trans){
11652         this.trans = false;
11653         this.destroyTrans(trans, false);
11654         this.fireEvent("loadexception", this, null, trans.arg);
11655         trans.callback.call(trans.scope||window, null, trans.arg, false);
11656     }
11657 });/*
11658  * Based on:
11659  * Ext JS Library 1.1.1
11660  * Copyright(c) 2006-2007, Ext JS, LLC.
11661  *
11662  * Originally Released Under LGPL - original licence link has changed is not relivant.
11663  *
11664  * Fork - LGPL
11665  * <script type="text/javascript">
11666  */
11667
11668 /**
11669  * @class Roo.data.JsonReader
11670  * @extends Roo.data.DataReader
11671  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11672  * based on mappings in a provided Roo.data.Record constructor.
11673  * 
11674  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11675  * in the reply previously. 
11676  * 
11677  * <p>
11678  * Example code:
11679  * <pre><code>
11680 var RecordDef = Roo.data.Record.create([
11681     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11682     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11683 ]);
11684 var myReader = new Roo.data.JsonReader({
11685     totalProperty: "results",    // The property which contains the total dataset size (optional)
11686     root: "rows",                // The property which contains an Array of row objects
11687     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11688 }, RecordDef);
11689 </code></pre>
11690  * <p>
11691  * This would consume a JSON file like this:
11692  * <pre><code>
11693 { 'results': 2, 'rows': [
11694     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11695     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11696 }
11697 </code></pre>
11698  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11699  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11700  * paged from the remote server.
11701  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11702  * @cfg {String} root name of the property which contains the Array of row objects.
11703  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11704  * @cfg {Array} fields Array of field definition objects
11705  * @constructor
11706  * Create a new JsonReader
11707  * @param {Object} meta Metadata configuration options
11708  * @param {Object} recordType Either an Array of field definition objects,
11709  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11710  */
11711 Roo.data.JsonReader = function(meta, recordType){
11712     
11713     meta = meta || {};
11714     // set some defaults:
11715     Roo.applyIf(meta, {
11716         totalProperty: 'total',
11717         successProperty : 'success',
11718         root : 'data',
11719         id : 'id'
11720     });
11721     
11722     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11723 };
11724 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11725     
11726     /**
11727      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11728      * Used by Store query builder to append _requestMeta to params.
11729      * 
11730      */
11731     metaFromRemote : false,
11732     /**
11733      * This method is only used by a DataProxy which has retrieved data from a remote server.
11734      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11735      * @return {Object} data A data block which is used by an Roo.data.Store object as
11736      * a cache of Roo.data.Records.
11737      */
11738     read : function(response){
11739         var json = response.responseText;
11740        
11741         var o = /* eval:var:o */ eval("("+json+")");
11742         if(!o) {
11743             throw {message: "JsonReader.read: Json object not found"};
11744         }
11745         
11746         if(o.metaData){
11747             
11748             delete this.ef;
11749             this.metaFromRemote = true;
11750             this.meta = o.metaData;
11751             this.recordType = Roo.data.Record.create(o.metaData.fields);
11752             this.onMetaChange(this.meta, this.recordType, o);
11753         }
11754         return this.readRecords(o);
11755     },
11756
11757     // private function a store will implement
11758     onMetaChange : function(meta, recordType, o){
11759
11760     },
11761
11762     /**
11763          * @ignore
11764          */
11765     simpleAccess: function(obj, subsc) {
11766         return obj[subsc];
11767     },
11768
11769         /**
11770          * @ignore
11771          */
11772     getJsonAccessor: function(){
11773         var re = /[\[\.]/;
11774         return function(expr) {
11775             try {
11776                 return(re.test(expr))
11777                     ? new Function("obj", "return obj." + expr)
11778                     : function(obj){
11779                         return obj[expr];
11780                     };
11781             } catch(e){}
11782             return Roo.emptyFn;
11783         };
11784     }(),
11785
11786     /**
11787      * Create a data block containing Roo.data.Records from an XML document.
11788      * @param {Object} o An object which contains an Array of row objects in the property specified
11789      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11790      * which contains the total size of the dataset.
11791      * @return {Object} data A data block which is used by an Roo.data.Store object as
11792      * a cache of Roo.data.Records.
11793      */
11794     readRecords : function(o){
11795         /**
11796          * After any data loads, the raw JSON data is available for further custom processing.
11797          * @type Object
11798          */
11799         this.o = o;
11800         var s = this.meta, Record = this.recordType,
11801             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11802
11803 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11804         if (!this.ef) {
11805             if(s.totalProperty) {
11806                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11807                 }
11808                 if(s.successProperty) {
11809                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11810                 }
11811                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11812                 if (s.id) {
11813                         var g = this.getJsonAccessor(s.id);
11814                         this.getId = function(rec) {
11815                                 var r = g(rec);  
11816                                 return (r === undefined || r === "") ? null : r;
11817                         };
11818                 } else {
11819                         this.getId = function(){return null;};
11820                 }
11821             this.ef = [];
11822             for(var jj = 0; jj < fl; jj++){
11823                 f = fi[jj];
11824                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11825                 this.ef[jj] = this.getJsonAccessor(map);
11826             }
11827         }
11828
11829         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11830         if(s.totalProperty){
11831             var vt = parseInt(this.getTotal(o), 10);
11832             if(!isNaN(vt)){
11833                 totalRecords = vt;
11834             }
11835         }
11836         if(s.successProperty){
11837             var vs = this.getSuccess(o);
11838             if(vs === false || vs === 'false'){
11839                 success = false;
11840             }
11841         }
11842         var records = [];
11843         for(var i = 0; i < c; i++){
11844                 var n = root[i];
11845             var values = {};
11846             var id = this.getId(n);
11847             for(var j = 0; j < fl; j++){
11848                 f = fi[j];
11849             var v = this.ef[j](n);
11850             if (!f.convert) {
11851                 Roo.log('missing convert for ' + f.name);
11852                 Roo.log(f);
11853                 continue;
11854             }
11855             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11856             }
11857             var record = new Record(values, id);
11858             record.json = n;
11859             records[i] = record;
11860         }
11861         return {
11862             raw : o,
11863             success : success,
11864             records : records,
11865             totalRecords : totalRecords
11866         };
11867     }
11868 });/*
11869  * Based on:
11870  * Ext JS Library 1.1.1
11871  * Copyright(c) 2006-2007, Ext JS, LLC.
11872  *
11873  * Originally Released Under LGPL - original licence link has changed is not relivant.
11874  *
11875  * Fork - LGPL
11876  * <script type="text/javascript">
11877  */
11878
11879 /**
11880  * @class Roo.data.ArrayReader
11881  * @extends Roo.data.DataReader
11882  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11883  * Each element of that Array represents a row of data fields. The
11884  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11885  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11886  * <p>
11887  * Example code:.
11888  * <pre><code>
11889 var RecordDef = Roo.data.Record.create([
11890     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11891     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11892 ]);
11893 var myReader = new Roo.data.ArrayReader({
11894     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11895 }, RecordDef);
11896 </code></pre>
11897  * <p>
11898  * This would consume an Array like this:
11899  * <pre><code>
11900 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11901   </code></pre>
11902  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11903  * @constructor
11904  * Create a new JsonReader
11905  * @param {Object} meta Metadata configuration options.
11906  * @param {Object} recordType Either an Array of field definition objects
11907  * as specified to {@link Roo.data.Record#create},
11908  * or an {@link Roo.data.Record} object
11909  * created using {@link Roo.data.Record#create}.
11910  */
11911 Roo.data.ArrayReader = function(meta, recordType){
11912     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11913 };
11914
11915 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11916     /**
11917      * Create a data block containing Roo.data.Records from an XML document.
11918      * @param {Object} o An Array of row objects which represents the dataset.
11919      * @return {Object} data A data block which is used by an Roo.data.Store object as
11920      * a cache of Roo.data.Records.
11921      */
11922     readRecords : function(o){
11923         var sid = this.meta ? this.meta.id : null;
11924         var recordType = this.recordType, fields = recordType.prototype.fields;
11925         var records = [];
11926         var root = o;
11927             for(var i = 0; i < root.length; i++){
11928                     var n = root[i];
11929                 var values = {};
11930                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11931                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11932                 var f = fields.items[j];
11933                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11934                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11935                 v = f.convert(v);
11936                 values[f.name] = v;
11937             }
11938                 var record = new recordType(values, id);
11939                 record.json = n;
11940                 records[records.length] = record;
11941             }
11942             return {
11943                 records : records,
11944                 totalRecords : records.length
11945             };
11946     }
11947 });/*
11948  * - LGPL
11949  * * 
11950  */
11951
11952 /**
11953  * @class Roo.bootstrap.ComboBox
11954  * @extends Roo.bootstrap.TriggerField
11955  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11956  * @cfg {Boolean} append (true|false) default false
11957  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11958  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11959  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11960  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11961  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11962  * @cfg {Boolean} animate default true
11963  * @cfg {Boolean} emptyResultText only for touch device
11964  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11965  * @constructor
11966  * Create a new ComboBox.
11967  * @param {Object} config Configuration options
11968  */
11969 Roo.bootstrap.ComboBox = function(config){
11970     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11971     this.addEvents({
11972         /**
11973          * @event expand
11974          * Fires when the dropdown list is expanded
11975              * @param {Roo.bootstrap.ComboBox} combo This combo box
11976              */
11977         'expand' : true,
11978         /**
11979          * @event collapse
11980          * Fires when the dropdown list is collapsed
11981              * @param {Roo.bootstrap.ComboBox} combo This combo box
11982              */
11983         'collapse' : true,
11984         /**
11985          * @event beforeselect
11986          * Fires before a list item is selected. Return false to cancel the selection.
11987              * @param {Roo.bootstrap.ComboBox} combo This combo box
11988              * @param {Roo.data.Record} record The data record returned from the underlying store
11989              * @param {Number} index The index of the selected item in the dropdown list
11990              */
11991         'beforeselect' : true,
11992         /**
11993          * @event select
11994          * Fires when a list item is selected
11995              * @param {Roo.bootstrap.ComboBox} combo This combo box
11996              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11997              * @param {Number} index The index of the selected item in the dropdown list
11998              */
11999         'select' : true,
12000         /**
12001          * @event beforequery
12002          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12003          * The event object passed has these properties:
12004              * @param {Roo.bootstrap.ComboBox} combo This combo box
12005              * @param {String} query The query
12006              * @param {Boolean} forceAll true to force "all" query
12007              * @param {Boolean} cancel true to cancel the query
12008              * @param {Object} e The query event object
12009              */
12010         'beforequery': true,
12011          /**
12012          * @event add
12013          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12014              * @param {Roo.bootstrap.ComboBox} combo This combo box
12015              */
12016         'add' : true,
12017         /**
12018          * @event edit
12019          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12020              * @param {Roo.bootstrap.ComboBox} combo This combo box
12021              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12022              */
12023         'edit' : true,
12024         /**
12025          * @event remove
12026          * Fires when the remove value from the combobox array
12027              * @param {Roo.bootstrap.ComboBox} combo This combo box
12028              */
12029         'remove' : true,
12030         /**
12031          * @event afterremove
12032          * Fires when the remove value from the combobox array
12033              * @param {Roo.bootstrap.ComboBox} combo This combo box
12034              */
12035         'afterremove' : true,
12036         /**
12037          * @event specialfilter
12038          * Fires when specialfilter
12039             * @param {Roo.bootstrap.ComboBox} combo This combo box
12040             */
12041         'specialfilter' : true,
12042         /**
12043          * @event tick
12044          * Fires when tick the element
12045             * @param {Roo.bootstrap.ComboBox} combo This combo box
12046             */
12047         'tick' : true,
12048         /**
12049          * @event touchviewdisplay
12050          * Fires when touch view require special display (default is using displayField)
12051             * @param {Roo.bootstrap.ComboBox} combo This combo box
12052             * @param {Object} cfg set html .
12053             */
12054         'touchviewdisplay' : true
12055         
12056     });
12057     
12058     this.item = [];
12059     this.tickItems = [];
12060     
12061     this.selectedIndex = -1;
12062     if(this.mode == 'local'){
12063         if(config.queryDelay === undefined){
12064             this.queryDelay = 10;
12065         }
12066         if(config.minChars === undefined){
12067             this.minChars = 0;
12068         }
12069     }
12070 };
12071
12072 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12073      
12074     /**
12075      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12076      * rendering into an Roo.Editor, defaults to false)
12077      */
12078     /**
12079      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12080      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12081      */
12082     /**
12083      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12084      */
12085     /**
12086      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12087      * the dropdown list (defaults to undefined, with no header element)
12088      */
12089
12090      /**
12091      * @cfg {String/Roo.Template} tpl The template to use to render the output
12092      */
12093      
12094      /**
12095      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12096      */
12097     listWidth: undefined,
12098     /**
12099      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12100      * mode = 'remote' or 'text' if mode = 'local')
12101      */
12102     displayField: undefined,
12103     
12104     /**
12105      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12106      * mode = 'remote' or 'value' if mode = 'local'). 
12107      * Note: use of a valueField requires the user make a selection
12108      * in order for a value to be mapped.
12109      */
12110     valueField: undefined,
12111     /**
12112      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12113      */
12114     modalTitle : '',
12115     
12116     /**
12117      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12118      * field's data value (defaults to the underlying DOM element's name)
12119      */
12120     hiddenName: undefined,
12121     /**
12122      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12123      */
12124     listClass: '',
12125     /**
12126      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12127      */
12128     selectedClass: 'active',
12129     
12130     /**
12131      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12132      */
12133     shadow:'sides',
12134     /**
12135      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12136      * anchor positions (defaults to 'tl-bl')
12137      */
12138     listAlign: 'tl-bl?',
12139     /**
12140      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12141      */
12142     maxHeight: 300,
12143     /**
12144      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12145      * query specified by the allQuery config option (defaults to 'query')
12146      */
12147     triggerAction: 'query',
12148     /**
12149      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12150      * (defaults to 4, does not apply if editable = false)
12151      */
12152     minChars : 4,
12153     /**
12154      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12155      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12156      */
12157     typeAhead: false,
12158     /**
12159      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12160      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12161      */
12162     queryDelay: 500,
12163     /**
12164      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12165      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12166      */
12167     pageSize: 0,
12168     /**
12169      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12170      * when editable = true (defaults to false)
12171      */
12172     selectOnFocus:false,
12173     /**
12174      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12175      */
12176     queryParam: 'query',
12177     /**
12178      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12179      * when mode = 'remote' (defaults to 'Loading...')
12180      */
12181     loadingText: 'Loading...',
12182     /**
12183      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12184      */
12185     resizable: false,
12186     /**
12187      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12188      */
12189     handleHeight : 8,
12190     /**
12191      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12192      * traditional select (defaults to true)
12193      */
12194     editable: true,
12195     /**
12196      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12197      */
12198     allQuery: '',
12199     /**
12200      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12201      */
12202     mode: 'remote',
12203     /**
12204      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12205      * listWidth has a higher value)
12206      */
12207     minListWidth : 70,
12208     /**
12209      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12210      * allow the user to set arbitrary text into the field (defaults to false)
12211      */
12212     forceSelection:false,
12213     /**
12214      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12215      * if typeAhead = true (defaults to 250)
12216      */
12217     typeAheadDelay : 250,
12218     /**
12219      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12220      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12221      */
12222     valueNotFoundText : undefined,
12223     /**
12224      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12225      */
12226     blockFocus : false,
12227     
12228     /**
12229      * @cfg {Boolean} disableClear Disable showing of clear button.
12230      */
12231     disableClear : false,
12232     /**
12233      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12234      */
12235     alwaysQuery : false,
12236     
12237     /**
12238      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12239      */
12240     multiple : false,
12241     
12242     /**
12243      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12244      */
12245     invalidClass : "has-warning",
12246     
12247     /**
12248      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12249      */
12250     validClass : "has-success",
12251     
12252     /**
12253      * @cfg {Boolean} specialFilter (true|false) special filter default false
12254      */
12255     specialFilter : false,
12256     
12257     /**
12258      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12259      */
12260     mobileTouchView : true,
12261     
12262     /**
12263      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12264      */
12265     useNativeIOS : false,
12266     
12267     ios_options : false,
12268     
12269     //private
12270     addicon : false,
12271     editicon: false,
12272     
12273     page: 0,
12274     hasQuery: false,
12275     append: false,
12276     loadNext: false,
12277     autoFocus : true,
12278     tickable : false,
12279     btnPosition : 'right',
12280     triggerList : true,
12281     showToggleBtn : true,
12282     animate : true,
12283     emptyResultText: 'Empty',
12284     triggerText : 'Select',
12285     
12286     // element that contains real text value.. (when hidden is used..)
12287     
12288     getAutoCreate : function()
12289     {
12290         var cfg = false;
12291         
12292         /*
12293          * Render classic select for iso
12294          */
12295         
12296         if(Roo.isIOS && this.useNativeIOS){
12297             cfg = this.getAutoCreateNativeIOS();
12298             return cfg;
12299         }
12300         
12301         /*
12302          * Touch Devices
12303          */
12304         
12305         if(Roo.isTouch && this.mobileTouchView){
12306             cfg = this.getAutoCreateTouchView();
12307             return cfg;;
12308         }
12309         
12310         /*
12311          *  Normal ComboBox
12312          */
12313         if(!this.tickable){
12314             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12315             return cfg;
12316         }
12317         
12318         /*
12319          *  ComboBox with tickable selections
12320          */
12321              
12322         var align = this.labelAlign || this.parentLabelAlign();
12323         
12324         cfg = {
12325             cls : 'form-group roo-combobox-tickable' //input-group
12326         };
12327         
12328         var buttons = {
12329             tag : 'div',
12330             cls : 'tickable-buttons',
12331             cn : [
12332                 {
12333                     tag : 'button',
12334                     type : 'button',
12335                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12336                     html : this.triggerText
12337                 },
12338                 {
12339                     tag : 'button',
12340                     type : 'button',
12341                     name : 'ok',
12342                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12343                     html : 'Done'
12344                 },
12345                 {
12346                     tag : 'button',
12347                     type : 'button',
12348                     name : 'cancel',
12349                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12350                     html : 'Cancel'
12351                 }
12352             ]
12353         };
12354         
12355         if(this.editable){
12356             buttons.cn.unshift({
12357                 tag: 'input',
12358                 cls: 'roo-select2-search-field-input'
12359             });
12360         }
12361         
12362         var _this = this;
12363         
12364         Roo.each(buttons.cn, function(c){
12365             if (_this.size) {
12366                 c.cls += ' btn-' + _this.size;
12367             }
12368
12369             if (_this.disabled) {
12370                 c.disabled = true;
12371             }
12372         });
12373         
12374         var box = {
12375             tag: 'div',
12376             cn: [
12377                 {
12378                     tag: 'input',
12379                     type : 'hidden',
12380                     cls: 'form-hidden-field'
12381                 },
12382                 {
12383                     tag: 'ul',
12384                     cls: 'roo-select2-choices',
12385                     cn:[
12386                         {
12387                             tag: 'li',
12388                             cls: 'roo-select2-search-field',
12389                             cn: [
12390
12391                                 buttons
12392                             ]
12393                         }
12394                     ]
12395                 }
12396             ]
12397         };
12398         
12399         var combobox = {
12400             cls: 'roo-select2-container input-group roo-select2-container-multi',
12401             cn: [
12402                 box
12403 //                {
12404 //                    tag: 'ul',
12405 //                    cls: 'typeahead typeahead-long dropdown-menu',
12406 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12407 //                }
12408             ]
12409         };
12410         
12411         if(this.hasFeedback && !this.allowBlank){
12412             
12413             var feedback = {
12414                 tag: 'span',
12415                 cls: 'glyphicon form-control-feedback'
12416             };
12417
12418             combobox.cn.push(feedback);
12419         }
12420         
12421         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12422             
12423 //                Roo.log("left and has label");
12424             cfg.cn = [
12425                 {
12426                     tag : 'i',
12427                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12428                     tooltip : 'This field is required'
12429                 },
12430                 {
12431                     tag: 'label',
12432                     'for' :  id,
12433                     cls : 'control-label col-sm-' + this.labelWidth,
12434                     html : this.fieldLabel
12435
12436                 },
12437                 {
12438                     cls : "col-sm-" + (12 - this.labelWidth), 
12439                     cn: [
12440                         combobox
12441                     ]
12442                 }
12443
12444             ];
12445
12446             if(this.indicatorpos == 'right'){
12447                 
12448                 cfg.cn = [
12449                     {
12450                         tag: 'label',
12451                         'for' :  id,
12452                         cls : 'control-label col-sm-' + this.labelWidth,
12453                         html : this.fieldLabel
12454
12455                     },
12456                     {
12457                         tag : 'i',
12458                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12459                         tooltip : 'This field is required'
12460                     },
12461                     {
12462                         cls : "col-sm-" + (12 - this.labelWidth), 
12463                         cn: [
12464                             combobox
12465                         ]
12466                     }
12467
12468                 ];
12469             
12470             }
12471                 
12472                 
12473         } else if ( this.fieldLabel.length) {
12474 //                Roo.log(" label");
12475                  cfg.cn = [
12476                     {
12477                         tag : 'i',
12478                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12479                         tooltip : 'This field is required'
12480                     },
12481                     {
12482                         tag: 'label',
12483                         //cls : 'input-group-addon',
12484                         html : this.fieldLabel
12485                         
12486                     },
12487                     
12488                     combobox
12489                     
12490                 ];
12491                 
12492                 if(this.indicatorpos == 'right'){
12493                     
12494                     cfg.cn = [
12495                         {
12496                             tag: 'label',
12497                             //cls : 'input-group-addon',
12498                             html : this.fieldLabel
12499
12500                         },
12501                         
12502                         {
12503                             tag : 'i',
12504                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12505                             tooltip : 'This field is required'
12506                         },
12507                         
12508                         combobox
12509
12510                     ];
12511                 
12512                 }
12513
12514         } else {
12515             
12516 //                Roo.log(" no label && no align");
12517                 cfg = combobox
12518                      
12519                 
12520         }
12521          
12522         var settings=this;
12523         ['xs','sm','md','lg'].map(function(size){
12524             if (settings[size]) {
12525                 cfg.cls += ' col-' + size + '-' + settings[size];
12526             }
12527         });
12528         
12529         return cfg;
12530         
12531     },
12532     
12533     _initEventsCalled : false,
12534     
12535     // private
12536     initEvents: function()
12537     {   
12538         if (this._initEventsCalled) { // as we call render... prevent looping...
12539             return;
12540         }
12541         this._initEventsCalled = true;
12542         
12543         if (!this.store) {
12544             throw "can not find store for combo";
12545         }
12546         
12547         this.store = Roo.factory(this.store, Roo.data);
12548         
12549         // if we are building from html. then this element is so complex, that we can not really
12550         // use the rendered HTML.
12551         // so we have to trash and replace the previous code.
12552         if (Roo.XComponent.build_from_html) {
12553             
12554             // remove this element....
12555             var e = this.el.dom, k=0;
12556             while (e ) { e = e.previousSibling;  ++k;}
12557
12558             this.el.remove();
12559             
12560             this.el=false;
12561             this.rendered = false;
12562             
12563             this.render(this.parent().getChildContainer(true), k);
12564             
12565             
12566             
12567         }
12568         
12569         if(Roo.isIOS && this.useNativeIOS){
12570             this.initIOSView();
12571             return;
12572         }
12573         
12574         /*
12575          * Touch Devices
12576          */
12577         
12578         if(Roo.isTouch && this.mobileTouchView){
12579             this.initTouchView();
12580             return;
12581         }
12582         
12583         if(this.tickable){
12584             this.initTickableEvents();
12585             return;
12586         }
12587         
12588         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12589         
12590         if(this.hiddenName){
12591             
12592             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12593             
12594             this.hiddenField.dom.value =
12595                 this.hiddenValue !== undefined ? this.hiddenValue :
12596                 this.value !== undefined ? this.value : '';
12597
12598             // prevent input submission
12599             this.el.dom.removeAttribute('name');
12600             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12601              
12602              
12603         }
12604         //if(Roo.isGecko){
12605         //    this.el.dom.setAttribute('autocomplete', 'off');
12606         //}
12607         
12608         var cls = 'x-combo-list';
12609         
12610         //this.list = new Roo.Layer({
12611         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12612         //});
12613         
12614         var _this = this;
12615         
12616         (function(){
12617             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12618             _this.list.setWidth(lw);
12619         }).defer(100);
12620         
12621         this.list.on('mouseover', this.onViewOver, this);
12622         this.list.on('mousemove', this.onViewMove, this);
12623         
12624         this.list.on('scroll', this.onViewScroll, this);
12625         
12626         /*
12627         this.list.swallowEvent('mousewheel');
12628         this.assetHeight = 0;
12629
12630         if(this.title){
12631             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12632             this.assetHeight += this.header.getHeight();
12633         }
12634
12635         this.innerList = this.list.createChild({cls:cls+'-inner'});
12636         this.innerList.on('mouseover', this.onViewOver, this);
12637         this.innerList.on('mousemove', this.onViewMove, this);
12638         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12639         
12640         if(this.allowBlank && !this.pageSize && !this.disableClear){
12641             this.footer = this.list.createChild({cls:cls+'-ft'});
12642             this.pageTb = new Roo.Toolbar(this.footer);
12643            
12644         }
12645         if(this.pageSize){
12646             this.footer = this.list.createChild({cls:cls+'-ft'});
12647             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12648                     {pageSize: this.pageSize});
12649             
12650         }
12651         
12652         if (this.pageTb && this.allowBlank && !this.disableClear) {
12653             var _this = this;
12654             this.pageTb.add(new Roo.Toolbar.Fill(), {
12655                 cls: 'x-btn-icon x-btn-clear',
12656                 text: '&#160;',
12657                 handler: function()
12658                 {
12659                     _this.collapse();
12660                     _this.clearValue();
12661                     _this.onSelect(false, -1);
12662                 }
12663             });
12664         }
12665         if (this.footer) {
12666             this.assetHeight += this.footer.getHeight();
12667         }
12668         */
12669             
12670         if(!this.tpl){
12671             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12672         }
12673
12674         this.view = new Roo.View(this.list, this.tpl, {
12675             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12676         });
12677         //this.view.wrapEl.setDisplayed(false);
12678         this.view.on('click', this.onViewClick, this);
12679         
12680         
12681         
12682         this.store.on('beforeload', this.onBeforeLoad, this);
12683         this.store.on('load', this.onLoad, this);
12684         this.store.on('loadexception', this.onLoadException, this);
12685         /*
12686         if(this.resizable){
12687             this.resizer = new Roo.Resizable(this.list,  {
12688                pinned:true, handles:'se'
12689             });
12690             this.resizer.on('resize', function(r, w, h){
12691                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12692                 this.listWidth = w;
12693                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12694                 this.restrictHeight();
12695             }, this);
12696             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12697         }
12698         */
12699         if(!this.editable){
12700             this.editable = true;
12701             this.setEditable(false);
12702         }
12703         
12704         /*
12705         
12706         if (typeof(this.events.add.listeners) != 'undefined') {
12707             
12708             this.addicon = this.wrap.createChild(
12709                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12710        
12711             this.addicon.on('click', function(e) {
12712                 this.fireEvent('add', this);
12713             }, this);
12714         }
12715         if (typeof(this.events.edit.listeners) != 'undefined') {
12716             
12717             this.editicon = this.wrap.createChild(
12718                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12719             if (this.addicon) {
12720                 this.editicon.setStyle('margin-left', '40px');
12721             }
12722             this.editicon.on('click', function(e) {
12723                 
12724                 // we fire even  if inothing is selected..
12725                 this.fireEvent('edit', this, this.lastData );
12726                 
12727             }, this);
12728         }
12729         */
12730         
12731         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12732             "up" : function(e){
12733                 this.inKeyMode = true;
12734                 this.selectPrev();
12735             },
12736
12737             "down" : function(e){
12738                 if(!this.isExpanded()){
12739                     this.onTriggerClick();
12740                 }else{
12741                     this.inKeyMode = true;
12742                     this.selectNext();
12743                 }
12744             },
12745
12746             "enter" : function(e){
12747 //                this.onViewClick();
12748                 //return true;
12749                 this.collapse();
12750                 
12751                 if(this.fireEvent("specialkey", this, e)){
12752                     this.onViewClick(false);
12753                 }
12754                 
12755                 return true;
12756             },
12757
12758             "esc" : function(e){
12759                 this.collapse();
12760             },
12761
12762             "tab" : function(e){
12763                 this.collapse();
12764                 
12765                 if(this.fireEvent("specialkey", this, e)){
12766                     this.onViewClick(false);
12767                 }
12768                 
12769                 return true;
12770             },
12771
12772             scope : this,
12773
12774             doRelay : function(foo, bar, hname){
12775                 if(hname == 'down' || this.scope.isExpanded()){
12776                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12777                 }
12778                 return true;
12779             },
12780
12781             forceKeyDown: true
12782         });
12783         
12784         
12785         this.queryDelay = Math.max(this.queryDelay || 10,
12786                 this.mode == 'local' ? 10 : 250);
12787         
12788         
12789         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12790         
12791         if(this.typeAhead){
12792             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12793         }
12794         if(this.editable !== false){
12795             this.inputEl().on("keyup", this.onKeyUp, this);
12796         }
12797         if(this.forceSelection){
12798             this.inputEl().on('blur', this.doForce, this);
12799         }
12800         
12801         if(this.multiple){
12802             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12803             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12804         }
12805     },
12806     
12807     initTickableEvents: function()
12808     {   
12809         this.createList();
12810         
12811         if(this.hiddenName){
12812             
12813             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12814             
12815             this.hiddenField.dom.value =
12816                 this.hiddenValue !== undefined ? this.hiddenValue :
12817                 this.value !== undefined ? this.value : '';
12818
12819             // prevent input submission
12820             this.el.dom.removeAttribute('name');
12821             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12822              
12823              
12824         }
12825         
12826 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12827         
12828         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12829         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12830         if(this.triggerList){
12831             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12832         }
12833          
12834         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12835         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12836         
12837         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12838         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12839         
12840         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12841         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12842         
12843         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12844         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12845         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12846         
12847         this.okBtn.hide();
12848         this.cancelBtn.hide();
12849         
12850         var _this = this;
12851         
12852         (function(){
12853             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12854             _this.list.setWidth(lw);
12855         }).defer(100);
12856         
12857         this.list.on('mouseover', this.onViewOver, this);
12858         this.list.on('mousemove', this.onViewMove, this);
12859         
12860         this.list.on('scroll', this.onViewScroll, this);
12861         
12862         if(!this.tpl){
12863             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>';
12864         }
12865
12866         this.view = new Roo.View(this.list, this.tpl, {
12867             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12868         });
12869         
12870         //this.view.wrapEl.setDisplayed(false);
12871         this.view.on('click', this.onViewClick, this);
12872         
12873         
12874         
12875         this.store.on('beforeload', this.onBeforeLoad, this);
12876         this.store.on('load', this.onLoad, this);
12877         this.store.on('loadexception', this.onLoadException, this);
12878         
12879         if(this.editable){
12880             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12881                 "up" : function(e){
12882                     this.inKeyMode = true;
12883                     this.selectPrev();
12884                 },
12885
12886                 "down" : function(e){
12887                     this.inKeyMode = true;
12888                     this.selectNext();
12889                 },
12890
12891                 "enter" : function(e){
12892                     if(this.fireEvent("specialkey", this, e)){
12893                         this.onViewClick(false);
12894                     }
12895                     
12896                     return true;
12897                 },
12898
12899                 "esc" : function(e){
12900                     this.onTickableFooterButtonClick(e, false, false);
12901                 },
12902
12903                 "tab" : function(e){
12904                     this.fireEvent("specialkey", this, e);
12905                     
12906                     this.onTickableFooterButtonClick(e, false, false);
12907                     
12908                     return true;
12909                 },
12910
12911                 scope : this,
12912
12913                 doRelay : function(e, fn, key){
12914                     if(this.scope.isExpanded()){
12915                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12916                     }
12917                     return true;
12918                 },
12919
12920                 forceKeyDown: true
12921             });
12922         }
12923         
12924         this.queryDelay = Math.max(this.queryDelay || 10,
12925                 this.mode == 'local' ? 10 : 250);
12926         
12927         
12928         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12929         
12930         if(this.typeAhead){
12931             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12932         }
12933         
12934         if(this.editable !== false){
12935             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12936         }
12937         
12938     },
12939
12940     onDestroy : function(){
12941         if(this.view){
12942             this.view.setStore(null);
12943             this.view.el.removeAllListeners();
12944             this.view.el.remove();
12945             this.view.purgeListeners();
12946         }
12947         if(this.list){
12948             this.list.dom.innerHTML  = '';
12949         }
12950         
12951         if(this.store){
12952             this.store.un('beforeload', this.onBeforeLoad, this);
12953             this.store.un('load', this.onLoad, this);
12954             this.store.un('loadexception', this.onLoadException, this);
12955         }
12956         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12957     },
12958
12959     // private
12960     fireKey : function(e){
12961         if(e.isNavKeyPress() && !this.list.isVisible()){
12962             this.fireEvent("specialkey", this, e);
12963         }
12964     },
12965
12966     // private
12967     onResize: function(w, h){
12968 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12969 //        
12970 //        if(typeof w != 'number'){
12971 //            // we do not handle it!?!?
12972 //            return;
12973 //        }
12974 //        var tw = this.trigger.getWidth();
12975 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12976 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12977 //        var x = w - tw;
12978 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12979 //            
12980 //        //this.trigger.setStyle('left', x+'px');
12981 //        
12982 //        if(this.list && this.listWidth === undefined){
12983 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12984 //            this.list.setWidth(lw);
12985 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12986 //        }
12987         
12988     
12989         
12990     },
12991
12992     /**
12993      * Allow or prevent the user from directly editing the field text.  If false is passed,
12994      * the user will only be able to select from the items defined in the dropdown list.  This method
12995      * is the runtime equivalent of setting the 'editable' config option at config time.
12996      * @param {Boolean} value True to allow the user to directly edit the field text
12997      */
12998     setEditable : function(value){
12999         if(value == this.editable){
13000             return;
13001         }
13002         this.editable = value;
13003         if(!value){
13004             this.inputEl().dom.setAttribute('readOnly', true);
13005             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13006             this.inputEl().addClass('x-combo-noedit');
13007         }else{
13008             this.inputEl().dom.setAttribute('readOnly', false);
13009             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13010             this.inputEl().removeClass('x-combo-noedit');
13011         }
13012     },
13013
13014     // private
13015     
13016     onBeforeLoad : function(combo,opts){
13017         if(!this.hasFocus){
13018             return;
13019         }
13020          if (!opts.add) {
13021             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13022          }
13023         this.restrictHeight();
13024         this.selectedIndex = -1;
13025     },
13026
13027     // private
13028     onLoad : function(){
13029         
13030         this.hasQuery = false;
13031         
13032         if(!this.hasFocus){
13033             return;
13034         }
13035         
13036         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13037             this.loading.hide();
13038         }
13039              
13040         if(this.store.getCount() > 0){
13041             this.expand();
13042             this.restrictHeight();
13043             if(this.lastQuery == this.allQuery){
13044                 if(this.editable && !this.tickable){
13045                     this.inputEl().dom.select();
13046                 }
13047                 
13048                 if(
13049                     !this.selectByValue(this.value, true) &&
13050                     this.autoFocus && 
13051                     (
13052                         !this.store.lastOptions ||
13053                         typeof(this.store.lastOptions.add) == 'undefined' || 
13054                         this.store.lastOptions.add != true
13055                     )
13056                 ){
13057                     this.select(0, true);
13058                 }
13059             }else{
13060                 if(this.autoFocus){
13061                     this.selectNext();
13062                 }
13063                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13064                     this.taTask.delay(this.typeAheadDelay);
13065                 }
13066             }
13067         }else{
13068             this.onEmptyResults();
13069         }
13070         
13071         //this.el.focus();
13072     },
13073     // private
13074     onLoadException : function()
13075     {
13076         this.hasQuery = false;
13077         
13078         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13079             this.loading.hide();
13080         }
13081         
13082         if(this.tickable && this.editable){
13083             return;
13084         }
13085         
13086         this.collapse();
13087         // only causes errors at present
13088         //Roo.log(this.store.reader.jsonData);
13089         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13090             // fixme
13091             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13092         //}
13093         
13094         
13095     },
13096     // private
13097     onTypeAhead : function(){
13098         if(this.store.getCount() > 0){
13099             var r = this.store.getAt(0);
13100             var newValue = r.data[this.displayField];
13101             var len = newValue.length;
13102             var selStart = this.getRawValue().length;
13103             
13104             if(selStart != len){
13105                 this.setRawValue(newValue);
13106                 this.selectText(selStart, newValue.length);
13107             }
13108         }
13109     },
13110
13111     // private
13112     onSelect : function(record, index){
13113         
13114         if(this.fireEvent('beforeselect', this, record, index) !== false){
13115         
13116             this.setFromData(index > -1 ? record.data : false);
13117             
13118             this.collapse();
13119             this.fireEvent('select', this, record, index);
13120         }
13121     },
13122
13123     /**
13124      * Returns the currently selected field value or empty string if no value is set.
13125      * @return {String} value The selected value
13126      */
13127     getValue : function()
13128     {
13129         if(Roo.isIOS && this.useNativeIOS){
13130             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13131         }
13132         
13133         if(this.multiple){
13134             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13135         }
13136         
13137         if(this.valueField){
13138             return typeof this.value != 'undefined' ? this.value : '';
13139         }else{
13140             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13141         }
13142     },
13143     
13144     getRawValue : function()
13145     {
13146         if(Roo.isIOS && this.useNativeIOS){
13147             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13148         }
13149         
13150         var v = this.inputEl().getValue();
13151         
13152         return v;
13153     },
13154
13155     /**
13156      * Clears any text/value currently set in the field
13157      */
13158     clearValue : function(){
13159         
13160         if(this.hiddenField){
13161             this.hiddenField.dom.value = '';
13162         }
13163         this.value = '';
13164         this.setRawValue('');
13165         this.lastSelectionText = '';
13166         this.lastData = false;
13167         
13168         var close = this.closeTriggerEl();
13169         
13170         if(close){
13171             close.hide();
13172         }
13173         
13174         this.validate();
13175         
13176     },
13177
13178     /**
13179      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13180      * will be displayed in the field.  If the value does not match the data value of an existing item,
13181      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13182      * Otherwise the field will be blank (although the value will still be set).
13183      * @param {String} value The value to match
13184      */
13185     setValue : function(v)
13186     {
13187         if(Roo.isIOS && this.useNativeIOS){
13188             this.setIOSValue(v);
13189             return;
13190         }
13191         
13192         if(this.multiple){
13193             this.syncValue();
13194             return;
13195         }
13196         
13197         var text = v;
13198         if(this.valueField){
13199             var r = this.findRecord(this.valueField, v);
13200             if(r){
13201                 text = r.data[this.displayField];
13202             }else if(this.valueNotFoundText !== undefined){
13203                 text = this.valueNotFoundText;
13204             }
13205         }
13206         this.lastSelectionText = text;
13207         if(this.hiddenField){
13208             this.hiddenField.dom.value = v;
13209         }
13210         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13211         this.value = v;
13212         
13213         var close = this.closeTriggerEl();
13214         
13215         if(close){
13216             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13217         }
13218         
13219         this.validate();
13220     },
13221     /**
13222      * @property {Object} the last set data for the element
13223      */
13224     
13225     lastData : false,
13226     /**
13227      * Sets the value of the field based on a object which is related to the record format for the store.
13228      * @param {Object} value the value to set as. or false on reset?
13229      */
13230     setFromData : function(o){
13231         
13232         if(this.multiple){
13233             this.addItem(o);
13234             return;
13235         }
13236             
13237         var dv = ''; // display value
13238         var vv = ''; // value value..
13239         this.lastData = o;
13240         if (this.displayField) {
13241             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13242         } else {
13243             // this is an error condition!!!
13244             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13245         }
13246         
13247         if(this.valueField){
13248             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13249         }
13250         
13251         var close = this.closeTriggerEl();
13252         
13253         if(close){
13254             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13255         }
13256         
13257         if(this.hiddenField){
13258             this.hiddenField.dom.value = vv;
13259             
13260             this.lastSelectionText = dv;
13261             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13262             this.value = vv;
13263             return;
13264         }
13265         // no hidden field.. - we store the value in 'value', but still display
13266         // display field!!!!
13267         this.lastSelectionText = dv;
13268         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13269         this.value = vv;
13270         
13271         
13272         
13273     },
13274     // private
13275     reset : function(){
13276         // overridden so that last data is reset..
13277         
13278         if(this.multiple){
13279             this.clearItem();
13280             return;
13281         }
13282         
13283         this.setValue(this.originalValue);
13284         //this.clearInvalid();
13285         this.lastData = false;
13286         if (this.view) {
13287             this.view.clearSelections();
13288         }
13289         
13290         this.validate();
13291     },
13292     // private
13293     findRecord : function(prop, value){
13294         var record;
13295         if(this.store.getCount() > 0){
13296             this.store.each(function(r){
13297                 if(r.data[prop] == value){
13298                     record = r;
13299                     return false;
13300                 }
13301                 return true;
13302             });
13303         }
13304         return record;
13305     },
13306     
13307     getName: function()
13308     {
13309         // returns hidden if it's set..
13310         if (!this.rendered) {return ''};
13311         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13312         
13313     },
13314     // private
13315     onViewMove : function(e, t){
13316         this.inKeyMode = false;
13317     },
13318
13319     // private
13320     onViewOver : function(e, t){
13321         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13322             return;
13323         }
13324         var item = this.view.findItemFromChild(t);
13325         
13326         if(item){
13327             var index = this.view.indexOf(item);
13328             this.select(index, false);
13329         }
13330     },
13331
13332     // private
13333     onViewClick : function(view, doFocus, el, e)
13334     {
13335         var index = this.view.getSelectedIndexes()[0];
13336         
13337         var r = this.store.getAt(index);
13338         
13339         if(this.tickable){
13340             
13341             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13342                 return;
13343             }
13344             
13345             var rm = false;
13346             var _this = this;
13347             
13348             Roo.each(this.tickItems, function(v,k){
13349                 
13350                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13351                     Roo.log(v);
13352                     _this.tickItems.splice(k, 1);
13353                     
13354                     if(typeof(e) == 'undefined' && view == false){
13355                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13356                     }
13357                     
13358                     rm = true;
13359                     return;
13360                 }
13361             });
13362             
13363             if(rm){
13364                 return;
13365             }
13366             
13367             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13368                 this.tickItems.push(r.data);
13369             }
13370             
13371             if(typeof(e) == 'undefined' && view == false){
13372                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13373             }
13374                     
13375             return;
13376         }
13377         
13378         if(r){
13379             this.onSelect(r, index);
13380         }
13381         if(doFocus !== false && !this.blockFocus){
13382             this.inputEl().focus();
13383         }
13384     },
13385
13386     // private
13387     restrictHeight : function(){
13388         //this.innerList.dom.style.height = '';
13389         //var inner = this.innerList.dom;
13390         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13391         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13392         //this.list.beginUpdate();
13393         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13394         this.list.alignTo(this.inputEl(), this.listAlign);
13395         this.list.alignTo(this.inputEl(), this.listAlign);
13396         //this.list.endUpdate();
13397     },
13398
13399     // private
13400     onEmptyResults : function(){
13401         
13402         if(this.tickable && this.editable){
13403             this.restrictHeight();
13404             return;
13405         }
13406         
13407         this.collapse();
13408     },
13409
13410     /**
13411      * Returns true if the dropdown list is expanded, else false.
13412      */
13413     isExpanded : function(){
13414         return this.list.isVisible();
13415     },
13416
13417     /**
13418      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13419      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13420      * @param {String} value The data value of the item to select
13421      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13422      * selected item if it is not currently in view (defaults to true)
13423      * @return {Boolean} True if the value matched an item in the list, else false
13424      */
13425     selectByValue : function(v, scrollIntoView){
13426         if(v !== undefined && v !== null){
13427             var r = this.findRecord(this.valueField || this.displayField, v);
13428             if(r){
13429                 this.select(this.store.indexOf(r), scrollIntoView);
13430                 return true;
13431             }
13432         }
13433         return false;
13434     },
13435
13436     /**
13437      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13438      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13439      * @param {Number} index The zero-based index of the list item to select
13440      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13441      * selected item if it is not currently in view (defaults to true)
13442      */
13443     select : function(index, scrollIntoView){
13444         this.selectedIndex = index;
13445         this.view.select(index);
13446         if(scrollIntoView !== false){
13447             var el = this.view.getNode(index);
13448             /*
13449              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13450              */
13451             if(el){
13452                 this.list.scrollChildIntoView(el, false);
13453             }
13454         }
13455     },
13456
13457     // private
13458     selectNext : function(){
13459         var ct = this.store.getCount();
13460         if(ct > 0){
13461             if(this.selectedIndex == -1){
13462                 this.select(0);
13463             }else if(this.selectedIndex < ct-1){
13464                 this.select(this.selectedIndex+1);
13465             }
13466         }
13467     },
13468
13469     // private
13470     selectPrev : function(){
13471         var ct = this.store.getCount();
13472         if(ct > 0){
13473             if(this.selectedIndex == -1){
13474                 this.select(0);
13475             }else if(this.selectedIndex != 0){
13476                 this.select(this.selectedIndex-1);
13477             }
13478         }
13479     },
13480
13481     // private
13482     onKeyUp : function(e){
13483         if(this.editable !== false && !e.isSpecialKey()){
13484             this.lastKey = e.getKey();
13485             this.dqTask.delay(this.queryDelay);
13486         }
13487     },
13488
13489     // private
13490     validateBlur : function(){
13491         return !this.list || !this.list.isVisible();   
13492     },
13493
13494     // private
13495     initQuery : function(){
13496         
13497         var v = this.getRawValue();
13498         
13499         if(this.tickable && this.editable){
13500             v = this.tickableInputEl().getValue();
13501         }
13502         
13503         this.doQuery(v);
13504     },
13505
13506     // private
13507     doForce : function(){
13508         if(this.inputEl().dom.value.length > 0){
13509             this.inputEl().dom.value =
13510                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13511              
13512         }
13513     },
13514
13515     /**
13516      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13517      * query allowing the query action to be canceled if needed.
13518      * @param {String} query The SQL query to execute
13519      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13520      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13521      * saved in the current store (defaults to false)
13522      */
13523     doQuery : function(q, forceAll){
13524         
13525         if(q === undefined || q === null){
13526             q = '';
13527         }
13528         var qe = {
13529             query: q,
13530             forceAll: forceAll,
13531             combo: this,
13532             cancel:false
13533         };
13534         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13535             return false;
13536         }
13537         q = qe.query;
13538         
13539         forceAll = qe.forceAll;
13540         if(forceAll === true || (q.length >= this.minChars)){
13541             
13542             this.hasQuery = true;
13543             
13544             if(this.lastQuery != q || this.alwaysQuery){
13545                 this.lastQuery = q;
13546                 if(this.mode == 'local'){
13547                     this.selectedIndex = -1;
13548                     if(forceAll){
13549                         this.store.clearFilter();
13550                     }else{
13551                         
13552                         if(this.specialFilter){
13553                             this.fireEvent('specialfilter', this);
13554                             this.onLoad();
13555                             return;
13556                         }
13557                         
13558                         this.store.filter(this.displayField, q);
13559                     }
13560                     
13561                     this.store.fireEvent("datachanged", this.store);
13562                     
13563                     this.onLoad();
13564                     
13565                     
13566                 }else{
13567                     
13568                     this.store.baseParams[this.queryParam] = q;
13569                     
13570                     var options = {params : this.getParams(q)};
13571                     
13572                     if(this.loadNext){
13573                         options.add = true;
13574                         options.params.start = this.page * this.pageSize;
13575                     }
13576                     
13577                     this.store.load(options);
13578                     
13579                     /*
13580                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13581                      *  we should expand the list on onLoad
13582                      *  so command out it
13583                      */
13584 //                    this.expand();
13585                 }
13586             }else{
13587                 this.selectedIndex = -1;
13588                 this.onLoad();   
13589             }
13590         }
13591         
13592         this.loadNext = false;
13593     },
13594     
13595     // private
13596     getParams : function(q){
13597         var p = {};
13598         //p[this.queryParam] = q;
13599         
13600         if(this.pageSize){
13601             p.start = 0;
13602             p.limit = this.pageSize;
13603         }
13604         return p;
13605     },
13606
13607     /**
13608      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13609      */
13610     collapse : function(){
13611         if(!this.isExpanded()){
13612             return;
13613         }
13614         
13615         this.list.hide();
13616         
13617         if(this.tickable){
13618             this.hasFocus = false;
13619             this.okBtn.hide();
13620             this.cancelBtn.hide();
13621             this.trigger.show();
13622             
13623             if(this.editable){
13624                 this.tickableInputEl().dom.value = '';
13625                 this.tickableInputEl().blur();
13626             }
13627             
13628         }
13629         
13630         Roo.get(document).un('mousedown', this.collapseIf, this);
13631         Roo.get(document).un('mousewheel', this.collapseIf, this);
13632         if (!this.editable) {
13633             Roo.get(document).un('keydown', this.listKeyPress, this);
13634         }
13635         this.fireEvent('collapse', this);
13636         
13637         this.validate();
13638     },
13639
13640     // private
13641     collapseIf : function(e){
13642         var in_combo  = e.within(this.el);
13643         var in_list =  e.within(this.list);
13644         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13645         
13646         if (in_combo || in_list || is_list) {
13647             //e.stopPropagation();
13648             return;
13649         }
13650         
13651         if(this.tickable){
13652             this.onTickableFooterButtonClick(e, false, false);
13653         }
13654
13655         this.collapse();
13656         
13657     },
13658
13659     /**
13660      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13661      */
13662     expand : function(){
13663        
13664         if(this.isExpanded() || !this.hasFocus){
13665             return;
13666         }
13667         
13668         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13669         this.list.setWidth(lw);
13670         
13671         
13672          Roo.log('expand');
13673         
13674         this.list.show();
13675         
13676         this.restrictHeight();
13677         
13678         if(this.tickable){
13679             
13680             this.tickItems = Roo.apply([], this.item);
13681             
13682             this.okBtn.show();
13683             this.cancelBtn.show();
13684             this.trigger.hide();
13685             
13686             if(this.editable){
13687                 this.tickableInputEl().focus();
13688             }
13689             
13690         }
13691         
13692         Roo.get(document).on('mousedown', this.collapseIf, this);
13693         Roo.get(document).on('mousewheel', this.collapseIf, this);
13694         if (!this.editable) {
13695             Roo.get(document).on('keydown', this.listKeyPress, this);
13696         }
13697         
13698         this.fireEvent('expand', this);
13699     },
13700
13701     // private
13702     // Implements the default empty TriggerField.onTriggerClick function
13703     onTriggerClick : function(e)
13704     {
13705         Roo.log('trigger click');
13706         
13707         if(this.disabled || !this.triggerList){
13708             return;
13709         }
13710         
13711         this.page = 0;
13712         this.loadNext = false;
13713         
13714         if(this.isExpanded()){
13715             this.collapse();
13716             if (!this.blockFocus) {
13717                 this.inputEl().focus();
13718             }
13719             
13720         }else {
13721             this.hasFocus = true;
13722             if(this.triggerAction == 'all') {
13723                 this.doQuery(this.allQuery, true);
13724             } else {
13725                 this.doQuery(this.getRawValue());
13726             }
13727             if (!this.blockFocus) {
13728                 this.inputEl().focus();
13729             }
13730         }
13731     },
13732     
13733     onTickableTriggerClick : function(e)
13734     {
13735         if(this.disabled){
13736             return;
13737         }
13738         
13739         this.page = 0;
13740         this.loadNext = false;
13741         this.hasFocus = true;
13742         
13743         if(this.triggerAction == 'all') {
13744             this.doQuery(this.allQuery, true);
13745         } else {
13746             this.doQuery(this.getRawValue());
13747         }
13748     },
13749     
13750     onSearchFieldClick : function(e)
13751     {
13752         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13753             this.onTickableFooterButtonClick(e, false, false);
13754             return;
13755         }
13756         
13757         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13758             return;
13759         }
13760         
13761         this.page = 0;
13762         this.loadNext = false;
13763         this.hasFocus = true;
13764         
13765         if(this.triggerAction == 'all') {
13766             this.doQuery(this.allQuery, true);
13767         } else {
13768             this.doQuery(this.getRawValue());
13769         }
13770     },
13771     
13772     listKeyPress : function(e)
13773     {
13774         //Roo.log('listkeypress');
13775         // scroll to first matching element based on key pres..
13776         if (e.isSpecialKey()) {
13777             return false;
13778         }
13779         var k = String.fromCharCode(e.getKey()).toUpperCase();
13780         //Roo.log(k);
13781         var match  = false;
13782         var csel = this.view.getSelectedNodes();
13783         var cselitem = false;
13784         if (csel.length) {
13785             var ix = this.view.indexOf(csel[0]);
13786             cselitem  = this.store.getAt(ix);
13787             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13788                 cselitem = false;
13789             }
13790             
13791         }
13792         
13793         this.store.each(function(v) { 
13794             if (cselitem) {
13795                 // start at existing selection.
13796                 if (cselitem.id == v.id) {
13797                     cselitem = false;
13798                 }
13799                 return true;
13800             }
13801                 
13802             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13803                 match = this.store.indexOf(v);
13804                 return false;
13805             }
13806             return true;
13807         }, this);
13808         
13809         if (match === false) {
13810             return true; // no more action?
13811         }
13812         // scroll to?
13813         this.view.select(match);
13814         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13815         sn.scrollIntoView(sn.dom.parentNode, false);
13816     },
13817     
13818     onViewScroll : function(e, t){
13819         
13820         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){
13821             return;
13822         }
13823         
13824         this.hasQuery = true;
13825         
13826         this.loading = this.list.select('.loading', true).first();
13827         
13828         if(this.loading === null){
13829             this.list.createChild({
13830                 tag: 'div',
13831                 cls: 'loading roo-select2-more-results roo-select2-active',
13832                 html: 'Loading more results...'
13833             });
13834             
13835             this.loading = this.list.select('.loading', true).first();
13836             
13837             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13838             
13839             this.loading.hide();
13840         }
13841         
13842         this.loading.show();
13843         
13844         var _combo = this;
13845         
13846         this.page++;
13847         this.loadNext = true;
13848         
13849         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13850         
13851         return;
13852     },
13853     
13854     addItem : function(o)
13855     {   
13856         var dv = ''; // display value
13857         
13858         if (this.displayField) {
13859             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13860         } else {
13861             // this is an error condition!!!
13862             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13863         }
13864         
13865         if(!dv.length){
13866             return;
13867         }
13868         
13869         var choice = this.choices.createChild({
13870             tag: 'li',
13871             cls: 'roo-select2-search-choice',
13872             cn: [
13873                 {
13874                     tag: 'div',
13875                     html: dv
13876                 },
13877                 {
13878                     tag: 'a',
13879                     href: '#',
13880                     cls: 'roo-select2-search-choice-close',
13881                     tabindex: '-1'
13882                 }
13883             ]
13884             
13885         }, this.searchField);
13886         
13887         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13888         
13889         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13890         
13891         this.item.push(o);
13892         
13893         this.lastData = o;
13894         
13895         this.syncValue();
13896         
13897         this.inputEl().dom.value = '';
13898         
13899         this.validate();
13900     },
13901     
13902     onRemoveItem : function(e, _self, o)
13903     {
13904         e.preventDefault();
13905         
13906         this.lastItem = Roo.apply([], this.item);
13907         
13908         var index = this.item.indexOf(o.data) * 1;
13909         
13910         if( index < 0){
13911             Roo.log('not this item?!');
13912             return;
13913         }
13914         
13915         this.item.splice(index, 1);
13916         o.item.remove();
13917         
13918         this.syncValue();
13919         
13920         this.fireEvent('remove', this, e);
13921         
13922         this.validate();
13923         
13924     },
13925     
13926     syncValue : function()
13927     {
13928         if(!this.item.length){
13929             this.clearValue();
13930             return;
13931         }
13932             
13933         var value = [];
13934         var _this = this;
13935         Roo.each(this.item, function(i){
13936             if(_this.valueField){
13937                 value.push(i[_this.valueField]);
13938                 return;
13939             }
13940
13941             value.push(i);
13942         });
13943
13944         this.value = value.join(',');
13945
13946         if(this.hiddenField){
13947             this.hiddenField.dom.value = this.value;
13948         }
13949         
13950         this.store.fireEvent("datachanged", this.store);
13951         
13952         this.validate();
13953     },
13954     
13955     clearItem : function()
13956     {
13957         if(!this.multiple){
13958             return;
13959         }
13960         
13961         this.item = [];
13962         
13963         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13964            c.remove();
13965         });
13966         
13967         this.syncValue();
13968         
13969         this.validate();
13970         
13971         if(this.tickable && !Roo.isTouch){
13972             this.view.refresh();
13973         }
13974     },
13975     
13976     inputEl: function ()
13977     {
13978         if(Roo.isIOS && this.useNativeIOS){
13979             return this.el.select('select.roo-ios-select', true).first();
13980         }
13981         
13982         if(Roo.isTouch && this.mobileTouchView){
13983             return this.el.select('input.form-control',true).first();
13984         }
13985         
13986         if(this.tickable){
13987             return this.searchField;
13988         }
13989         
13990         return this.el.select('input.form-control',true).first();
13991     },
13992     
13993     onTickableFooterButtonClick : function(e, btn, el)
13994     {
13995         e.preventDefault();
13996         
13997         this.lastItem = Roo.apply([], this.item);
13998         
13999         if(btn && btn.name == 'cancel'){
14000             this.tickItems = Roo.apply([], this.item);
14001             this.collapse();
14002             return;
14003         }
14004         
14005         this.clearItem();
14006         
14007         var _this = this;
14008         
14009         Roo.each(this.tickItems, function(o){
14010             _this.addItem(o);
14011         });
14012         
14013         this.collapse();
14014         
14015     },
14016     
14017     validate : function()
14018     {
14019         var v = this.getRawValue();
14020         
14021         if(this.multiple){
14022             v = this.getValue();
14023         }
14024         
14025         if(this.disabled || this.allowBlank || v.length){
14026             this.markValid();
14027             return true;
14028         }
14029         
14030         this.markInvalid();
14031         return false;
14032     },
14033     
14034     tickableInputEl : function()
14035     {
14036         if(!this.tickable || !this.editable){
14037             return this.inputEl();
14038         }
14039         
14040         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14041     },
14042     
14043     
14044     getAutoCreateTouchView : function()
14045     {
14046         var id = Roo.id();
14047         
14048         var cfg = {
14049             cls: 'form-group' //input-group
14050         };
14051         
14052         var input =  {
14053             tag: 'input',
14054             id : id,
14055             type : this.inputType,
14056             cls : 'form-control x-combo-noedit',
14057             autocomplete: 'new-password',
14058             placeholder : this.placeholder || '',
14059             readonly : true
14060         };
14061         
14062         if (this.name) {
14063             input.name = this.name;
14064         }
14065         
14066         if (this.size) {
14067             input.cls += ' input-' + this.size;
14068         }
14069         
14070         if (this.disabled) {
14071             input.disabled = true;
14072         }
14073         
14074         var inputblock = {
14075             cls : '',
14076             cn : [
14077                 input
14078             ]
14079         };
14080         
14081         if(this.before){
14082             inputblock.cls += ' input-group';
14083             
14084             inputblock.cn.unshift({
14085                 tag :'span',
14086                 cls : 'input-group-addon',
14087                 html : this.before
14088             });
14089         }
14090         
14091         if(this.removable && !this.multiple){
14092             inputblock.cls += ' roo-removable';
14093             
14094             inputblock.cn.push({
14095                 tag: 'button',
14096                 html : 'x',
14097                 cls : 'roo-combo-removable-btn close'
14098             });
14099         }
14100
14101         if(this.hasFeedback && !this.allowBlank){
14102             
14103             inputblock.cls += ' has-feedback';
14104             
14105             inputblock.cn.push({
14106                 tag: 'span',
14107                 cls: 'glyphicon form-control-feedback'
14108             });
14109             
14110         }
14111         
14112         if (this.after) {
14113             
14114             inputblock.cls += (this.before) ? '' : ' input-group';
14115             
14116             inputblock.cn.push({
14117                 tag :'span',
14118                 cls : 'input-group-addon',
14119                 html : this.after
14120             });
14121         }
14122
14123         var box = {
14124             tag: 'div',
14125             cn: [
14126                 {
14127                     tag: 'input',
14128                     type : 'hidden',
14129                     cls: 'form-hidden-field'
14130                 },
14131                 inputblock
14132             ]
14133             
14134         };
14135         
14136         if(this.multiple){
14137             box = {
14138                 tag: 'div',
14139                 cn: [
14140                     {
14141                         tag: 'input',
14142                         type : 'hidden',
14143                         cls: 'form-hidden-field'
14144                     },
14145                     {
14146                         tag: 'ul',
14147                         cls: 'roo-select2-choices',
14148                         cn:[
14149                             {
14150                                 tag: 'li',
14151                                 cls: 'roo-select2-search-field',
14152                                 cn: [
14153
14154                                     inputblock
14155                                 ]
14156                             }
14157                         ]
14158                     }
14159                 ]
14160             }
14161         };
14162         
14163         var combobox = {
14164             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14165             cn: [
14166                 box
14167             ]
14168         };
14169         
14170         if(!this.multiple && this.showToggleBtn){
14171             
14172             var caret = {
14173                         tag: 'span',
14174                         cls: 'caret'
14175             };
14176             
14177             if (this.caret != false) {
14178                 caret = {
14179                      tag: 'i',
14180                      cls: 'fa fa-' + this.caret
14181                 };
14182                 
14183             }
14184             
14185             combobox.cn.push({
14186                 tag :'span',
14187                 cls : 'input-group-addon btn dropdown-toggle',
14188                 cn : [
14189                     caret,
14190                     {
14191                         tag: 'span',
14192                         cls: 'combobox-clear',
14193                         cn  : [
14194                             {
14195                                 tag : 'i',
14196                                 cls: 'icon-remove'
14197                             }
14198                         ]
14199                     }
14200                 ]
14201
14202             })
14203         }
14204         
14205         if(this.multiple){
14206             combobox.cls += ' roo-select2-container-multi';
14207         }
14208         
14209         var align = this.labelAlign || this.parentLabelAlign();
14210         
14211         cfg.cn = combobox;
14212         
14213         if(this.fieldLabel.length && this.labelWidth){
14214             
14215             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14216             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14217             
14218             cfg.cn = [
14219                 {
14220                    tag : 'i',
14221                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14222                    tooltip : 'This field is required'
14223                 },
14224                 {
14225                     tag: 'label',
14226                     cls : 'control-label ' + lw,
14227                     html : this.fieldLabel
14228
14229                 },
14230                 {
14231                     cls : cw, 
14232                     cn: [
14233                         combobox
14234                     ]
14235                 }
14236             ];
14237             
14238             if(this.indicatorpos == 'right'){
14239                 cfg.cn = [
14240                     {
14241                         tag: 'label',
14242                         cls : 'control-label ' + lw,
14243                         html : this.fieldLabel
14244
14245                     },
14246                     {
14247                        tag : 'i',
14248                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14249                        tooltip : 'This field is required'
14250                     },
14251                     {
14252                         cls : cw, 
14253                         cn: [
14254                             combobox
14255                         ]
14256                     }
14257                 ];
14258             }
14259         }
14260         
14261         var settings = this;
14262         
14263         ['xs','sm','md','lg'].map(function(size){
14264             if (settings[size]) {
14265                 cfg.cls += ' col-' + size + '-' + settings[size];
14266             }
14267         });
14268         
14269         return cfg;
14270     },
14271     
14272     initTouchView : function()
14273     {
14274         this.renderTouchView();
14275         
14276         this.touchViewEl.on('scroll', function(){
14277             this.el.dom.scrollTop = 0;
14278         }, this);
14279         
14280         this.originalValue = this.getValue();
14281         
14282         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14283         
14284         this.inputEl().on("click", this.showTouchView, this);
14285         if (this.triggerEl) {
14286             this.triggerEl.on("click", this.showTouchView, this);
14287         }
14288         
14289         
14290         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14291         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14292         
14293         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14294         
14295         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14296         this.store.on('load', this.onTouchViewLoad, this);
14297         this.store.on('loadexception', this.onTouchViewLoadException, this);
14298         
14299         if(this.hiddenName){
14300             
14301             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14302             
14303             this.hiddenField.dom.value =
14304                 this.hiddenValue !== undefined ? this.hiddenValue :
14305                 this.value !== undefined ? this.value : '';
14306         
14307             this.el.dom.removeAttribute('name');
14308             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14309         }
14310         
14311         if(this.multiple){
14312             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14313             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14314         }
14315         
14316         if(this.removable && !this.multiple){
14317             var close = this.closeTriggerEl();
14318             if(close){
14319                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14320                 close.on('click', this.removeBtnClick, this, close);
14321             }
14322         }
14323         /*
14324          * fix the bug in Safari iOS8
14325          */
14326         this.inputEl().on("focus", function(e){
14327             document.activeElement.blur();
14328         }, this);
14329         
14330         return;
14331         
14332         
14333     },
14334     
14335     renderTouchView : function()
14336     {
14337         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14338         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14339         
14340         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14341         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14342         
14343         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14344         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14345         this.touchViewBodyEl.setStyle('overflow', 'auto');
14346         
14347         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14348         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14349         
14350         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14351         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14352         
14353     },
14354     
14355     showTouchView : function()
14356     {
14357         if(this.disabled){
14358             return;
14359         }
14360         
14361         this.touchViewHeaderEl.hide();
14362
14363         if(this.modalTitle.length){
14364             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14365             this.touchViewHeaderEl.show();
14366         }
14367
14368         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14369         this.touchViewEl.show();
14370
14371         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14372         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14373                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14374
14375         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14376
14377         if(this.modalTitle.length){
14378             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14379         }
14380         
14381         this.touchViewBodyEl.setHeight(bodyHeight);
14382
14383         if(this.animate){
14384             var _this = this;
14385             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14386         }else{
14387             this.touchViewEl.addClass('in');
14388         }
14389
14390         this.doTouchViewQuery();
14391         
14392     },
14393     
14394     hideTouchView : function()
14395     {
14396         this.touchViewEl.removeClass('in');
14397
14398         if(this.animate){
14399             var _this = this;
14400             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14401         }else{
14402             this.touchViewEl.setStyle('display', 'none');
14403         }
14404         
14405     },
14406     
14407     setTouchViewValue : function()
14408     {
14409         if(this.multiple){
14410             this.clearItem();
14411         
14412             var _this = this;
14413
14414             Roo.each(this.tickItems, function(o){
14415                 this.addItem(o);
14416             }, this);
14417         }
14418         
14419         this.hideTouchView();
14420     },
14421     
14422     doTouchViewQuery : function()
14423     {
14424         var qe = {
14425             query: '',
14426             forceAll: true,
14427             combo: this,
14428             cancel:false
14429         };
14430         
14431         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14432             return false;
14433         }
14434         
14435         if(!this.alwaysQuery || this.mode == 'local'){
14436             this.onTouchViewLoad();
14437             return;
14438         }
14439         
14440         this.store.load();
14441     },
14442     
14443     onTouchViewBeforeLoad : function(combo,opts)
14444     {
14445         return;
14446     },
14447
14448     // private
14449     onTouchViewLoad : function()
14450     {
14451         if(this.store.getCount() < 1){
14452             this.onTouchViewEmptyResults();
14453             return;
14454         }
14455         
14456         this.clearTouchView();
14457         
14458         var rawValue = this.getRawValue();
14459         
14460         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14461         
14462         this.tickItems = [];
14463         
14464         this.store.data.each(function(d, rowIndex){
14465             var row = this.touchViewListGroup.createChild(template);
14466             
14467             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14468                 row.addClass(d.data.cls);
14469             }
14470             
14471             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14472                 var cfg = {
14473                     data : d.data,
14474                     html : d.data[this.displayField]
14475                 };
14476                 
14477                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14478                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14479                 }
14480             }
14481             row.removeClass('selected');
14482             if(!this.multiple && this.valueField &&
14483                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14484             {
14485                 // radio buttons..
14486                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14487                 row.addClass('selected');
14488             }
14489             
14490             if(this.multiple && this.valueField &&
14491                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14492             {
14493                 
14494                 // checkboxes...
14495                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14496                 this.tickItems.push(d.data);
14497             }
14498             
14499             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14500             
14501         }, this);
14502         
14503         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14504         
14505         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14506
14507         if(this.modalTitle.length){
14508             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14509         }
14510
14511         var listHeight = this.touchViewListGroup.getHeight();
14512         
14513         var _this = this;
14514         
14515         if(firstChecked && listHeight > bodyHeight){
14516             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14517         }
14518         
14519     },
14520     
14521     onTouchViewLoadException : function()
14522     {
14523         this.hideTouchView();
14524     },
14525     
14526     onTouchViewEmptyResults : function()
14527     {
14528         this.clearTouchView();
14529         
14530         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14531         
14532         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14533         
14534     },
14535     
14536     clearTouchView : function()
14537     {
14538         this.touchViewListGroup.dom.innerHTML = '';
14539     },
14540     
14541     onTouchViewClick : function(e, el, o)
14542     {
14543         e.preventDefault();
14544         
14545         var row = o.row;
14546         var rowIndex = o.rowIndex;
14547         
14548         var r = this.store.getAt(rowIndex);
14549         
14550         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14551             
14552             if(!this.multiple){
14553                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14554                     c.dom.removeAttribute('checked');
14555                 }, this);
14556
14557                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14558
14559                 this.setFromData(r.data);
14560
14561                 var close = this.closeTriggerEl();
14562
14563                 if(close){
14564                     close.show();
14565                 }
14566
14567                 this.hideTouchView();
14568
14569                 this.fireEvent('select', this, r, rowIndex);
14570
14571                 return;
14572             }
14573
14574             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14575                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14576                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14577                 return;
14578             }
14579
14580             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14581             this.addItem(r.data);
14582             this.tickItems.push(r.data);
14583         }
14584     },
14585     
14586     getAutoCreateNativeIOS : function()
14587     {
14588         var cfg = {
14589             cls: 'form-group' //input-group,
14590         };
14591         
14592         var combobox =  {
14593             tag: 'select',
14594             cls : 'roo-ios-select'
14595         };
14596         
14597         if (this.name) {
14598             combobox.name = this.name;
14599         }
14600         
14601         if (this.disabled) {
14602             combobox.disabled = true;
14603         }
14604         
14605         var settings = this;
14606         
14607         ['xs','sm','md','lg'].map(function(size){
14608             if (settings[size]) {
14609                 cfg.cls += ' col-' + size + '-' + settings[size];
14610             }
14611         });
14612         
14613         cfg.cn = combobox;
14614         
14615         return cfg;
14616         
14617     },
14618     
14619     initIOSView : function()
14620     {
14621         this.store.on('load', this.onIOSViewLoad, this);
14622         
14623         return;
14624     },
14625     
14626     onIOSViewLoad : function()
14627     {
14628         if(this.store.getCount() < 1){
14629             return;
14630         }
14631         
14632         this.clearIOSView();
14633         
14634         if(this.allowBlank) {
14635             
14636             var default_text = '-- SELECT --';
14637             
14638             var opt = this.inputEl().createChild({
14639                 tag: 'option',
14640                 value : 0,
14641                 html : default_text
14642             });
14643             
14644             var o = {};
14645             o[this.valueField] = 0;
14646             o[this.displayField] = default_text;
14647             
14648             this.ios_options.push({
14649                 data : o,
14650                 el : opt
14651             });
14652             
14653         }
14654         
14655         this.store.data.each(function(d, rowIndex){
14656             
14657             var html = '';
14658             
14659             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14660                 html = d.data[this.displayField];
14661             }
14662             
14663             var value = '';
14664             
14665             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14666                 value = d.data[this.valueField];
14667             }
14668             
14669             var option = {
14670                 tag: 'option',
14671                 value : value,
14672                 html : html
14673             };
14674             
14675             if(this.value == d.data[this.valueField]){
14676                 option['selected'] = true;
14677             }
14678             
14679             var opt = this.inputEl().createChild(option);
14680             
14681             this.ios_options.push({
14682                 data : d.data,
14683                 el : opt
14684             });
14685             
14686         }, this);
14687         
14688         this.inputEl().on('change', function(){
14689            this.fireEvent('select', this);
14690         }, this);
14691         
14692     },
14693     
14694     clearIOSView: function()
14695     {
14696         this.inputEl().dom.innerHTML = '';
14697         
14698         this.ios_options = [];
14699     },
14700     
14701     setIOSValue: function(v)
14702     {
14703         this.value = v;
14704         
14705         if(!this.ios_options){
14706             return;
14707         }
14708         
14709         Roo.each(this.ios_options, function(opts){
14710            
14711            opts.el.dom.removeAttribute('selected');
14712            
14713            if(opts.data[this.valueField] != v){
14714                return;
14715            }
14716            
14717            opts.el.dom.setAttribute('selected', true);
14718            
14719         }, this);
14720     }
14721
14722     /** 
14723     * @cfg {Boolean} grow 
14724     * @hide 
14725     */
14726     /** 
14727     * @cfg {Number} growMin 
14728     * @hide 
14729     */
14730     /** 
14731     * @cfg {Number} growMax 
14732     * @hide 
14733     */
14734     /**
14735      * @hide
14736      * @method autoSize
14737      */
14738 });
14739
14740 Roo.apply(Roo.bootstrap.ComboBox,  {
14741     
14742     header : {
14743         tag: 'div',
14744         cls: 'modal-header',
14745         cn: [
14746             {
14747                 tag: 'h4',
14748                 cls: 'modal-title'
14749             }
14750         ]
14751     },
14752     
14753     body : {
14754         tag: 'div',
14755         cls: 'modal-body',
14756         cn: [
14757             {
14758                 tag: 'ul',
14759                 cls: 'list-group'
14760             }
14761         ]
14762     },
14763     
14764     listItemRadio : {
14765         tag: 'li',
14766         cls: 'list-group-item',
14767         cn: [
14768             {
14769                 tag: 'span',
14770                 cls: 'roo-combobox-list-group-item-value'
14771             },
14772             {
14773                 tag: 'div',
14774                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14775                 cn: [
14776                     {
14777                         tag: 'input',
14778                         type: 'radio'
14779                     },
14780                     {
14781                         tag: 'label'
14782                     }
14783                 ]
14784             }
14785         ]
14786     },
14787     
14788     listItemCheckbox : {
14789         tag: 'li',
14790         cls: 'list-group-item',
14791         cn: [
14792             {
14793                 tag: 'span',
14794                 cls: 'roo-combobox-list-group-item-value'
14795             },
14796             {
14797                 tag: 'div',
14798                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14799                 cn: [
14800                     {
14801                         tag: 'input',
14802                         type: 'checkbox'
14803                     },
14804                     {
14805                         tag: 'label'
14806                     }
14807                 ]
14808             }
14809         ]
14810     },
14811     
14812     emptyResult : {
14813         tag: 'div',
14814         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14815     },
14816     
14817     footer : {
14818         tag: 'div',
14819         cls: 'modal-footer',
14820         cn: [
14821             {
14822                 tag: 'div',
14823                 cls: 'row',
14824                 cn: [
14825                     {
14826                         tag: 'div',
14827                         cls: 'col-xs-6 text-left',
14828                         cn: {
14829                             tag: 'button',
14830                             cls: 'btn btn-danger roo-touch-view-cancel',
14831                             html: 'Cancel'
14832                         }
14833                     },
14834                     {
14835                         tag: 'div',
14836                         cls: 'col-xs-6 text-right',
14837                         cn: {
14838                             tag: 'button',
14839                             cls: 'btn btn-success roo-touch-view-ok',
14840                             html: 'OK'
14841                         }
14842                     }
14843                 ]
14844             }
14845         ]
14846         
14847     }
14848 });
14849
14850 Roo.apply(Roo.bootstrap.ComboBox,  {
14851     
14852     touchViewTemplate : {
14853         tag: 'div',
14854         cls: 'modal fade roo-combobox-touch-view',
14855         cn: [
14856             {
14857                 tag: 'div',
14858                 cls: 'modal-dialog',
14859                 style : 'position:fixed', // we have to fix position....
14860                 cn: [
14861                     {
14862                         tag: 'div',
14863                         cls: 'modal-content',
14864                         cn: [
14865                             Roo.bootstrap.ComboBox.header,
14866                             Roo.bootstrap.ComboBox.body,
14867                             Roo.bootstrap.ComboBox.footer
14868                         ]
14869                     }
14870                 ]
14871             }
14872         ]
14873     }
14874 });/*
14875  * Based on:
14876  * Ext JS Library 1.1.1
14877  * Copyright(c) 2006-2007, Ext JS, LLC.
14878  *
14879  * Originally Released Under LGPL - original licence link has changed is not relivant.
14880  *
14881  * Fork - LGPL
14882  * <script type="text/javascript">
14883  */
14884
14885 /**
14886  * @class Roo.View
14887  * @extends Roo.util.Observable
14888  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14889  * This class also supports single and multi selection modes. <br>
14890  * Create a data model bound view:
14891  <pre><code>
14892  var store = new Roo.data.Store(...);
14893
14894  var view = new Roo.View({
14895     el : "my-element",
14896     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14897  
14898     singleSelect: true,
14899     selectedClass: "ydataview-selected",
14900     store: store
14901  });
14902
14903  // listen for node click?
14904  view.on("click", function(vw, index, node, e){
14905  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14906  });
14907
14908  // load XML data
14909  dataModel.load("foobar.xml");
14910  </code></pre>
14911  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14912  * <br><br>
14913  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14914  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14915  * 
14916  * Note: old style constructor is still suported (container, template, config)
14917  * 
14918  * @constructor
14919  * Create a new View
14920  * @param {Object} config The config object
14921  * 
14922  */
14923 Roo.View = function(config, depreciated_tpl, depreciated_config){
14924     
14925     this.parent = false;
14926     
14927     if (typeof(depreciated_tpl) == 'undefined') {
14928         // new way.. - universal constructor.
14929         Roo.apply(this, config);
14930         this.el  = Roo.get(this.el);
14931     } else {
14932         // old format..
14933         this.el  = Roo.get(config);
14934         this.tpl = depreciated_tpl;
14935         Roo.apply(this, depreciated_config);
14936     }
14937     this.wrapEl  = this.el.wrap().wrap();
14938     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14939     
14940     
14941     if(typeof(this.tpl) == "string"){
14942         this.tpl = new Roo.Template(this.tpl);
14943     } else {
14944         // support xtype ctors..
14945         this.tpl = new Roo.factory(this.tpl, Roo);
14946     }
14947     
14948     
14949     this.tpl.compile();
14950     
14951     /** @private */
14952     this.addEvents({
14953         /**
14954          * @event beforeclick
14955          * Fires before a click is processed. Returns false to cancel the default action.
14956          * @param {Roo.View} this
14957          * @param {Number} index The index of the target node
14958          * @param {HTMLElement} node The target node
14959          * @param {Roo.EventObject} e The raw event object
14960          */
14961             "beforeclick" : true,
14962         /**
14963          * @event click
14964          * Fires when a template node is clicked.
14965          * @param {Roo.View} this
14966          * @param {Number} index The index of the target node
14967          * @param {HTMLElement} node The target node
14968          * @param {Roo.EventObject} e The raw event object
14969          */
14970             "click" : true,
14971         /**
14972          * @event dblclick
14973          * Fires when a template node is double clicked.
14974          * @param {Roo.View} this
14975          * @param {Number} index The index of the target node
14976          * @param {HTMLElement} node The target node
14977          * @param {Roo.EventObject} e The raw event object
14978          */
14979             "dblclick" : true,
14980         /**
14981          * @event contextmenu
14982          * Fires when a template node is right clicked.
14983          * @param {Roo.View} this
14984          * @param {Number} index The index of the target node
14985          * @param {HTMLElement} node The target node
14986          * @param {Roo.EventObject} e The raw event object
14987          */
14988             "contextmenu" : true,
14989         /**
14990          * @event selectionchange
14991          * Fires when the selected nodes change.
14992          * @param {Roo.View} this
14993          * @param {Array} selections Array of the selected nodes
14994          */
14995             "selectionchange" : true,
14996     
14997         /**
14998          * @event beforeselect
14999          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15000          * @param {Roo.View} this
15001          * @param {HTMLElement} node The node to be selected
15002          * @param {Array} selections Array of currently selected nodes
15003          */
15004             "beforeselect" : true,
15005         /**
15006          * @event preparedata
15007          * Fires on every row to render, to allow you to change the data.
15008          * @param {Roo.View} this
15009          * @param {Object} data to be rendered (change this)
15010          */
15011           "preparedata" : true
15012           
15013           
15014         });
15015
15016
15017
15018     this.el.on({
15019         "click": this.onClick,
15020         "dblclick": this.onDblClick,
15021         "contextmenu": this.onContextMenu,
15022         scope:this
15023     });
15024
15025     this.selections = [];
15026     this.nodes = [];
15027     this.cmp = new Roo.CompositeElementLite([]);
15028     if(this.store){
15029         this.store = Roo.factory(this.store, Roo.data);
15030         this.setStore(this.store, true);
15031     }
15032     
15033     if ( this.footer && this.footer.xtype) {
15034            
15035          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15036         
15037         this.footer.dataSource = this.store;
15038         this.footer.container = fctr;
15039         this.footer = Roo.factory(this.footer, Roo);
15040         fctr.insertFirst(this.el);
15041         
15042         // this is a bit insane - as the paging toolbar seems to detach the el..
15043 //        dom.parentNode.parentNode.parentNode
15044          // they get detached?
15045     }
15046     
15047     
15048     Roo.View.superclass.constructor.call(this);
15049     
15050     
15051 };
15052
15053 Roo.extend(Roo.View, Roo.util.Observable, {
15054     
15055      /**
15056      * @cfg {Roo.data.Store} store Data store to load data from.
15057      */
15058     store : false,
15059     
15060     /**
15061      * @cfg {String|Roo.Element} el The container element.
15062      */
15063     el : '',
15064     
15065     /**
15066      * @cfg {String|Roo.Template} tpl The template used by this View 
15067      */
15068     tpl : false,
15069     /**
15070      * @cfg {String} dataName the named area of the template to use as the data area
15071      *                          Works with domtemplates roo-name="name"
15072      */
15073     dataName: false,
15074     /**
15075      * @cfg {String} selectedClass The css class to add to selected nodes
15076      */
15077     selectedClass : "x-view-selected",
15078      /**
15079      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15080      */
15081     emptyText : "",
15082     
15083     /**
15084      * @cfg {String} text to display on mask (default Loading)
15085      */
15086     mask : false,
15087     /**
15088      * @cfg {Boolean} multiSelect Allow multiple selection
15089      */
15090     multiSelect : false,
15091     /**
15092      * @cfg {Boolean} singleSelect Allow single selection
15093      */
15094     singleSelect:  false,
15095     
15096     /**
15097      * @cfg {Boolean} toggleSelect - selecting 
15098      */
15099     toggleSelect : false,
15100     
15101     /**
15102      * @cfg {Boolean} tickable - selecting 
15103      */
15104     tickable : false,
15105     
15106     /**
15107      * Returns the element this view is bound to.
15108      * @return {Roo.Element}
15109      */
15110     getEl : function(){
15111         return this.wrapEl;
15112     },
15113     
15114     
15115
15116     /**
15117      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15118      */
15119     refresh : function(){
15120         //Roo.log('refresh');
15121         var t = this.tpl;
15122         
15123         // if we are using something like 'domtemplate', then
15124         // the what gets used is:
15125         // t.applySubtemplate(NAME, data, wrapping data..)
15126         // the outer template then get' applied with
15127         //     the store 'extra data'
15128         // and the body get's added to the
15129         //      roo-name="data" node?
15130         //      <span class='roo-tpl-{name}'></span> ?????
15131         
15132         
15133         
15134         this.clearSelections();
15135         this.el.update("");
15136         var html = [];
15137         var records = this.store.getRange();
15138         if(records.length < 1) {
15139             
15140             // is this valid??  = should it render a template??
15141             
15142             this.el.update(this.emptyText);
15143             return;
15144         }
15145         var el = this.el;
15146         if (this.dataName) {
15147             this.el.update(t.apply(this.store.meta)); //????
15148             el = this.el.child('.roo-tpl-' + this.dataName);
15149         }
15150         
15151         for(var i = 0, len = records.length; i < len; i++){
15152             var data = this.prepareData(records[i].data, i, records[i]);
15153             this.fireEvent("preparedata", this, data, i, records[i]);
15154             
15155             var d = Roo.apply({}, data);
15156             
15157             if(this.tickable){
15158                 Roo.apply(d, {'roo-id' : Roo.id()});
15159                 
15160                 var _this = this;
15161             
15162                 Roo.each(this.parent.item, function(item){
15163                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15164                         return;
15165                     }
15166                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15167                 });
15168             }
15169             
15170             html[html.length] = Roo.util.Format.trim(
15171                 this.dataName ?
15172                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15173                     t.apply(d)
15174             );
15175         }
15176         
15177         
15178         
15179         el.update(html.join(""));
15180         this.nodes = el.dom.childNodes;
15181         this.updateIndexes(0);
15182     },
15183     
15184
15185     /**
15186      * Function to override to reformat the data that is sent to
15187      * the template for each node.
15188      * DEPRICATED - use the preparedata event handler.
15189      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15190      * a JSON object for an UpdateManager bound view).
15191      */
15192     prepareData : function(data, index, record)
15193     {
15194         this.fireEvent("preparedata", this, data, index, record);
15195         return data;
15196     },
15197
15198     onUpdate : function(ds, record){
15199         // Roo.log('on update');   
15200         this.clearSelections();
15201         var index = this.store.indexOf(record);
15202         var n = this.nodes[index];
15203         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15204         n.parentNode.removeChild(n);
15205         this.updateIndexes(index, index);
15206     },
15207
15208     
15209     
15210 // --------- FIXME     
15211     onAdd : function(ds, records, index)
15212     {
15213         //Roo.log(['on Add', ds, records, index] );        
15214         this.clearSelections();
15215         if(this.nodes.length == 0){
15216             this.refresh();
15217             return;
15218         }
15219         var n = this.nodes[index];
15220         for(var i = 0, len = records.length; i < len; i++){
15221             var d = this.prepareData(records[i].data, i, records[i]);
15222             if(n){
15223                 this.tpl.insertBefore(n, d);
15224             }else{
15225                 
15226                 this.tpl.append(this.el, d);
15227             }
15228         }
15229         this.updateIndexes(index);
15230     },
15231
15232     onRemove : function(ds, record, index){
15233        // Roo.log('onRemove');
15234         this.clearSelections();
15235         var el = this.dataName  ?
15236             this.el.child('.roo-tpl-' + this.dataName) :
15237             this.el; 
15238         
15239         el.dom.removeChild(this.nodes[index]);
15240         this.updateIndexes(index);
15241     },
15242
15243     /**
15244      * Refresh an individual node.
15245      * @param {Number} index
15246      */
15247     refreshNode : function(index){
15248         this.onUpdate(this.store, this.store.getAt(index));
15249     },
15250
15251     updateIndexes : function(startIndex, endIndex){
15252         var ns = this.nodes;
15253         startIndex = startIndex || 0;
15254         endIndex = endIndex || ns.length - 1;
15255         for(var i = startIndex; i <= endIndex; i++){
15256             ns[i].nodeIndex = i;
15257         }
15258     },
15259
15260     /**
15261      * Changes the data store this view uses and refresh the view.
15262      * @param {Store} store
15263      */
15264     setStore : function(store, initial){
15265         if(!initial && this.store){
15266             this.store.un("datachanged", this.refresh);
15267             this.store.un("add", this.onAdd);
15268             this.store.un("remove", this.onRemove);
15269             this.store.un("update", this.onUpdate);
15270             this.store.un("clear", this.refresh);
15271             this.store.un("beforeload", this.onBeforeLoad);
15272             this.store.un("load", this.onLoad);
15273             this.store.un("loadexception", this.onLoad);
15274         }
15275         if(store){
15276           
15277             store.on("datachanged", this.refresh, this);
15278             store.on("add", this.onAdd, this);
15279             store.on("remove", this.onRemove, this);
15280             store.on("update", this.onUpdate, this);
15281             store.on("clear", this.refresh, this);
15282             store.on("beforeload", this.onBeforeLoad, this);
15283             store.on("load", this.onLoad, this);
15284             store.on("loadexception", this.onLoad, this);
15285         }
15286         
15287         if(store){
15288             this.refresh();
15289         }
15290     },
15291     /**
15292      * onbeforeLoad - masks the loading area.
15293      *
15294      */
15295     onBeforeLoad : function(store,opts)
15296     {
15297          //Roo.log('onBeforeLoad');   
15298         if (!opts.add) {
15299             this.el.update("");
15300         }
15301         this.el.mask(this.mask ? this.mask : "Loading" ); 
15302     },
15303     onLoad : function ()
15304     {
15305         this.el.unmask();
15306     },
15307     
15308
15309     /**
15310      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15311      * @param {HTMLElement} node
15312      * @return {HTMLElement} The template node
15313      */
15314     findItemFromChild : function(node){
15315         var el = this.dataName  ?
15316             this.el.child('.roo-tpl-' + this.dataName,true) :
15317             this.el.dom; 
15318         
15319         if(!node || node.parentNode == el){
15320                     return node;
15321             }
15322             var p = node.parentNode;
15323             while(p && p != el){
15324             if(p.parentNode == el){
15325                 return p;
15326             }
15327             p = p.parentNode;
15328         }
15329             return null;
15330     },
15331
15332     /** @ignore */
15333     onClick : function(e){
15334         var item = this.findItemFromChild(e.getTarget());
15335         if(item){
15336             var index = this.indexOf(item);
15337             if(this.onItemClick(item, index, e) !== false){
15338                 this.fireEvent("click", this, index, item, e);
15339             }
15340         }else{
15341             this.clearSelections();
15342         }
15343     },
15344
15345     /** @ignore */
15346     onContextMenu : function(e){
15347         var item = this.findItemFromChild(e.getTarget());
15348         if(item){
15349             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15350         }
15351     },
15352
15353     /** @ignore */
15354     onDblClick : function(e){
15355         var item = this.findItemFromChild(e.getTarget());
15356         if(item){
15357             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15358         }
15359     },
15360
15361     onItemClick : function(item, index, e)
15362     {
15363         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15364             return false;
15365         }
15366         if (this.toggleSelect) {
15367             var m = this.isSelected(item) ? 'unselect' : 'select';
15368             //Roo.log(m);
15369             var _t = this;
15370             _t[m](item, true, false);
15371             return true;
15372         }
15373         if(this.multiSelect || this.singleSelect){
15374             if(this.multiSelect && e.shiftKey && this.lastSelection){
15375                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15376             }else{
15377                 this.select(item, this.multiSelect && e.ctrlKey);
15378                 this.lastSelection = item;
15379             }
15380             
15381             if(!this.tickable){
15382                 e.preventDefault();
15383             }
15384             
15385         }
15386         return true;
15387     },
15388
15389     /**
15390      * Get the number of selected nodes.
15391      * @return {Number}
15392      */
15393     getSelectionCount : function(){
15394         return this.selections.length;
15395     },
15396
15397     /**
15398      * Get the currently selected nodes.
15399      * @return {Array} An array of HTMLElements
15400      */
15401     getSelectedNodes : function(){
15402         return this.selections;
15403     },
15404
15405     /**
15406      * Get the indexes of the selected nodes.
15407      * @return {Array}
15408      */
15409     getSelectedIndexes : function(){
15410         var indexes = [], s = this.selections;
15411         for(var i = 0, len = s.length; i < len; i++){
15412             indexes.push(s[i].nodeIndex);
15413         }
15414         return indexes;
15415     },
15416
15417     /**
15418      * Clear all selections
15419      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15420      */
15421     clearSelections : function(suppressEvent){
15422         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15423             this.cmp.elements = this.selections;
15424             this.cmp.removeClass(this.selectedClass);
15425             this.selections = [];
15426             if(!suppressEvent){
15427                 this.fireEvent("selectionchange", this, this.selections);
15428             }
15429         }
15430     },
15431
15432     /**
15433      * Returns true if the passed node is selected
15434      * @param {HTMLElement/Number} node The node or node index
15435      * @return {Boolean}
15436      */
15437     isSelected : function(node){
15438         var s = this.selections;
15439         if(s.length < 1){
15440             return false;
15441         }
15442         node = this.getNode(node);
15443         return s.indexOf(node) !== -1;
15444     },
15445
15446     /**
15447      * Selects nodes.
15448      * @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
15449      * @param {Boolean} keepExisting (optional) true to keep existing selections
15450      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15451      */
15452     select : function(nodeInfo, keepExisting, suppressEvent){
15453         if(nodeInfo instanceof Array){
15454             if(!keepExisting){
15455                 this.clearSelections(true);
15456             }
15457             for(var i = 0, len = nodeInfo.length; i < len; i++){
15458                 this.select(nodeInfo[i], true, true);
15459             }
15460             return;
15461         } 
15462         var node = this.getNode(nodeInfo);
15463         if(!node || this.isSelected(node)){
15464             return; // already selected.
15465         }
15466         if(!keepExisting){
15467             this.clearSelections(true);
15468         }
15469         
15470         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15471             Roo.fly(node).addClass(this.selectedClass);
15472             this.selections.push(node);
15473             if(!suppressEvent){
15474                 this.fireEvent("selectionchange", this, this.selections);
15475             }
15476         }
15477         
15478         
15479     },
15480       /**
15481      * Unselects nodes.
15482      * @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
15483      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15484      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15485      */
15486     unselect : function(nodeInfo, keepExisting, suppressEvent)
15487     {
15488         if(nodeInfo instanceof Array){
15489             Roo.each(this.selections, function(s) {
15490                 this.unselect(s, nodeInfo);
15491             }, this);
15492             return;
15493         }
15494         var node = this.getNode(nodeInfo);
15495         if(!node || !this.isSelected(node)){
15496             //Roo.log("not selected");
15497             return; // not selected.
15498         }
15499         // fireevent???
15500         var ns = [];
15501         Roo.each(this.selections, function(s) {
15502             if (s == node ) {
15503                 Roo.fly(node).removeClass(this.selectedClass);
15504
15505                 return;
15506             }
15507             ns.push(s);
15508         },this);
15509         
15510         this.selections= ns;
15511         this.fireEvent("selectionchange", this, this.selections);
15512     },
15513
15514     /**
15515      * Gets a template node.
15516      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15517      * @return {HTMLElement} The node or null if it wasn't found
15518      */
15519     getNode : function(nodeInfo){
15520         if(typeof nodeInfo == "string"){
15521             return document.getElementById(nodeInfo);
15522         }else if(typeof nodeInfo == "number"){
15523             return this.nodes[nodeInfo];
15524         }
15525         return nodeInfo;
15526     },
15527
15528     /**
15529      * Gets a range template nodes.
15530      * @param {Number} startIndex
15531      * @param {Number} endIndex
15532      * @return {Array} An array of nodes
15533      */
15534     getNodes : function(start, end){
15535         var ns = this.nodes;
15536         start = start || 0;
15537         end = typeof end == "undefined" ? ns.length - 1 : end;
15538         var nodes = [];
15539         if(start <= end){
15540             for(var i = start; i <= end; i++){
15541                 nodes.push(ns[i]);
15542             }
15543         } else{
15544             for(var i = start; i >= end; i--){
15545                 nodes.push(ns[i]);
15546             }
15547         }
15548         return nodes;
15549     },
15550
15551     /**
15552      * Finds the index of the passed node
15553      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15554      * @return {Number} The index of the node or -1
15555      */
15556     indexOf : function(node){
15557         node = this.getNode(node);
15558         if(typeof node.nodeIndex == "number"){
15559             return node.nodeIndex;
15560         }
15561         var ns = this.nodes;
15562         for(var i = 0, len = ns.length; i < len; i++){
15563             if(ns[i] == node){
15564                 return i;
15565             }
15566         }
15567         return -1;
15568     }
15569 });
15570 /*
15571  * - LGPL
15572  *
15573  * based on jquery fullcalendar
15574  * 
15575  */
15576
15577 Roo.bootstrap = Roo.bootstrap || {};
15578 /**
15579  * @class Roo.bootstrap.Calendar
15580  * @extends Roo.bootstrap.Component
15581  * Bootstrap Calendar class
15582  * @cfg {Boolean} loadMask (true|false) default false
15583  * @cfg {Object} header generate the user specific header of the calendar, default false
15584
15585  * @constructor
15586  * Create a new Container
15587  * @param {Object} config The config object
15588  */
15589
15590
15591
15592 Roo.bootstrap.Calendar = function(config){
15593     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15594      this.addEvents({
15595         /**
15596              * @event select
15597              * Fires when a date is selected
15598              * @param {DatePicker} this
15599              * @param {Date} date The selected date
15600              */
15601         'select': true,
15602         /**
15603              * @event monthchange
15604              * Fires when the displayed month changes 
15605              * @param {DatePicker} this
15606              * @param {Date} date The selected month
15607              */
15608         'monthchange': true,
15609         /**
15610              * @event evententer
15611              * Fires when mouse over an event
15612              * @param {Calendar} this
15613              * @param {event} Event
15614              */
15615         'evententer': true,
15616         /**
15617              * @event eventleave
15618              * Fires when the mouse leaves an
15619              * @param {Calendar} this
15620              * @param {event}
15621              */
15622         'eventleave': true,
15623         /**
15624              * @event eventclick
15625              * Fires when the mouse click an
15626              * @param {Calendar} this
15627              * @param {event}
15628              */
15629         'eventclick': true
15630         
15631     });
15632
15633 };
15634
15635 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15636     
15637      /**
15638      * @cfg {Number} startDay
15639      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15640      */
15641     startDay : 0,
15642     
15643     loadMask : false,
15644     
15645     header : false,
15646       
15647     getAutoCreate : function(){
15648         
15649         
15650         var fc_button = function(name, corner, style, content ) {
15651             return Roo.apply({},{
15652                 tag : 'span',
15653                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15654                          (corner.length ?
15655                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15656                             ''
15657                         ),
15658                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15659                 unselectable: 'on'
15660             });
15661         };
15662         
15663         var header = {};
15664         
15665         if(!this.header){
15666             header = {
15667                 tag : 'table',
15668                 cls : 'fc-header',
15669                 style : 'width:100%',
15670                 cn : [
15671                     {
15672                         tag: 'tr',
15673                         cn : [
15674                             {
15675                                 tag : 'td',
15676                                 cls : 'fc-header-left',
15677                                 cn : [
15678                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15679                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15680                                     { tag: 'span', cls: 'fc-header-space' },
15681                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15682
15683
15684                                 ]
15685                             },
15686
15687                             {
15688                                 tag : 'td',
15689                                 cls : 'fc-header-center',
15690                                 cn : [
15691                                     {
15692                                         tag: 'span',
15693                                         cls: 'fc-header-title',
15694                                         cn : {
15695                                             tag: 'H2',
15696                                             html : 'month / year'
15697                                         }
15698                                     }
15699
15700                                 ]
15701                             },
15702                             {
15703                                 tag : 'td',
15704                                 cls : 'fc-header-right',
15705                                 cn : [
15706                               /*      fc_button('month', 'left', '', 'month' ),
15707                                     fc_button('week', '', '', 'week' ),
15708                                     fc_button('day', 'right', '', 'day' )
15709                                 */    
15710
15711                                 ]
15712                             }
15713
15714                         ]
15715                     }
15716                 ]
15717             };
15718         }
15719         
15720         header = this.header;
15721         
15722        
15723         var cal_heads = function() {
15724             var ret = [];
15725             // fixme - handle this.
15726             
15727             for (var i =0; i < Date.dayNames.length; i++) {
15728                 var d = Date.dayNames[i];
15729                 ret.push({
15730                     tag: 'th',
15731                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15732                     html : d.substring(0,3)
15733                 });
15734                 
15735             }
15736             ret[0].cls += ' fc-first';
15737             ret[6].cls += ' fc-last';
15738             return ret;
15739         };
15740         var cal_cell = function(n) {
15741             return  {
15742                 tag: 'td',
15743                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15744                 cn : [
15745                     {
15746                         cn : [
15747                             {
15748                                 cls: 'fc-day-number',
15749                                 html: 'D'
15750                             },
15751                             {
15752                                 cls: 'fc-day-content',
15753                              
15754                                 cn : [
15755                                      {
15756                                         style: 'position: relative;' // height: 17px;
15757                                     }
15758                                 ]
15759                             }
15760                             
15761                             
15762                         ]
15763                     }
15764                 ]
15765                 
15766             }
15767         };
15768         var cal_rows = function() {
15769             
15770             var ret = [];
15771             for (var r = 0; r < 6; r++) {
15772                 var row= {
15773                     tag : 'tr',
15774                     cls : 'fc-week',
15775                     cn : []
15776                 };
15777                 
15778                 for (var i =0; i < Date.dayNames.length; i++) {
15779                     var d = Date.dayNames[i];
15780                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15781
15782                 }
15783                 row.cn[0].cls+=' fc-first';
15784                 row.cn[0].cn[0].style = 'min-height:90px';
15785                 row.cn[6].cls+=' fc-last';
15786                 ret.push(row);
15787                 
15788             }
15789             ret[0].cls += ' fc-first';
15790             ret[4].cls += ' fc-prev-last';
15791             ret[5].cls += ' fc-last';
15792             return ret;
15793             
15794         };
15795         
15796         var cal_table = {
15797             tag: 'table',
15798             cls: 'fc-border-separate',
15799             style : 'width:100%',
15800             cellspacing  : 0,
15801             cn : [
15802                 { 
15803                     tag: 'thead',
15804                     cn : [
15805                         { 
15806                             tag: 'tr',
15807                             cls : 'fc-first fc-last',
15808                             cn : cal_heads()
15809                         }
15810                     ]
15811                 },
15812                 { 
15813                     tag: 'tbody',
15814                     cn : cal_rows()
15815                 }
15816                   
15817             ]
15818         };
15819          
15820          var cfg = {
15821             cls : 'fc fc-ltr',
15822             cn : [
15823                 header,
15824                 {
15825                     cls : 'fc-content',
15826                     style : "position: relative;",
15827                     cn : [
15828                         {
15829                             cls : 'fc-view fc-view-month fc-grid',
15830                             style : 'position: relative',
15831                             unselectable : 'on',
15832                             cn : [
15833                                 {
15834                                     cls : 'fc-event-container',
15835                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15836                                 },
15837                                 cal_table
15838                             ]
15839                         }
15840                     ]
15841     
15842                 }
15843            ] 
15844             
15845         };
15846         
15847          
15848         
15849         return cfg;
15850     },
15851     
15852     
15853     initEvents : function()
15854     {
15855         if(!this.store){
15856             throw "can not find store for calendar";
15857         }
15858         
15859         var mark = {
15860             tag: "div",
15861             cls:"x-dlg-mask",
15862             style: "text-align:center",
15863             cn: [
15864                 {
15865                     tag: "div",
15866                     style: "background-color:white;width:50%;margin:250 auto",
15867                     cn: [
15868                         {
15869                             tag: "img",
15870                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15871                         },
15872                         {
15873                             tag: "span",
15874                             html: "Loading"
15875                         }
15876                         
15877                     ]
15878                 }
15879             ]
15880         };
15881         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15882         
15883         var size = this.el.select('.fc-content', true).first().getSize();
15884         this.maskEl.setSize(size.width, size.height);
15885         this.maskEl.enableDisplayMode("block");
15886         if(!this.loadMask){
15887             this.maskEl.hide();
15888         }
15889         
15890         this.store = Roo.factory(this.store, Roo.data);
15891         this.store.on('load', this.onLoad, this);
15892         this.store.on('beforeload', this.onBeforeLoad, this);
15893         
15894         this.resize();
15895         
15896         this.cells = this.el.select('.fc-day',true);
15897         //Roo.log(this.cells);
15898         this.textNodes = this.el.query('.fc-day-number');
15899         this.cells.addClassOnOver('fc-state-hover');
15900         
15901         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15902         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15903         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15904         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15905         
15906         this.on('monthchange', this.onMonthChange, this);
15907         
15908         this.update(new Date().clearTime());
15909     },
15910     
15911     resize : function() {
15912         var sz  = this.el.getSize();
15913         
15914         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15915         this.el.select('.fc-day-content div',true).setHeight(34);
15916     },
15917     
15918     
15919     // private
15920     showPrevMonth : function(e){
15921         this.update(this.activeDate.add("mo", -1));
15922     },
15923     showToday : function(e){
15924         this.update(new Date().clearTime());
15925     },
15926     // private
15927     showNextMonth : function(e){
15928         this.update(this.activeDate.add("mo", 1));
15929     },
15930
15931     // private
15932     showPrevYear : function(){
15933         this.update(this.activeDate.add("y", -1));
15934     },
15935
15936     // private
15937     showNextYear : function(){
15938         this.update(this.activeDate.add("y", 1));
15939     },
15940
15941     
15942    // private
15943     update : function(date)
15944     {
15945         var vd = this.activeDate;
15946         this.activeDate = date;
15947 //        if(vd && this.el){
15948 //            var t = date.getTime();
15949 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15950 //                Roo.log('using add remove');
15951 //                
15952 //                this.fireEvent('monthchange', this, date);
15953 //                
15954 //                this.cells.removeClass("fc-state-highlight");
15955 //                this.cells.each(function(c){
15956 //                   if(c.dateValue == t){
15957 //                       c.addClass("fc-state-highlight");
15958 //                       setTimeout(function(){
15959 //                            try{c.dom.firstChild.focus();}catch(e){}
15960 //                       }, 50);
15961 //                       return false;
15962 //                   }
15963 //                   return true;
15964 //                });
15965 //                return;
15966 //            }
15967 //        }
15968         
15969         var days = date.getDaysInMonth();
15970         
15971         var firstOfMonth = date.getFirstDateOfMonth();
15972         var startingPos = firstOfMonth.getDay()-this.startDay;
15973         
15974         if(startingPos < this.startDay){
15975             startingPos += 7;
15976         }
15977         
15978         var pm = date.add(Date.MONTH, -1);
15979         var prevStart = pm.getDaysInMonth()-startingPos;
15980 //        
15981         this.cells = this.el.select('.fc-day',true);
15982         this.textNodes = this.el.query('.fc-day-number');
15983         this.cells.addClassOnOver('fc-state-hover');
15984         
15985         var cells = this.cells.elements;
15986         var textEls = this.textNodes;
15987         
15988         Roo.each(cells, function(cell){
15989             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15990         });
15991         
15992         days += startingPos;
15993
15994         // convert everything to numbers so it's fast
15995         var day = 86400000;
15996         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15997         //Roo.log(d);
15998         //Roo.log(pm);
15999         //Roo.log(prevStart);
16000         
16001         var today = new Date().clearTime().getTime();
16002         var sel = date.clearTime().getTime();
16003         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16004         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16005         var ddMatch = this.disabledDatesRE;
16006         var ddText = this.disabledDatesText;
16007         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16008         var ddaysText = this.disabledDaysText;
16009         var format = this.format;
16010         
16011         var setCellClass = function(cal, cell){
16012             cell.row = 0;
16013             cell.events = [];
16014             cell.more = [];
16015             //Roo.log('set Cell Class');
16016             cell.title = "";
16017             var t = d.getTime();
16018             
16019             //Roo.log(d);
16020             
16021             cell.dateValue = t;
16022             if(t == today){
16023                 cell.className += " fc-today";
16024                 cell.className += " fc-state-highlight";
16025                 cell.title = cal.todayText;
16026             }
16027             if(t == sel){
16028                 // disable highlight in other month..
16029                 //cell.className += " fc-state-highlight";
16030                 
16031             }
16032             // disabling
16033             if(t < min) {
16034                 cell.className = " fc-state-disabled";
16035                 cell.title = cal.minText;
16036                 return;
16037             }
16038             if(t > max) {
16039                 cell.className = " fc-state-disabled";
16040                 cell.title = cal.maxText;
16041                 return;
16042             }
16043             if(ddays){
16044                 if(ddays.indexOf(d.getDay()) != -1){
16045                     cell.title = ddaysText;
16046                     cell.className = " fc-state-disabled";
16047                 }
16048             }
16049             if(ddMatch && format){
16050                 var fvalue = d.dateFormat(format);
16051                 if(ddMatch.test(fvalue)){
16052                     cell.title = ddText.replace("%0", fvalue);
16053                     cell.className = " fc-state-disabled";
16054                 }
16055             }
16056             
16057             if (!cell.initialClassName) {
16058                 cell.initialClassName = cell.dom.className;
16059             }
16060             
16061             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16062         };
16063
16064         var i = 0;
16065         
16066         for(; i < startingPos; i++) {
16067             textEls[i].innerHTML = (++prevStart);
16068             d.setDate(d.getDate()+1);
16069             
16070             cells[i].className = "fc-past fc-other-month";
16071             setCellClass(this, cells[i]);
16072         }
16073         
16074         var intDay = 0;
16075         
16076         for(; i < days; i++){
16077             intDay = i - startingPos + 1;
16078             textEls[i].innerHTML = (intDay);
16079             d.setDate(d.getDate()+1);
16080             
16081             cells[i].className = ''; // "x-date-active";
16082             setCellClass(this, cells[i]);
16083         }
16084         var extraDays = 0;
16085         
16086         for(; i < 42; i++) {
16087             textEls[i].innerHTML = (++extraDays);
16088             d.setDate(d.getDate()+1);
16089             
16090             cells[i].className = "fc-future fc-other-month";
16091             setCellClass(this, cells[i]);
16092         }
16093         
16094         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16095         
16096         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16097         
16098         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16099         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16100         
16101         if(totalRows != 6){
16102             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16103             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16104         }
16105         
16106         this.fireEvent('monthchange', this, date);
16107         
16108         
16109         /*
16110         if(!this.internalRender){
16111             var main = this.el.dom.firstChild;
16112             var w = main.offsetWidth;
16113             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16114             Roo.fly(main).setWidth(w);
16115             this.internalRender = true;
16116             // opera does not respect the auto grow header center column
16117             // then, after it gets a width opera refuses to recalculate
16118             // without a second pass
16119             if(Roo.isOpera && !this.secondPass){
16120                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16121                 this.secondPass = true;
16122                 this.update.defer(10, this, [date]);
16123             }
16124         }
16125         */
16126         
16127     },
16128     
16129     findCell : function(dt) {
16130         dt = dt.clearTime().getTime();
16131         var ret = false;
16132         this.cells.each(function(c){
16133             //Roo.log("check " +c.dateValue + '?=' + dt);
16134             if(c.dateValue == dt){
16135                 ret = c;
16136                 return false;
16137             }
16138             return true;
16139         });
16140         
16141         return ret;
16142     },
16143     
16144     findCells : function(ev) {
16145         var s = ev.start.clone().clearTime().getTime();
16146        // Roo.log(s);
16147         var e= ev.end.clone().clearTime().getTime();
16148        // Roo.log(e);
16149         var ret = [];
16150         this.cells.each(function(c){
16151              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16152             
16153             if(c.dateValue > e){
16154                 return ;
16155             }
16156             if(c.dateValue < s){
16157                 return ;
16158             }
16159             ret.push(c);
16160         });
16161         
16162         return ret;    
16163     },
16164     
16165 //    findBestRow: function(cells)
16166 //    {
16167 //        var ret = 0;
16168 //        
16169 //        for (var i =0 ; i < cells.length;i++) {
16170 //            ret  = Math.max(cells[i].rows || 0,ret);
16171 //        }
16172 //        return ret;
16173 //        
16174 //    },
16175     
16176     
16177     addItem : function(ev)
16178     {
16179         // look for vertical location slot in
16180         var cells = this.findCells(ev);
16181         
16182 //        ev.row = this.findBestRow(cells);
16183         
16184         // work out the location.
16185         
16186         var crow = false;
16187         var rows = [];
16188         for(var i =0; i < cells.length; i++) {
16189             
16190             cells[i].row = cells[0].row;
16191             
16192             if(i == 0){
16193                 cells[i].row = cells[i].row + 1;
16194             }
16195             
16196             if (!crow) {
16197                 crow = {
16198                     start : cells[i],
16199                     end :  cells[i]
16200                 };
16201                 continue;
16202             }
16203             if (crow.start.getY() == cells[i].getY()) {
16204                 // on same row.
16205                 crow.end = cells[i];
16206                 continue;
16207             }
16208             // different row.
16209             rows.push(crow);
16210             crow = {
16211                 start: cells[i],
16212                 end : cells[i]
16213             };
16214             
16215         }
16216         
16217         rows.push(crow);
16218         ev.els = [];
16219         ev.rows = rows;
16220         ev.cells = cells;
16221         
16222         cells[0].events.push(ev);
16223         
16224         this.calevents.push(ev);
16225     },
16226     
16227     clearEvents: function() {
16228         
16229         if(!this.calevents){
16230             return;
16231         }
16232         
16233         Roo.each(this.cells.elements, function(c){
16234             c.row = 0;
16235             c.events = [];
16236             c.more = [];
16237         });
16238         
16239         Roo.each(this.calevents, function(e) {
16240             Roo.each(e.els, function(el) {
16241                 el.un('mouseenter' ,this.onEventEnter, this);
16242                 el.un('mouseleave' ,this.onEventLeave, this);
16243                 el.remove();
16244             },this);
16245         },this);
16246         
16247         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16248             e.remove();
16249         });
16250         
16251     },
16252     
16253     renderEvents: function()
16254     {   
16255         var _this = this;
16256         
16257         this.cells.each(function(c) {
16258             
16259             if(c.row < 5){
16260                 return;
16261             }
16262             
16263             var ev = c.events;
16264             
16265             var r = 4;
16266             if(c.row != c.events.length){
16267                 r = 4 - (4 - (c.row - c.events.length));
16268             }
16269             
16270             c.events = ev.slice(0, r);
16271             c.more = ev.slice(r);
16272             
16273             if(c.more.length && c.more.length == 1){
16274                 c.events.push(c.more.pop());
16275             }
16276             
16277             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16278             
16279         });
16280             
16281         this.cells.each(function(c) {
16282             
16283             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16284             
16285             
16286             for (var e = 0; e < c.events.length; e++){
16287                 var ev = c.events[e];
16288                 var rows = ev.rows;
16289                 
16290                 for(var i = 0; i < rows.length; i++) {
16291                 
16292                     // how many rows should it span..
16293
16294                     var  cfg = {
16295                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16296                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16297
16298                         unselectable : "on",
16299                         cn : [
16300                             {
16301                                 cls: 'fc-event-inner',
16302                                 cn : [
16303     //                                {
16304     //                                  tag:'span',
16305     //                                  cls: 'fc-event-time',
16306     //                                  html : cells.length > 1 ? '' : ev.time
16307     //                                },
16308                                     {
16309                                       tag:'span',
16310                                       cls: 'fc-event-title',
16311                                       html : String.format('{0}', ev.title)
16312                                     }
16313
16314
16315                                 ]
16316                             },
16317                             {
16318                                 cls: 'ui-resizable-handle ui-resizable-e',
16319                                 html : '&nbsp;&nbsp;&nbsp'
16320                             }
16321
16322                         ]
16323                     };
16324
16325                     if (i == 0) {
16326                         cfg.cls += ' fc-event-start';
16327                     }
16328                     if ((i+1) == rows.length) {
16329                         cfg.cls += ' fc-event-end';
16330                     }
16331
16332                     var ctr = _this.el.select('.fc-event-container',true).first();
16333                     var cg = ctr.createChild(cfg);
16334
16335                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16336                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16337
16338                     var r = (c.more.length) ? 1 : 0;
16339                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16340                     cg.setWidth(ebox.right - sbox.x -2);
16341
16342                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16343                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16344                     cg.on('click', _this.onEventClick, _this, ev);
16345
16346                     ev.els.push(cg);
16347                     
16348                 }
16349                 
16350             }
16351             
16352             
16353             if(c.more.length){
16354                 var  cfg = {
16355                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16356                     style : 'position: absolute',
16357                     unselectable : "on",
16358                     cn : [
16359                         {
16360                             cls: 'fc-event-inner',
16361                             cn : [
16362                                 {
16363                                   tag:'span',
16364                                   cls: 'fc-event-title',
16365                                   html : 'More'
16366                                 }
16367
16368
16369                             ]
16370                         },
16371                         {
16372                             cls: 'ui-resizable-handle ui-resizable-e',
16373                             html : '&nbsp;&nbsp;&nbsp'
16374                         }
16375
16376                     ]
16377                 };
16378
16379                 var ctr = _this.el.select('.fc-event-container',true).first();
16380                 var cg = ctr.createChild(cfg);
16381
16382                 var sbox = c.select('.fc-day-content',true).first().getBox();
16383                 var ebox = c.select('.fc-day-content',true).first().getBox();
16384                 //Roo.log(cg);
16385                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16386                 cg.setWidth(ebox.right - sbox.x -2);
16387
16388                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16389                 
16390             }
16391             
16392         });
16393         
16394         
16395         
16396     },
16397     
16398     onEventEnter: function (e, el,event,d) {
16399         this.fireEvent('evententer', this, el, event);
16400     },
16401     
16402     onEventLeave: function (e, el,event,d) {
16403         this.fireEvent('eventleave', this, el, event);
16404     },
16405     
16406     onEventClick: function (e, el,event,d) {
16407         this.fireEvent('eventclick', this, el, event);
16408     },
16409     
16410     onMonthChange: function () {
16411         this.store.load();
16412     },
16413     
16414     onMoreEventClick: function(e, el, more)
16415     {
16416         var _this = this;
16417         
16418         this.calpopover.placement = 'right';
16419         this.calpopover.setTitle('More');
16420         
16421         this.calpopover.setContent('');
16422         
16423         var ctr = this.calpopover.el.select('.popover-content', true).first();
16424         
16425         Roo.each(more, function(m){
16426             var cfg = {
16427                 cls : 'fc-event-hori fc-event-draggable',
16428                 html : m.title
16429             };
16430             var cg = ctr.createChild(cfg);
16431             
16432             cg.on('click', _this.onEventClick, _this, m);
16433         });
16434         
16435         this.calpopover.show(el);
16436         
16437         
16438     },
16439     
16440     onLoad: function () 
16441     {   
16442         this.calevents = [];
16443         var cal = this;
16444         
16445         if(this.store.getCount() > 0){
16446             this.store.data.each(function(d){
16447                cal.addItem({
16448                     id : d.data.id,
16449                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16450                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16451                     time : d.data.start_time,
16452                     title : d.data.title,
16453                     description : d.data.description,
16454                     venue : d.data.venue
16455                 });
16456             });
16457         }
16458         
16459         this.renderEvents();
16460         
16461         if(this.calevents.length && this.loadMask){
16462             this.maskEl.hide();
16463         }
16464     },
16465     
16466     onBeforeLoad: function()
16467     {
16468         this.clearEvents();
16469         if(this.loadMask){
16470             this.maskEl.show();
16471         }
16472     }
16473 });
16474
16475  
16476  /*
16477  * - LGPL
16478  *
16479  * element
16480  * 
16481  */
16482
16483 /**
16484  * @class Roo.bootstrap.Popover
16485  * @extends Roo.bootstrap.Component
16486  * Bootstrap Popover class
16487  * @cfg {String} html contents of the popover   (or false to use children..)
16488  * @cfg {String} title of popover (or false to hide)
16489  * @cfg {String} placement how it is placed
16490  * @cfg {String} trigger click || hover (or false to trigger manually)
16491  * @cfg {String} over what (parent or false to trigger manually.)
16492  * @cfg {Number} delay - delay before showing
16493  
16494  * @constructor
16495  * Create a new Popover
16496  * @param {Object} config The config object
16497  */
16498
16499 Roo.bootstrap.Popover = function(config){
16500     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16501     
16502     this.addEvents({
16503         // raw events
16504          /**
16505          * @event show
16506          * After the popover show
16507          * 
16508          * @param {Roo.bootstrap.Popover} this
16509          */
16510         "show" : true,
16511         /**
16512          * @event hide
16513          * After the popover hide
16514          * 
16515          * @param {Roo.bootstrap.Popover} this
16516          */
16517         "hide" : true
16518     });
16519 };
16520
16521 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16522     
16523     title: 'Fill in a title',
16524     html: false,
16525     
16526     placement : 'right',
16527     trigger : 'hover', // hover
16528     
16529     delay : 0,
16530     
16531     over: 'parent',
16532     
16533     can_build_overlaid : false,
16534     
16535     getChildContainer : function()
16536     {
16537         return this.el.select('.popover-content',true).first();
16538     },
16539     
16540     getAutoCreate : function(){
16541          
16542         var cfg = {
16543            cls : 'popover roo-dynamic',
16544            style: 'display:block',
16545            cn : [
16546                 {
16547                     cls : 'arrow'
16548                 },
16549                 {
16550                     cls : 'popover-inner',
16551                     cn : [
16552                         {
16553                             tag: 'h3',
16554                             cls: 'popover-title',
16555                             html : this.title
16556                         },
16557                         {
16558                             cls : 'popover-content',
16559                             html : this.html
16560                         }
16561                     ]
16562                     
16563                 }
16564            ]
16565         };
16566         
16567         return cfg;
16568     },
16569     setTitle: function(str)
16570     {
16571         this.title = str;
16572         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16573     },
16574     setContent: function(str)
16575     {
16576         this.html = str;
16577         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16578     },
16579     // as it get's added to the bottom of the page.
16580     onRender : function(ct, position)
16581     {
16582         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16583         if(!this.el){
16584             var cfg = Roo.apply({},  this.getAutoCreate());
16585             cfg.id = Roo.id();
16586             
16587             if (this.cls) {
16588                 cfg.cls += ' ' + this.cls;
16589             }
16590             if (this.style) {
16591                 cfg.style = this.style;
16592             }
16593             //Roo.log("adding to ");
16594             this.el = Roo.get(document.body).createChild(cfg, position);
16595 //            Roo.log(this.el);
16596         }
16597         this.initEvents();
16598     },
16599     
16600     initEvents : function()
16601     {
16602         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16603         this.el.enableDisplayMode('block');
16604         this.el.hide();
16605         if (this.over === false) {
16606             return; 
16607         }
16608         if (this.triggers === false) {
16609             return;
16610         }
16611         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16612         var triggers = this.trigger ? this.trigger.split(' ') : [];
16613         Roo.each(triggers, function(trigger) {
16614         
16615             if (trigger == 'click') {
16616                 on_el.on('click', this.toggle, this);
16617             } else if (trigger != 'manual') {
16618                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16619                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16620       
16621                 on_el.on(eventIn  ,this.enter, this);
16622                 on_el.on(eventOut, this.leave, this);
16623             }
16624         }, this);
16625         
16626     },
16627     
16628     
16629     // private
16630     timeout : null,
16631     hoverState : null,
16632     
16633     toggle : function () {
16634         this.hoverState == 'in' ? this.leave() : this.enter();
16635     },
16636     
16637     enter : function () {
16638         
16639         clearTimeout(this.timeout);
16640     
16641         this.hoverState = 'in';
16642     
16643         if (!this.delay || !this.delay.show) {
16644             this.show();
16645             return;
16646         }
16647         var _t = this;
16648         this.timeout = setTimeout(function () {
16649             if (_t.hoverState == 'in') {
16650                 _t.show();
16651             }
16652         }, this.delay.show)
16653     },
16654     
16655     leave : function() {
16656         clearTimeout(this.timeout);
16657     
16658         this.hoverState = 'out';
16659     
16660         if (!this.delay || !this.delay.hide) {
16661             this.hide();
16662             return;
16663         }
16664         var _t = this;
16665         this.timeout = setTimeout(function () {
16666             if (_t.hoverState == 'out') {
16667                 _t.hide();
16668             }
16669         }, this.delay.hide)
16670     },
16671     
16672     show : function (on_el)
16673     {
16674         if (!on_el) {
16675             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16676         }
16677         
16678         // set content.
16679         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16680         if (this.html !== false) {
16681             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16682         }
16683         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16684         if (!this.title.length) {
16685             this.el.select('.popover-title',true).hide();
16686         }
16687         
16688         var placement = typeof this.placement == 'function' ?
16689             this.placement.call(this, this.el, on_el) :
16690             this.placement;
16691             
16692         var autoToken = /\s?auto?\s?/i;
16693         var autoPlace = autoToken.test(placement);
16694         if (autoPlace) {
16695             placement = placement.replace(autoToken, '') || 'top';
16696         }
16697         
16698         //this.el.detach()
16699         //this.el.setXY([0,0]);
16700         this.el.show();
16701         this.el.dom.style.display='block';
16702         this.el.addClass(placement);
16703         
16704         //this.el.appendTo(on_el);
16705         
16706         var p = this.getPosition();
16707         var box = this.el.getBox();
16708         
16709         if (autoPlace) {
16710             // fixme..
16711         }
16712         var align = Roo.bootstrap.Popover.alignment[placement];
16713         this.el.alignTo(on_el, align[0],align[1]);
16714         //var arrow = this.el.select('.arrow',true).first();
16715         //arrow.set(align[2], 
16716         
16717         this.el.addClass('in');
16718         
16719         
16720         if (this.el.hasClass('fade')) {
16721             // fade it?
16722         }
16723         
16724         this.hoverState = 'in';
16725         
16726         this.fireEvent('show', this);
16727         
16728     },
16729     hide : function()
16730     {
16731         this.el.setXY([0,0]);
16732         this.el.removeClass('in');
16733         this.el.hide();
16734         this.hoverState = null;
16735         
16736         this.fireEvent('hide', this);
16737     }
16738     
16739 });
16740
16741 Roo.bootstrap.Popover.alignment = {
16742     'left' : ['r-l', [-10,0], 'right'],
16743     'right' : ['l-r', [10,0], 'left'],
16744     'bottom' : ['t-b', [0,10], 'top'],
16745     'top' : [ 'b-t', [0,-10], 'bottom']
16746 };
16747
16748  /*
16749  * - LGPL
16750  *
16751  * Progress
16752  * 
16753  */
16754
16755 /**
16756  * @class Roo.bootstrap.Progress
16757  * @extends Roo.bootstrap.Component
16758  * Bootstrap Progress class
16759  * @cfg {Boolean} striped striped of the progress bar
16760  * @cfg {Boolean} active animated of the progress bar
16761  * 
16762  * 
16763  * @constructor
16764  * Create a new Progress
16765  * @param {Object} config The config object
16766  */
16767
16768 Roo.bootstrap.Progress = function(config){
16769     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16770 };
16771
16772 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16773     
16774     striped : false,
16775     active: false,
16776     
16777     getAutoCreate : function(){
16778         var cfg = {
16779             tag: 'div',
16780             cls: 'progress'
16781         };
16782         
16783         
16784         if(this.striped){
16785             cfg.cls += ' progress-striped';
16786         }
16787       
16788         if(this.active){
16789             cfg.cls += ' active';
16790         }
16791         
16792         
16793         return cfg;
16794     }
16795    
16796 });
16797
16798  
16799
16800  /*
16801  * - LGPL
16802  *
16803  * ProgressBar
16804  * 
16805  */
16806
16807 /**
16808  * @class Roo.bootstrap.ProgressBar
16809  * @extends Roo.bootstrap.Component
16810  * Bootstrap ProgressBar class
16811  * @cfg {Number} aria_valuenow aria-value now
16812  * @cfg {Number} aria_valuemin aria-value min
16813  * @cfg {Number} aria_valuemax aria-value max
16814  * @cfg {String} label label for the progress bar
16815  * @cfg {String} panel (success | info | warning | danger )
16816  * @cfg {String} role role of the progress bar
16817  * @cfg {String} sr_only text
16818  * 
16819  * 
16820  * @constructor
16821  * Create a new ProgressBar
16822  * @param {Object} config The config object
16823  */
16824
16825 Roo.bootstrap.ProgressBar = function(config){
16826     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16827 };
16828
16829 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16830     
16831     aria_valuenow : 0,
16832     aria_valuemin : 0,
16833     aria_valuemax : 100,
16834     label : false,
16835     panel : false,
16836     role : false,
16837     sr_only: false,
16838     
16839     getAutoCreate : function()
16840     {
16841         
16842         var cfg = {
16843             tag: 'div',
16844             cls: 'progress-bar',
16845             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16846         };
16847         
16848         if(this.sr_only){
16849             cfg.cn = {
16850                 tag: 'span',
16851                 cls: 'sr-only',
16852                 html: this.sr_only
16853             }
16854         }
16855         
16856         if(this.role){
16857             cfg.role = this.role;
16858         }
16859         
16860         if(this.aria_valuenow){
16861             cfg['aria-valuenow'] = this.aria_valuenow;
16862         }
16863         
16864         if(this.aria_valuemin){
16865             cfg['aria-valuemin'] = this.aria_valuemin;
16866         }
16867         
16868         if(this.aria_valuemax){
16869             cfg['aria-valuemax'] = this.aria_valuemax;
16870         }
16871         
16872         if(this.label && !this.sr_only){
16873             cfg.html = this.label;
16874         }
16875         
16876         if(this.panel){
16877             cfg.cls += ' progress-bar-' + this.panel;
16878         }
16879         
16880         return cfg;
16881     },
16882     
16883     update : function(aria_valuenow)
16884     {
16885         this.aria_valuenow = aria_valuenow;
16886         
16887         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16888     }
16889    
16890 });
16891
16892  
16893
16894  /*
16895  * - LGPL
16896  *
16897  * column
16898  * 
16899  */
16900
16901 /**
16902  * @class Roo.bootstrap.TabGroup
16903  * @extends Roo.bootstrap.Column
16904  * Bootstrap Column class
16905  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16906  * @cfg {Boolean} carousel true to make the group behave like a carousel
16907  * @cfg {Boolean} bullets show bullets for the panels
16908  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16909  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16910  * @cfg {Boolean} showarrow (true|false) show arrow default true
16911  * 
16912  * @constructor
16913  * Create a new TabGroup
16914  * @param {Object} config The config object
16915  */
16916
16917 Roo.bootstrap.TabGroup = function(config){
16918     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16919     if (!this.navId) {
16920         this.navId = Roo.id();
16921     }
16922     this.tabs = [];
16923     Roo.bootstrap.TabGroup.register(this);
16924     
16925 };
16926
16927 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16928     
16929     carousel : false,
16930     transition : false,
16931     bullets : 0,
16932     timer : 0,
16933     autoslide : false,
16934     slideFn : false,
16935     slideOnTouch : false,
16936     showarrow : true,
16937     
16938     getAutoCreate : function()
16939     {
16940         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16941         
16942         cfg.cls += ' tab-content';
16943         
16944         if (this.carousel) {
16945             cfg.cls += ' carousel slide';
16946             
16947             cfg.cn = [{
16948                cls : 'carousel-inner',
16949                cn : []
16950             }];
16951         
16952             if(this.bullets  && !Roo.isTouch){
16953                 
16954                 var bullets = {
16955                     cls : 'carousel-bullets',
16956                     cn : []
16957                 };
16958                
16959                 if(this.bullets_cls){
16960                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16961                 }
16962                 
16963                 bullets.cn.push({
16964                     cls : 'clear'
16965                 });
16966                 
16967                 cfg.cn[0].cn.push(bullets);
16968             }
16969             
16970             if(this.showarrow){
16971                 cfg.cn[0].cn.push({
16972                     tag : 'div',
16973                     class : 'carousel-arrow',
16974                     cn : [
16975                         {
16976                             tag : 'div',
16977                             class : 'carousel-prev',
16978                             cn : [
16979                                 {
16980                                     tag : 'i',
16981                                     class : 'fa fa-chevron-left'
16982                                 }
16983                             ]
16984                         },
16985                         {
16986                             tag : 'div',
16987                             class : 'carousel-next',
16988                             cn : [
16989                                 {
16990                                     tag : 'i',
16991                                     class : 'fa fa-chevron-right'
16992                                 }
16993                             ]
16994                         }
16995                     ]
16996                 });
16997             }
16998             
16999         }
17000         
17001         return cfg;
17002     },
17003     
17004     initEvents:  function()
17005     {
17006 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17007 //            this.el.on("touchstart", this.onTouchStart, this);
17008 //        }
17009         
17010         if(this.autoslide){
17011             var _this = this;
17012             
17013             this.slideFn = window.setInterval(function() {
17014                 _this.showPanelNext();
17015             }, this.timer);
17016         }
17017         
17018         if(this.showarrow){
17019             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17020             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17021         }
17022         
17023         
17024     },
17025     
17026 //    onTouchStart : function(e, el, o)
17027 //    {
17028 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17029 //            return;
17030 //        }
17031 //        
17032 //        this.showPanelNext();
17033 //    },
17034     
17035     
17036     getChildContainer : function()
17037     {
17038         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17039     },
17040     
17041     /**
17042     * register a Navigation item
17043     * @param {Roo.bootstrap.NavItem} the navitem to add
17044     */
17045     register : function(item)
17046     {
17047         this.tabs.push( item);
17048         item.navId = this.navId; // not really needed..
17049         this.addBullet();
17050     
17051     },
17052     
17053     getActivePanel : function()
17054     {
17055         var r = false;
17056         Roo.each(this.tabs, function(t) {
17057             if (t.active) {
17058                 r = t;
17059                 return false;
17060             }
17061             return null;
17062         });
17063         return r;
17064         
17065     },
17066     getPanelByName : function(n)
17067     {
17068         var r = false;
17069         Roo.each(this.tabs, function(t) {
17070             if (t.tabId == n) {
17071                 r = t;
17072                 return false;
17073             }
17074             return null;
17075         });
17076         return r;
17077     },
17078     indexOfPanel : function(p)
17079     {
17080         var r = false;
17081         Roo.each(this.tabs, function(t,i) {
17082             if (t.tabId == p.tabId) {
17083                 r = i;
17084                 return false;
17085             }
17086             return null;
17087         });
17088         return r;
17089     },
17090     /**
17091      * show a specific panel
17092      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17093      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17094      */
17095     showPanel : function (pan)
17096     {
17097         if(this.transition || typeof(pan) == 'undefined'){
17098             Roo.log("waiting for the transitionend");
17099             return;
17100         }
17101         
17102         if (typeof(pan) == 'number') {
17103             pan = this.tabs[pan];
17104         }
17105         
17106         if (typeof(pan) == 'string') {
17107             pan = this.getPanelByName(pan);
17108         }
17109         
17110         var cur = this.getActivePanel();
17111         
17112         if(!pan || !cur){
17113             Roo.log('pan or acitve pan is undefined');
17114             return false;
17115         }
17116         
17117         if (pan.tabId == this.getActivePanel().tabId) {
17118             return true;
17119         }
17120         
17121         if (false === cur.fireEvent('beforedeactivate')) {
17122             return false;
17123         }
17124         
17125         if(this.bullets > 0 && !Roo.isTouch){
17126             this.setActiveBullet(this.indexOfPanel(pan));
17127         }
17128         
17129         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17130             
17131             this.transition = true;
17132             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17133             var lr = dir == 'next' ? 'left' : 'right';
17134             pan.el.addClass(dir); // or prev
17135             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17136             cur.el.addClass(lr); // or right
17137             pan.el.addClass(lr);
17138             
17139             var _this = this;
17140             cur.el.on('transitionend', function() {
17141                 Roo.log("trans end?");
17142                 
17143                 pan.el.removeClass([lr,dir]);
17144                 pan.setActive(true);
17145                 
17146                 cur.el.removeClass([lr]);
17147                 cur.setActive(false);
17148                 
17149                 _this.transition = false;
17150                 
17151             }, this, { single:  true } );
17152             
17153             return true;
17154         }
17155         
17156         cur.setActive(false);
17157         pan.setActive(true);
17158         
17159         return true;
17160         
17161     },
17162     showPanelNext : function()
17163     {
17164         var i = this.indexOfPanel(this.getActivePanel());
17165         
17166         if (i >= this.tabs.length - 1 && !this.autoslide) {
17167             return;
17168         }
17169         
17170         if (i >= this.tabs.length - 1 && this.autoslide) {
17171             i = -1;
17172         }
17173         
17174         this.showPanel(this.tabs[i+1]);
17175     },
17176     
17177     showPanelPrev : function()
17178     {
17179         var i = this.indexOfPanel(this.getActivePanel());
17180         
17181         if (i  < 1 && !this.autoslide) {
17182             return;
17183         }
17184         
17185         if (i < 1 && this.autoslide) {
17186             i = this.tabs.length;
17187         }
17188         
17189         this.showPanel(this.tabs[i-1]);
17190     },
17191     
17192     
17193     addBullet: function()
17194     {
17195         if(!this.bullets || Roo.isTouch){
17196             return;
17197         }
17198         var ctr = this.el.select('.carousel-bullets',true).first();
17199         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17200         var bullet = ctr.createChild({
17201             cls : 'bullet bullet-' + i
17202         },ctr.dom.lastChild);
17203         
17204         
17205         var _this = this;
17206         
17207         bullet.on('click', (function(e, el, o, ii, t){
17208
17209             e.preventDefault();
17210
17211             this.showPanel(ii);
17212
17213             if(this.autoslide && this.slideFn){
17214                 clearInterval(this.slideFn);
17215                 this.slideFn = window.setInterval(function() {
17216                     _this.showPanelNext();
17217                 }, this.timer);
17218             }
17219
17220         }).createDelegate(this, [i, bullet], true));
17221                 
17222         
17223     },
17224      
17225     setActiveBullet : function(i)
17226     {
17227         if(Roo.isTouch){
17228             return;
17229         }
17230         
17231         Roo.each(this.el.select('.bullet', true).elements, function(el){
17232             el.removeClass('selected');
17233         });
17234
17235         var bullet = this.el.select('.bullet-' + i, true).first();
17236         
17237         if(!bullet){
17238             return;
17239         }
17240         
17241         bullet.addClass('selected');
17242     }
17243     
17244     
17245   
17246 });
17247
17248  
17249
17250  
17251  
17252 Roo.apply(Roo.bootstrap.TabGroup, {
17253     
17254     groups: {},
17255      /**
17256     * register a Navigation Group
17257     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17258     */
17259     register : function(navgrp)
17260     {
17261         this.groups[navgrp.navId] = navgrp;
17262         
17263     },
17264     /**
17265     * fetch a Navigation Group based on the navigation ID
17266     * if one does not exist , it will get created.
17267     * @param {string} the navgroup to add
17268     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17269     */
17270     get: function(navId) {
17271         if (typeof(this.groups[navId]) == 'undefined') {
17272             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17273         }
17274         return this.groups[navId] ;
17275     }
17276     
17277     
17278     
17279 });
17280
17281  /*
17282  * - LGPL
17283  *
17284  * TabPanel
17285  * 
17286  */
17287
17288 /**
17289  * @class Roo.bootstrap.TabPanel
17290  * @extends Roo.bootstrap.Component
17291  * Bootstrap TabPanel class
17292  * @cfg {Boolean} active panel active
17293  * @cfg {String} html panel content
17294  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17295  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17296  * @cfg {String} href click to link..
17297  * 
17298  * 
17299  * @constructor
17300  * Create a new TabPanel
17301  * @param {Object} config The config object
17302  */
17303
17304 Roo.bootstrap.TabPanel = function(config){
17305     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17306     this.addEvents({
17307         /**
17308              * @event changed
17309              * Fires when the active status changes
17310              * @param {Roo.bootstrap.TabPanel} this
17311              * @param {Boolean} state the new state
17312             
17313          */
17314         'changed': true,
17315         /**
17316              * @event beforedeactivate
17317              * Fires before a tab is de-activated - can be used to do validation on a form.
17318              * @param {Roo.bootstrap.TabPanel} this
17319              * @return {Boolean} false if there is an error
17320             
17321          */
17322         'beforedeactivate': true
17323      });
17324     
17325     this.tabId = this.tabId || Roo.id();
17326   
17327 };
17328
17329 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17330     
17331     active: false,
17332     html: false,
17333     tabId: false,
17334     navId : false,
17335     href : '',
17336     
17337     getAutoCreate : function(){
17338         var cfg = {
17339             tag: 'div',
17340             // item is needed for carousel - not sure if it has any effect otherwise
17341             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17342             html: this.html || ''
17343         };
17344         
17345         if(this.active){
17346             cfg.cls += ' active';
17347         }
17348         
17349         if(this.tabId){
17350             cfg.tabId = this.tabId;
17351         }
17352         
17353         
17354         return cfg;
17355     },
17356     
17357     initEvents:  function()
17358     {
17359         var p = this.parent();
17360         
17361         this.navId = this.navId || p.navId;
17362         
17363         if (typeof(this.navId) != 'undefined') {
17364             // not really needed.. but just in case.. parent should be a NavGroup.
17365             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17366             
17367             tg.register(this);
17368             
17369             var i = tg.tabs.length - 1;
17370             
17371             if(this.active && tg.bullets > 0 && i < tg.bullets){
17372                 tg.setActiveBullet(i);
17373             }
17374         }
17375         
17376         this.el.on('click', this.onClick, this);
17377         
17378         if(Roo.isTouch){
17379             this.el.on("touchstart", this.onTouchStart, this);
17380             this.el.on("touchmove", this.onTouchMove, this);
17381             this.el.on("touchend", this.onTouchEnd, this);
17382         }
17383         
17384     },
17385     
17386     onRender : function(ct, position)
17387     {
17388         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17389     },
17390     
17391     setActive : function(state)
17392     {
17393         Roo.log("panel - set active " + this.tabId + "=" + state);
17394         
17395         this.active = state;
17396         if (!state) {
17397             this.el.removeClass('active');
17398             
17399         } else  if (!this.el.hasClass('active')) {
17400             this.el.addClass('active');
17401         }
17402         
17403         this.fireEvent('changed', this, state);
17404     },
17405     
17406     onClick : function(e)
17407     {
17408         e.preventDefault();
17409         
17410         if(!this.href.length){
17411             return;
17412         }
17413         
17414         window.location.href = this.href;
17415     },
17416     
17417     startX : 0,
17418     startY : 0,
17419     endX : 0,
17420     endY : 0,
17421     swiping : false,
17422     
17423     onTouchStart : function(e)
17424     {
17425         this.swiping = false;
17426         
17427         this.startX = e.browserEvent.touches[0].clientX;
17428         this.startY = e.browserEvent.touches[0].clientY;
17429     },
17430     
17431     onTouchMove : function(e)
17432     {
17433         this.swiping = true;
17434         
17435         this.endX = e.browserEvent.touches[0].clientX;
17436         this.endY = e.browserEvent.touches[0].clientY;
17437     },
17438     
17439     onTouchEnd : function(e)
17440     {
17441         if(!this.swiping){
17442             this.onClick(e);
17443             return;
17444         }
17445         
17446         var tabGroup = this.parent();
17447         
17448         if(this.endX > this.startX){ // swiping right
17449             tabGroup.showPanelPrev();
17450             return;
17451         }
17452         
17453         if(this.startX > this.endX){ // swiping left
17454             tabGroup.showPanelNext();
17455             return;
17456         }
17457     }
17458     
17459     
17460 });
17461  
17462
17463  
17464
17465  /*
17466  * - LGPL
17467  *
17468  * DateField
17469  * 
17470  */
17471
17472 /**
17473  * @class Roo.bootstrap.DateField
17474  * @extends Roo.bootstrap.Input
17475  * Bootstrap DateField class
17476  * @cfg {Number} weekStart default 0
17477  * @cfg {String} viewMode default empty, (months|years)
17478  * @cfg {String} minViewMode default empty, (months|years)
17479  * @cfg {Number} startDate default -Infinity
17480  * @cfg {Number} endDate default Infinity
17481  * @cfg {Boolean} todayHighlight default false
17482  * @cfg {Boolean} todayBtn default false
17483  * @cfg {Boolean} calendarWeeks default false
17484  * @cfg {Object} daysOfWeekDisabled default empty
17485  * @cfg {Boolean} singleMode default false (true | false)
17486  * 
17487  * @cfg {Boolean} keyboardNavigation default true
17488  * @cfg {String} language default en
17489  * 
17490  * @constructor
17491  * Create a new DateField
17492  * @param {Object} config The config object
17493  */
17494
17495 Roo.bootstrap.DateField = function(config){
17496     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17497      this.addEvents({
17498             /**
17499              * @event show
17500              * Fires when this field show.
17501              * @param {Roo.bootstrap.DateField} this
17502              * @param {Mixed} date The date value
17503              */
17504             show : true,
17505             /**
17506              * @event show
17507              * Fires when this field hide.
17508              * @param {Roo.bootstrap.DateField} this
17509              * @param {Mixed} date The date value
17510              */
17511             hide : true,
17512             /**
17513              * @event select
17514              * Fires when select a date.
17515              * @param {Roo.bootstrap.DateField} this
17516              * @param {Mixed} date The date value
17517              */
17518             select : true,
17519             /**
17520              * @event beforeselect
17521              * Fires when before select a date.
17522              * @param {Roo.bootstrap.DateField} this
17523              * @param {Mixed} date The date value
17524              */
17525             beforeselect : true
17526         });
17527 };
17528
17529 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17530     
17531     /**
17532      * @cfg {String} format
17533      * The default date format string which can be overriden for localization support.  The format must be
17534      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17535      */
17536     format : "m/d/y",
17537     /**
17538      * @cfg {String} altFormats
17539      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17540      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17541      */
17542     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17543     
17544     weekStart : 0,
17545     
17546     viewMode : '',
17547     
17548     minViewMode : '',
17549     
17550     todayHighlight : false,
17551     
17552     todayBtn: false,
17553     
17554     language: 'en',
17555     
17556     keyboardNavigation: true,
17557     
17558     calendarWeeks: false,
17559     
17560     startDate: -Infinity,
17561     
17562     endDate: Infinity,
17563     
17564     daysOfWeekDisabled: [],
17565     
17566     _events: [],
17567     
17568     singleMode : false,
17569     
17570     UTCDate: function()
17571     {
17572         return new Date(Date.UTC.apply(Date, arguments));
17573     },
17574     
17575     UTCToday: function()
17576     {
17577         var today = new Date();
17578         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17579     },
17580     
17581     getDate: function() {
17582             var d = this.getUTCDate();
17583             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17584     },
17585     
17586     getUTCDate: function() {
17587             return this.date;
17588     },
17589     
17590     setDate: function(d) {
17591             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17592     },
17593     
17594     setUTCDate: function(d) {
17595             this.date = d;
17596             this.setValue(this.formatDate(this.date));
17597     },
17598         
17599     onRender: function(ct, position)
17600     {
17601         
17602         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17603         
17604         this.language = this.language || 'en';
17605         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17606         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17607         
17608         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17609         this.format = this.format || 'm/d/y';
17610         this.isInline = false;
17611         this.isInput = true;
17612         this.component = this.el.select('.add-on', true).first() || false;
17613         this.component = (this.component && this.component.length === 0) ? false : this.component;
17614         this.hasInput = this.component && this.inputEl().length;
17615         
17616         if (typeof(this.minViewMode === 'string')) {
17617             switch (this.minViewMode) {
17618                 case 'months':
17619                     this.minViewMode = 1;
17620                     break;
17621                 case 'years':
17622                     this.minViewMode = 2;
17623                     break;
17624                 default:
17625                     this.minViewMode = 0;
17626                     break;
17627             }
17628         }
17629         
17630         if (typeof(this.viewMode === 'string')) {
17631             switch (this.viewMode) {
17632                 case 'months':
17633                     this.viewMode = 1;
17634                     break;
17635                 case 'years':
17636                     this.viewMode = 2;
17637                     break;
17638                 default:
17639                     this.viewMode = 0;
17640                     break;
17641             }
17642         }
17643                 
17644         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17645         
17646 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17647         
17648         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17649         
17650         this.picker().on('mousedown', this.onMousedown, this);
17651         this.picker().on('click', this.onClick, this);
17652         
17653         this.picker().addClass('datepicker-dropdown');
17654         
17655         this.startViewMode = this.viewMode;
17656         
17657         if(this.singleMode){
17658             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17659                 v.setVisibilityMode(Roo.Element.DISPLAY);
17660                 v.hide();
17661             });
17662             
17663             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17664                 v.setStyle('width', '189px');
17665             });
17666         }
17667         
17668         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17669             if(!this.calendarWeeks){
17670                 v.remove();
17671                 return;
17672             }
17673             
17674             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17675             v.attr('colspan', function(i, val){
17676                 return parseInt(val) + 1;
17677             });
17678         });
17679                         
17680         
17681         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17682         
17683         this.setStartDate(this.startDate);
17684         this.setEndDate(this.endDate);
17685         
17686         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17687         
17688         this.fillDow();
17689         this.fillMonths();
17690         this.update();
17691         this.showMode();
17692         
17693         if(this.isInline) {
17694             this.show();
17695         }
17696     },
17697     
17698     picker : function()
17699     {
17700         return this.pickerEl;
17701 //        return this.el.select('.datepicker', true).first();
17702     },
17703     
17704     fillDow: function()
17705     {
17706         var dowCnt = this.weekStart;
17707         
17708         var dow = {
17709             tag: 'tr',
17710             cn: [
17711                 
17712             ]
17713         };
17714         
17715         if(this.calendarWeeks){
17716             dow.cn.push({
17717                 tag: 'th',
17718                 cls: 'cw',
17719                 html: '&nbsp;'
17720             })
17721         }
17722         
17723         while (dowCnt < this.weekStart + 7) {
17724             dow.cn.push({
17725                 tag: 'th',
17726                 cls: 'dow',
17727                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17728             });
17729         }
17730         
17731         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17732     },
17733     
17734     fillMonths: function()
17735     {    
17736         var i = 0;
17737         var months = this.picker().select('>.datepicker-months td', true).first();
17738         
17739         months.dom.innerHTML = '';
17740         
17741         while (i < 12) {
17742             var month = {
17743                 tag: 'span',
17744                 cls: 'month',
17745                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17746             };
17747             
17748             months.createChild(month);
17749         }
17750         
17751     },
17752     
17753     update: function()
17754     {
17755         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;
17756         
17757         if (this.date < this.startDate) {
17758             this.viewDate = new Date(this.startDate);
17759         } else if (this.date > this.endDate) {
17760             this.viewDate = new Date(this.endDate);
17761         } else {
17762             this.viewDate = new Date(this.date);
17763         }
17764         
17765         this.fill();
17766     },
17767     
17768     fill: function() 
17769     {
17770         var d = new Date(this.viewDate),
17771                 year = d.getUTCFullYear(),
17772                 month = d.getUTCMonth(),
17773                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17774                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17775                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17776                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17777                 currentDate = this.date && this.date.valueOf(),
17778                 today = this.UTCToday();
17779         
17780         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17781         
17782 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17783         
17784 //        this.picker.select('>tfoot th.today').
17785 //                                              .text(dates[this.language].today)
17786 //                                              .toggle(this.todayBtn !== false);
17787     
17788         this.updateNavArrows();
17789         this.fillMonths();
17790                                                 
17791         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17792         
17793         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17794          
17795         prevMonth.setUTCDate(day);
17796         
17797         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17798         
17799         var nextMonth = new Date(prevMonth);
17800         
17801         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17802         
17803         nextMonth = nextMonth.valueOf();
17804         
17805         var fillMonths = false;
17806         
17807         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17808         
17809         while(prevMonth.valueOf() < nextMonth) {
17810             var clsName = '';
17811             
17812             if (prevMonth.getUTCDay() === this.weekStart) {
17813                 if(fillMonths){
17814                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17815                 }
17816                     
17817                 fillMonths = {
17818                     tag: 'tr',
17819                     cn: []
17820                 };
17821                 
17822                 if(this.calendarWeeks){
17823                     // ISO 8601: First week contains first thursday.
17824                     // ISO also states week starts on Monday, but we can be more abstract here.
17825                     var
17826                     // Start of current week: based on weekstart/current date
17827                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17828                     // Thursday of this week
17829                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17830                     // First Thursday of year, year from thursday
17831                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17832                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17833                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17834                     
17835                     fillMonths.cn.push({
17836                         tag: 'td',
17837                         cls: 'cw',
17838                         html: calWeek
17839                     });
17840                 }
17841             }
17842             
17843             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17844                 clsName += ' old';
17845             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17846                 clsName += ' new';
17847             }
17848             if (this.todayHighlight &&
17849                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17850                 prevMonth.getUTCMonth() == today.getMonth() &&
17851                 prevMonth.getUTCDate() == today.getDate()) {
17852                 clsName += ' today';
17853             }
17854             
17855             if (currentDate && prevMonth.valueOf() === currentDate) {
17856                 clsName += ' active';
17857             }
17858             
17859             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17860                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17861                     clsName += ' disabled';
17862             }
17863             
17864             fillMonths.cn.push({
17865                 tag: 'td',
17866                 cls: 'day ' + clsName,
17867                 html: prevMonth.getDate()
17868             });
17869             
17870             prevMonth.setDate(prevMonth.getDate()+1);
17871         }
17872           
17873         var currentYear = this.date && this.date.getUTCFullYear();
17874         var currentMonth = this.date && this.date.getUTCMonth();
17875         
17876         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17877         
17878         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17879             v.removeClass('active');
17880             
17881             if(currentYear === year && k === currentMonth){
17882                 v.addClass('active');
17883             }
17884             
17885             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17886                 v.addClass('disabled');
17887             }
17888             
17889         });
17890         
17891         
17892         year = parseInt(year/10, 10) * 10;
17893         
17894         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17895         
17896         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17897         
17898         year -= 1;
17899         for (var i = -1; i < 11; i++) {
17900             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17901                 tag: 'span',
17902                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17903                 html: year
17904             });
17905             
17906             year += 1;
17907         }
17908     },
17909     
17910     showMode: function(dir) 
17911     {
17912         if (dir) {
17913             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17914         }
17915         
17916         Roo.each(this.picker().select('>div',true).elements, function(v){
17917             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17918             v.hide();
17919         });
17920         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17921     },
17922     
17923     place: function()
17924     {
17925         if(this.isInline) {
17926             return;
17927         }
17928         
17929         this.picker().removeClass(['bottom', 'top']);
17930         
17931         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17932             /*
17933              * place to the top of element!
17934              *
17935              */
17936             
17937             this.picker().addClass('top');
17938             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17939             
17940             return;
17941         }
17942         
17943         this.picker().addClass('bottom');
17944         
17945         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17946     },
17947     
17948     parseDate : function(value)
17949     {
17950         if(!value || value instanceof Date){
17951             return value;
17952         }
17953         var v = Date.parseDate(value, this.format);
17954         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17955             v = Date.parseDate(value, 'Y-m-d');
17956         }
17957         if(!v && this.altFormats){
17958             if(!this.altFormatsArray){
17959                 this.altFormatsArray = this.altFormats.split("|");
17960             }
17961             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17962                 v = Date.parseDate(value, this.altFormatsArray[i]);
17963             }
17964         }
17965         return v;
17966     },
17967     
17968     formatDate : function(date, fmt)
17969     {   
17970         return (!date || !(date instanceof Date)) ?
17971         date : date.dateFormat(fmt || this.format);
17972     },
17973     
17974     onFocus : function()
17975     {
17976         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17977         this.show();
17978     },
17979     
17980     onBlur : function()
17981     {
17982         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17983         
17984         var d = this.inputEl().getValue();
17985         
17986         this.setValue(d);
17987                 
17988         this.hide();
17989     },
17990     
17991     show : function()
17992     {
17993         this.picker().show();
17994         this.update();
17995         this.place();
17996         
17997         this.fireEvent('show', this, this.date);
17998     },
17999     
18000     hide : function()
18001     {
18002         if(this.isInline) {
18003             return;
18004         }
18005         this.picker().hide();
18006         this.viewMode = this.startViewMode;
18007         this.showMode();
18008         
18009         this.fireEvent('hide', this, this.date);
18010         
18011     },
18012     
18013     onMousedown: function(e)
18014     {
18015         e.stopPropagation();
18016         e.preventDefault();
18017     },
18018     
18019     keyup: function(e)
18020     {
18021         Roo.bootstrap.DateField.superclass.keyup.call(this);
18022         this.update();
18023     },
18024
18025     setValue: function(v)
18026     {
18027         if(this.fireEvent('beforeselect', this, v) !== false){
18028             var d = new Date(this.parseDate(v) ).clearTime();
18029         
18030             if(isNaN(d.getTime())){
18031                 this.date = this.viewDate = '';
18032                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18033                 return;
18034             }
18035
18036             v = this.formatDate(d);
18037
18038             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18039
18040             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18041
18042             this.update();
18043
18044             this.fireEvent('select', this, this.date);
18045         }
18046     },
18047     
18048     getValue: function()
18049     {
18050         return this.formatDate(this.date);
18051     },
18052     
18053     fireKey: function(e)
18054     {
18055         if (!this.picker().isVisible()){
18056             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18057                 this.show();
18058             }
18059             return;
18060         }
18061         
18062         var dateChanged = false,
18063         dir, day, month,
18064         newDate, newViewDate;
18065         
18066         switch(e.keyCode){
18067             case 27: // escape
18068                 this.hide();
18069                 e.preventDefault();
18070                 break;
18071             case 37: // left
18072             case 39: // right
18073                 if (!this.keyboardNavigation) {
18074                     break;
18075                 }
18076                 dir = e.keyCode == 37 ? -1 : 1;
18077                 
18078                 if (e.ctrlKey){
18079                     newDate = this.moveYear(this.date, dir);
18080                     newViewDate = this.moveYear(this.viewDate, dir);
18081                 } else if (e.shiftKey){
18082                     newDate = this.moveMonth(this.date, dir);
18083                     newViewDate = this.moveMonth(this.viewDate, dir);
18084                 } else {
18085                     newDate = new Date(this.date);
18086                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18087                     newViewDate = new Date(this.viewDate);
18088                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18089                 }
18090                 if (this.dateWithinRange(newDate)){
18091                     this.date = newDate;
18092                     this.viewDate = newViewDate;
18093                     this.setValue(this.formatDate(this.date));
18094 //                    this.update();
18095                     e.preventDefault();
18096                     dateChanged = true;
18097                 }
18098                 break;
18099             case 38: // up
18100             case 40: // down
18101                 if (!this.keyboardNavigation) {
18102                     break;
18103                 }
18104                 dir = e.keyCode == 38 ? -1 : 1;
18105                 if (e.ctrlKey){
18106                     newDate = this.moveYear(this.date, dir);
18107                     newViewDate = this.moveYear(this.viewDate, dir);
18108                 } else if (e.shiftKey){
18109                     newDate = this.moveMonth(this.date, dir);
18110                     newViewDate = this.moveMonth(this.viewDate, dir);
18111                 } else {
18112                     newDate = new Date(this.date);
18113                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18114                     newViewDate = new Date(this.viewDate);
18115                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18116                 }
18117                 if (this.dateWithinRange(newDate)){
18118                     this.date = newDate;
18119                     this.viewDate = newViewDate;
18120                     this.setValue(this.formatDate(this.date));
18121 //                    this.update();
18122                     e.preventDefault();
18123                     dateChanged = true;
18124                 }
18125                 break;
18126             case 13: // enter
18127                 this.setValue(this.formatDate(this.date));
18128                 this.hide();
18129                 e.preventDefault();
18130                 break;
18131             case 9: // tab
18132                 this.setValue(this.formatDate(this.date));
18133                 this.hide();
18134                 break;
18135             case 16: // shift
18136             case 17: // ctrl
18137             case 18: // alt
18138                 break;
18139             default :
18140                 this.hide();
18141                 
18142         }
18143     },
18144     
18145     
18146     onClick: function(e) 
18147     {
18148         e.stopPropagation();
18149         e.preventDefault();
18150         
18151         var target = e.getTarget();
18152         
18153         if(target.nodeName.toLowerCase() === 'i'){
18154             target = Roo.get(target).dom.parentNode;
18155         }
18156         
18157         var nodeName = target.nodeName;
18158         var className = target.className;
18159         var html = target.innerHTML;
18160         //Roo.log(nodeName);
18161         
18162         switch(nodeName.toLowerCase()) {
18163             case 'th':
18164                 switch(className) {
18165                     case 'switch':
18166                         this.showMode(1);
18167                         break;
18168                     case 'prev':
18169                     case 'next':
18170                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18171                         switch(this.viewMode){
18172                                 case 0:
18173                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18174                                         break;
18175                                 case 1:
18176                                 case 2:
18177                                         this.viewDate = this.moveYear(this.viewDate, dir);
18178                                         break;
18179                         }
18180                         this.fill();
18181                         break;
18182                     case 'today':
18183                         var date = new Date();
18184                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18185 //                        this.fill()
18186                         this.setValue(this.formatDate(this.date));
18187                         
18188                         this.hide();
18189                         break;
18190                 }
18191                 break;
18192             case 'span':
18193                 if (className.indexOf('disabled') < 0) {
18194                     this.viewDate.setUTCDate(1);
18195                     if (className.indexOf('month') > -1) {
18196                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18197                     } else {
18198                         var year = parseInt(html, 10) || 0;
18199                         this.viewDate.setUTCFullYear(year);
18200                         
18201                     }
18202                     
18203                     if(this.singleMode){
18204                         this.setValue(this.formatDate(this.viewDate));
18205                         this.hide();
18206                         return;
18207                     }
18208                     
18209                     this.showMode(-1);
18210                     this.fill();
18211                 }
18212                 break;
18213                 
18214             case 'td':
18215                 //Roo.log(className);
18216                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18217                     var day = parseInt(html, 10) || 1;
18218                     var year = this.viewDate.getUTCFullYear(),
18219                         month = this.viewDate.getUTCMonth();
18220
18221                     if (className.indexOf('old') > -1) {
18222                         if(month === 0 ){
18223                             month = 11;
18224                             year -= 1;
18225                         }else{
18226                             month -= 1;
18227                         }
18228                     } else if (className.indexOf('new') > -1) {
18229                         if (month == 11) {
18230                             month = 0;
18231                             year += 1;
18232                         } else {
18233                             month += 1;
18234                         }
18235                     }
18236                     //Roo.log([year,month,day]);
18237                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18238                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18239 //                    this.fill();
18240                     //Roo.log(this.formatDate(this.date));
18241                     this.setValue(this.formatDate(this.date));
18242                     this.hide();
18243                 }
18244                 break;
18245         }
18246     },
18247     
18248     setStartDate: function(startDate)
18249     {
18250         this.startDate = startDate || -Infinity;
18251         if (this.startDate !== -Infinity) {
18252             this.startDate = this.parseDate(this.startDate);
18253         }
18254         this.update();
18255         this.updateNavArrows();
18256     },
18257
18258     setEndDate: function(endDate)
18259     {
18260         this.endDate = endDate || Infinity;
18261         if (this.endDate !== Infinity) {
18262             this.endDate = this.parseDate(this.endDate);
18263         }
18264         this.update();
18265         this.updateNavArrows();
18266     },
18267     
18268     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18269     {
18270         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18271         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18272             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18273         }
18274         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18275             return parseInt(d, 10);
18276         });
18277         this.update();
18278         this.updateNavArrows();
18279     },
18280     
18281     updateNavArrows: function() 
18282     {
18283         if(this.singleMode){
18284             return;
18285         }
18286         
18287         var d = new Date(this.viewDate),
18288         year = d.getUTCFullYear(),
18289         month = d.getUTCMonth();
18290         
18291         Roo.each(this.picker().select('.prev', true).elements, function(v){
18292             v.show();
18293             switch (this.viewMode) {
18294                 case 0:
18295
18296                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18297                         v.hide();
18298                     }
18299                     break;
18300                 case 1:
18301                 case 2:
18302                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18303                         v.hide();
18304                     }
18305                     break;
18306             }
18307         });
18308         
18309         Roo.each(this.picker().select('.next', true).elements, function(v){
18310             v.show();
18311             switch (this.viewMode) {
18312                 case 0:
18313
18314                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18315                         v.hide();
18316                     }
18317                     break;
18318                 case 1:
18319                 case 2:
18320                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18321                         v.hide();
18322                     }
18323                     break;
18324             }
18325         })
18326     },
18327     
18328     moveMonth: function(date, dir)
18329     {
18330         if (!dir) {
18331             return date;
18332         }
18333         var new_date = new Date(date.valueOf()),
18334         day = new_date.getUTCDate(),
18335         month = new_date.getUTCMonth(),
18336         mag = Math.abs(dir),
18337         new_month, test;
18338         dir = dir > 0 ? 1 : -1;
18339         if (mag == 1){
18340             test = dir == -1
18341             // If going back one month, make sure month is not current month
18342             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18343             ? function(){
18344                 return new_date.getUTCMonth() == month;
18345             }
18346             // If going forward one month, make sure month is as expected
18347             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18348             : function(){
18349                 return new_date.getUTCMonth() != new_month;
18350             };
18351             new_month = month + dir;
18352             new_date.setUTCMonth(new_month);
18353             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18354             if (new_month < 0 || new_month > 11) {
18355                 new_month = (new_month + 12) % 12;
18356             }
18357         } else {
18358             // For magnitudes >1, move one month at a time...
18359             for (var i=0; i<mag; i++) {
18360                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18361                 new_date = this.moveMonth(new_date, dir);
18362             }
18363             // ...then reset the day, keeping it in the new month
18364             new_month = new_date.getUTCMonth();
18365             new_date.setUTCDate(day);
18366             test = function(){
18367                 return new_month != new_date.getUTCMonth();
18368             };
18369         }
18370         // Common date-resetting loop -- if date is beyond end of month, make it
18371         // end of month
18372         while (test()){
18373             new_date.setUTCDate(--day);
18374             new_date.setUTCMonth(new_month);
18375         }
18376         return new_date;
18377     },
18378
18379     moveYear: function(date, dir)
18380     {
18381         return this.moveMonth(date, dir*12);
18382     },
18383
18384     dateWithinRange: function(date)
18385     {
18386         return date >= this.startDate && date <= this.endDate;
18387     },
18388
18389     
18390     remove: function() 
18391     {
18392         this.picker().remove();
18393     },
18394     
18395     validateValue : function(value)
18396     {
18397         if(value.length < 1)  {
18398             if(this.allowBlank){
18399                 return true;
18400             }
18401             return false;
18402         }
18403         
18404         if(value.length < this.minLength){
18405             return false;
18406         }
18407         if(value.length > this.maxLength){
18408             return false;
18409         }
18410         if(this.vtype){
18411             var vt = Roo.form.VTypes;
18412             if(!vt[this.vtype](value, this)){
18413                 return false;
18414             }
18415         }
18416         if(typeof this.validator == "function"){
18417             var msg = this.validator(value);
18418             if(msg !== true){
18419                 return false;
18420             }
18421         }
18422         
18423         if(this.regex && !this.regex.test(value)){
18424             return false;
18425         }
18426         
18427         if(typeof(this.parseDate(value)) == 'undefined'){
18428             return false;
18429         }
18430         
18431         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18432             return false;
18433         }      
18434         
18435         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18436             return false;
18437         } 
18438         
18439         
18440         return true;
18441     }
18442    
18443 });
18444
18445 Roo.apply(Roo.bootstrap.DateField,  {
18446     
18447     head : {
18448         tag: 'thead',
18449         cn: [
18450         {
18451             tag: 'tr',
18452             cn: [
18453             {
18454                 tag: 'th',
18455                 cls: 'prev',
18456                 html: '<i class="fa fa-arrow-left"/>'
18457             },
18458             {
18459                 tag: 'th',
18460                 cls: 'switch',
18461                 colspan: '5'
18462             },
18463             {
18464                 tag: 'th',
18465                 cls: 'next',
18466                 html: '<i class="fa fa-arrow-right"/>'
18467             }
18468
18469             ]
18470         }
18471         ]
18472     },
18473     
18474     content : {
18475         tag: 'tbody',
18476         cn: [
18477         {
18478             tag: 'tr',
18479             cn: [
18480             {
18481                 tag: 'td',
18482                 colspan: '7'
18483             }
18484             ]
18485         }
18486         ]
18487     },
18488     
18489     footer : {
18490         tag: 'tfoot',
18491         cn: [
18492         {
18493             tag: 'tr',
18494             cn: [
18495             {
18496                 tag: 'th',
18497                 colspan: '7',
18498                 cls: 'today'
18499             }
18500                     
18501             ]
18502         }
18503         ]
18504     },
18505     
18506     dates:{
18507         en: {
18508             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18509             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18510             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18511             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18512             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18513             today: "Today"
18514         }
18515     },
18516     
18517     modes: [
18518     {
18519         clsName: 'days',
18520         navFnc: 'Month',
18521         navStep: 1
18522     },
18523     {
18524         clsName: 'months',
18525         navFnc: 'FullYear',
18526         navStep: 1
18527     },
18528     {
18529         clsName: 'years',
18530         navFnc: 'FullYear',
18531         navStep: 10
18532     }]
18533 });
18534
18535 Roo.apply(Roo.bootstrap.DateField,  {
18536   
18537     template : {
18538         tag: 'div',
18539         cls: 'datepicker dropdown-menu roo-dynamic',
18540         cn: [
18541         {
18542             tag: 'div',
18543             cls: 'datepicker-days',
18544             cn: [
18545             {
18546                 tag: 'table',
18547                 cls: 'table-condensed',
18548                 cn:[
18549                 Roo.bootstrap.DateField.head,
18550                 {
18551                     tag: 'tbody'
18552                 },
18553                 Roo.bootstrap.DateField.footer
18554                 ]
18555             }
18556             ]
18557         },
18558         {
18559             tag: 'div',
18560             cls: 'datepicker-months',
18561             cn: [
18562             {
18563                 tag: 'table',
18564                 cls: 'table-condensed',
18565                 cn:[
18566                 Roo.bootstrap.DateField.head,
18567                 Roo.bootstrap.DateField.content,
18568                 Roo.bootstrap.DateField.footer
18569                 ]
18570             }
18571             ]
18572         },
18573         {
18574             tag: 'div',
18575             cls: 'datepicker-years',
18576             cn: [
18577             {
18578                 tag: 'table',
18579                 cls: 'table-condensed',
18580                 cn:[
18581                 Roo.bootstrap.DateField.head,
18582                 Roo.bootstrap.DateField.content,
18583                 Roo.bootstrap.DateField.footer
18584                 ]
18585             }
18586             ]
18587         }
18588         ]
18589     }
18590 });
18591
18592  
18593
18594  /*
18595  * - LGPL
18596  *
18597  * TimeField
18598  * 
18599  */
18600
18601 /**
18602  * @class Roo.bootstrap.TimeField
18603  * @extends Roo.bootstrap.Input
18604  * Bootstrap DateField class
18605  * 
18606  * 
18607  * @constructor
18608  * Create a new TimeField
18609  * @param {Object} config The config object
18610  */
18611
18612 Roo.bootstrap.TimeField = function(config){
18613     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18614     this.addEvents({
18615             /**
18616              * @event show
18617              * Fires when this field show.
18618              * @param {Roo.bootstrap.DateField} thisthis
18619              * @param {Mixed} date The date value
18620              */
18621             show : true,
18622             /**
18623              * @event show
18624              * Fires when this field hide.
18625              * @param {Roo.bootstrap.DateField} this
18626              * @param {Mixed} date The date value
18627              */
18628             hide : true,
18629             /**
18630              * @event select
18631              * Fires when select a date.
18632              * @param {Roo.bootstrap.DateField} this
18633              * @param {Mixed} date The date value
18634              */
18635             select : true
18636         });
18637 };
18638
18639 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18640     
18641     /**
18642      * @cfg {String} format
18643      * The default time format string which can be overriden for localization support.  The format must be
18644      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18645      */
18646     format : "H:i",
18647        
18648     onRender: function(ct, position)
18649     {
18650         
18651         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18652                 
18653         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18654         
18655         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18656         
18657         this.pop = this.picker().select('>.datepicker-time',true).first();
18658         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18659         
18660         this.picker().on('mousedown', this.onMousedown, this);
18661         this.picker().on('click', this.onClick, this);
18662         
18663         this.picker().addClass('datepicker-dropdown');
18664     
18665         this.fillTime();
18666         this.update();
18667             
18668         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18669         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18670         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18671         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18672         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18673         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18674
18675     },
18676     
18677     fireKey: function(e){
18678         if (!this.picker().isVisible()){
18679             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18680                 this.show();
18681             }
18682             return;
18683         }
18684
18685         e.preventDefault();
18686         
18687         switch(e.keyCode){
18688             case 27: // escape
18689                 this.hide();
18690                 break;
18691             case 37: // left
18692             case 39: // right
18693                 this.onTogglePeriod();
18694                 break;
18695             case 38: // up
18696                 this.onIncrementMinutes();
18697                 break;
18698             case 40: // down
18699                 this.onDecrementMinutes();
18700                 break;
18701             case 13: // enter
18702             case 9: // tab
18703                 this.setTime();
18704                 break;
18705         }
18706     },
18707     
18708     onClick: function(e) {
18709         e.stopPropagation();
18710         e.preventDefault();
18711     },
18712     
18713     picker : function()
18714     {
18715         return this.el.select('.datepicker', true).first();
18716     },
18717     
18718     fillTime: function()
18719     {    
18720         var time = this.pop.select('tbody', true).first();
18721         
18722         time.dom.innerHTML = '';
18723         
18724         time.createChild({
18725             tag: 'tr',
18726             cn: [
18727                 {
18728                     tag: 'td',
18729                     cn: [
18730                         {
18731                             tag: 'a',
18732                             href: '#',
18733                             cls: 'btn',
18734                             cn: [
18735                                 {
18736                                     tag: 'span',
18737                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18738                                 }
18739                             ]
18740                         } 
18741                     ]
18742                 },
18743                 {
18744                     tag: 'td',
18745                     cls: 'separator'
18746                 },
18747                 {
18748                     tag: 'td',
18749                     cn: [
18750                         {
18751                             tag: 'a',
18752                             href: '#',
18753                             cls: 'btn',
18754                             cn: [
18755                                 {
18756                                     tag: 'span',
18757                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18758                                 }
18759                             ]
18760                         }
18761                     ]
18762                 },
18763                 {
18764                     tag: 'td',
18765                     cls: 'separator'
18766                 }
18767             ]
18768         });
18769         
18770         time.createChild({
18771             tag: 'tr',
18772             cn: [
18773                 {
18774                     tag: 'td',
18775                     cn: [
18776                         {
18777                             tag: 'span',
18778                             cls: 'timepicker-hour',
18779                             html: '00'
18780                         }  
18781                     ]
18782                 },
18783                 {
18784                     tag: 'td',
18785                     cls: 'separator',
18786                     html: ':'
18787                 },
18788                 {
18789                     tag: 'td',
18790                     cn: [
18791                         {
18792                             tag: 'span',
18793                             cls: 'timepicker-minute',
18794                             html: '00'
18795                         }  
18796                     ]
18797                 },
18798                 {
18799                     tag: 'td',
18800                     cls: 'separator'
18801                 },
18802                 {
18803                     tag: 'td',
18804                     cn: [
18805                         {
18806                             tag: 'button',
18807                             type: 'button',
18808                             cls: 'btn btn-primary period',
18809                             html: 'AM'
18810                             
18811                         }
18812                     ]
18813                 }
18814             ]
18815         });
18816         
18817         time.createChild({
18818             tag: 'tr',
18819             cn: [
18820                 {
18821                     tag: 'td',
18822                     cn: [
18823                         {
18824                             tag: 'a',
18825                             href: '#',
18826                             cls: 'btn',
18827                             cn: [
18828                                 {
18829                                     tag: 'span',
18830                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18831                                 }
18832                             ]
18833                         }
18834                     ]
18835                 },
18836                 {
18837                     tag: 'td',
18838                     cls: 'separator'
18839                 },
18840                 {
18841                     tag: 'td',
18842                     cn: [
18843                         {
18844                             tag: 'a',
18845                             href: '#',
18846                             cls: 'btn',
18847                             cn: [
18848                                 {
18849                                     tag: 'span',
18850                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18851                                 }
18852                             ]
18853                         }
18854                     ]
18855                 },
18856                 {
18857                     tag: 'td',
18858                     cls: 'separator'
18859                 }
18860             ]
18861         });
18862         
18863     },
18864     
18865     update: function()
18866     {
18867         
18868         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18869         
18870         this.fill();
18871     },
18872     
18873     fill: function() 
18874     {
18875         var hours = this.time.getHours();
18876         var minutes = this.time.getMinutes();
18877         var period = 'AM';
18878         
18879         if(hours > 11){
18880             period = 'PM';
18881         }
18882         
18883         if(hours == 0){
18884             hours = 12;
18885         }
18886         
18887         
18888         if(hours > 12){
18889             hours = hours - 12;
18890         }
18891         
18892         if(hours < 10){
18893             hours = '0' + hours;
18894         }
18895         
18896         if(minutes < 10){
18897             minutes = '0' + minutes;
18898         }
18899         
18900         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18901         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18902         this.pop.select('button', true).first().dom.innerHTML = period;
18903         
18904     },
18905     
18906     place: function()
18907     {   
18908         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18909         
18910         var cls = ['bottom'];
18911         
18912         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18913             cls.pop();
18914             cls.push('top');
18915         }
18916         
18917         cls.push('right');
18918         
18919         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18920             cls.pop();
18921             cls.push('left');
18922         }
18923         
18924         this.picker().addClass(cls.join('-'));
18925         
18926         var _this = this;
18927         
18928         Roo.each(cls, function(c){
18929             if(c == 'bottom'){
18930                 _this.picker().setTop(_this.inputEl().getHeight());
18931                 return;
18932             }
18933             if(c == 'top'){
18934                 _this.picker().setTop(0 - _this.picker().getHeight());
18935                 return;
18936             }
18937             
18938             if(c == 'left'){
18939                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18940                 return;
18941             }
18942             if(c == 'right'){
18943                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18944                 return;
18945             }
18946         });
18947         
18948     },
18949   
18950     onFocus : function()
18951     {
18952         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18953         this.show();
18954     },
18955     
18956     onBlur : function()
18957     {
18958         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18959         this.hide();
18960     },
18961     
18962     show : function()
18963     {
18964         this.picker().show();
18965         this.pop.show();
18966         this.update();
18967         this.place();
18968         
18969         this.fireEvent('show', this, this.date);
18970     },
18971     
18972     hide : function()
18973     {
18974         this.picker().hide();
18975         this.pop.hide();
18976         
18977         this.fireEvent('hide', this, this.date);
18978     },
18979     
18980     setTime : function()
18981     {
18982         this.hide();
18983         this.setValue(this.time.format(this.format));
18984         
18985         this.fireEvent('select', this, this.date);
18986         
18987         
18988     },
18989     
18990     onMousedown: function(e){
18991         e.stopPropagation();
18992         e.preventDefault();
18993     },
18994     
18995     onIncrementHours: function()
18996     {
18997         Roo.log('onIncrementHours');
18998         this.time = this.time.add(Date.HOUR, 1);
18999         this.update();
19000         
19001     },
19002     
19003     onDecrementHours: function()
19004     {
19005         Roo.log('onDecrementHours');
19006         this.time = this.time.add(Date.HOUR, -1);
19007         this.update();
19008     },
19009     
19010     onIncrementMinutes: function()
19011     {
19012         Roo.log('onIncrementMinutes');
19013         this.time = this.time.add(Date.MINUTE, 1);
19014         this.update();
19015     },
19016     
19017     onDecrementMinutes: function()
19018     {
19019         Roo.log('onDecrementMinutes');
19020         this.time = this.time.add(Date.MINUTE, -1);
19021         this.update();
19022     },
19023     
19024     onTogglePeriod: function()
19025     {
19026         Roo.log('onTogglePeriod');
19027         this.time = this.time.add(Date.HOUR, 12);
19028         this.update();
19029     }
19030     
19031    
19032 });
19033
19034 Roo.apply(Roo.bootstrap.TimeField,  {
19035     
19036     content : {
19037         tag: 'tbody',
19038         cn: [
19039             {
19040                 tag: 'tr',
19041                 cn: [
19042                 {
19043                     tag: 'td',
19044                     colspan: '7'
19045                 }
19046                 ]
19047             }
19048         ]
19049     },
19050     
19051     footer : {
19052         tag: 'tfoot',
19053         cn: [
19054             {
19055                 tag: 'tr',
19056                 cn: [
19057                 {
19058                     tag: 'th',
19059                     colspan: '7',
19060                     cls: '',
19061                     cn: [
19062                         {
19063                             tag: 'button',
19064                             cls: 'btn btn-info ok',
19065                             html: 'OK'
19066                         }
19067                     ]
19068                 }
19069
19070                 ]
19071             }
19072         ]
19073     }
19074 });
19075
19076 Roo.apply(Roo.bootstrap.TimeField,  {
19077   
19078     template : {
19079         tag: 'div',
19080         cls: 'datepicker dropdown-menu',
19081         cn: [
19082             {
19083                 tag: 'div',
19084                 cls: 'datepicker-time',
19085                 cn: [
19086                 {
19087                     tag: 'table',
19088                     cls: 'table-condensed',
19089                     cn:[
19090                     Roo.bootstrap.TimeField.content,
19091                     Roo.bootstrap.TimeField.footer
19092                     ]
19093                 }
19094                 ]
19095             }
19096         ]
19097     }
19098 });
19099
19100  
19101
19102  /*
19103  * - LGPL
19104  *
19105  * MonthField
19106  * 
19107  */
19108
19109 /**
19110  * @class Roo.bootstrap.MonthField
19111  * @extends Roo.bootstrap.Input
19112  * Bootstrap MonthField class
19113  * 
19114  * @cfg {String} language default en
19115  * 
19116  * @constructor
19117  * Create a new MonthField
19118  * @param {Object} config The config object
19119  */
19120
19121 Roo.bootstrap.MonthField = function(config){
19122     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19123     
19124     this.addEvents({
19125         /**
19126          * @event show
19127          * Fires when this field show.
19128          * @param {Roo.bootstrap.MonthField} this
19129          * @param {Mixed} date The date value
19130          */
19131         show : true,
19132         /**
19133          * @event show
19134          * Fires when this field hide.
19135          * @param {Roo.bootstrap.MonthField} this
19136          * @param {Mixed} date The date value
19137          */
19138         hide : true,
19139         /**
19140          * @event select
19141          * Fires when select a date.
19142          * @param {Roo.bootstrap.MonthField} this
19143          * @param {String} oldvalue The old value
19144          * @param {String} newvalue The new value
19145          */
19146         select : true
19147     });
19148 };
19149
19150 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19151     
19152     onRender: function(ct, position)
19153     {
19154         
19155         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19156         
19157         this.language = this.language || 'en';
19158         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19159         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19160         
19161         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19162         this.isInline = false;
19163         this.isInput = true;
19164         this.component = this.el.select('.add-on', true).first() || false;
19165         this.component = (this.component && this.component.length === 0) ? false : this.component;
19166         this.hasInput = this.component && this.inputEL().length;
19167         
19168         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19169         
19170         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19171         
19172         this.picker().on('mousedown', this.onMousedown, this);
19173         this.picker().on('click', this.onClick, this);
19174         
19175         this.picker().addClass('datepicker-dropdown');
19176         
19177         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19178             v.setStyle('width', '189px');
19179         });
19180         
19181         this.fillMonths();
19182         
19183         this.update();
19184         
19185         if(this.isInline) {
19186             this.show();
19187         }
19188         
19189     },
19190     
19191     setValue: function(v, suppressEvent)
19192     {   
19193         var o = this.getValue();
19194         
19195         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19196         
19197         this.update();
19198
19199         if(suppressEvent !== true){
19200             this.fireEvent('select', this, o, v);
19201         }
19202         
19203     },
19204     
19205     getValue: function()
19206     {
19207         return this.value;
19208     },
19209     
19210     onClick: function(e) 
19211     {
19212         e.stopPropagation();
19213         e.preventDefault();
19214         
19215         var target = e.getTarget();
19216         
19217         if(target.nodeName.toLowerCase() === 'i'){
19218             target = Roo.get(target).dom.parentNode;
19219         }
19220         
19221         var nodeName = target.nodeName;
19222         var className = target.className;
19223         var html = target.innerHTML;
19224         
19225         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19226             return;
19227         }
19228         
19229         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19230         
19231         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19232         
19233         this.hide();
19234                         
19235     },
19236     
19237     picker : function()
19238     {
19239         return this.pickerEl;
19240     },
19241     
19242     fillMonths: function()
19243     {    
19244         var i = 0;
19245         var months = this.picker().select('>.datepicker-months td', true).first();
19246         
19247         months.dom.innerHTML = '';
19248         
19249         while (i < 12) {
19250             var month = {
19251                 tag: 'span',
19252                 cls: 'month',
19253                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19254             };
19255             
19256             months.createChild(month);
19257         }
19258         
19259     },
19260     
19261     update: function()
19262     {
19263         var _this = this;
19264         
19265         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19266             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19267         }
19268         
19269         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19270             e.removeClass('active');
19271             
19272             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19273                 e.addClass('active');
19274             }
19275         })
19276     },
19277     
19278     place: function()
19279     {
19280         if(this.isInline) {
19281             return;
19282         }
19283         
19284         this.picker().removeClass(['bottom', 'top']);
19285         
19286         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19287             /*
19288              * place to the top of element!
19289              *
19290              */
19291             
19292             this.picker().addClass('top');
19293             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19294             
19295             return;
19296         }
19297         
19298         this.picker().addClass('bottom');
19299         
19300         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19301     },
19302     
19303     onFocus : function()
19304     {
19305         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19306         this.show();
19307     },
19308     
19309     onBlur : function()
19310     {
19311         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19312         
19313         var d = this.inputEl().getValue();
19314         
19315         this.setValue(d);
19316                 
19317         this.hide();
19318     },
19319     
19320     show : function()
19321     {
19322         this.picker().show();
19323         this.picker().select('>.datepicker-months', true).first().show();
19324         this.update();
19325         this.place();
19326         
19327         this.fireEvent('show', this, this.date);
19328     },
19329     
19330     hide : function()
19331     {
19332         if(this.isInline) {
19333             return;
19334         }
19335         this.picker().hide();
19336         this.fireEvent('hide', this, this.date);
19337         
19338     },
19339     
19340     onMousedown: function(e)
19341     {
19342         e.stopPropagation();
19343         e.preventDefault();
19344     },
19345     
19346     keyup: function(e)
19347     {
19348         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19349         this.update();
19350     },
19351
19352     fireKey: function(e)
19353     {
19354         if (!this.picker().isVisible()){
19355             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19356                 this.show();
19357             }
19358             return;
19359         }
19360         
19361         var dir;
19362         
19363         switch(e.keyCode){
19364             case 27: // escape
19365                 this.hide();
19366                 e.preventDefault();
19367                 break;
19368             case 37: // left
19369             case 39: // right
19370                 dir = e.keyCode == 37 ? -1 : 1;
19371                 
19372                 this.vIndex = this.vIndex + dir;
19373                 
19374                 if(this.vIndex < 0){
19375                     this.vIndex = 0;
19376                 }
19377                 
19378                 if(this.vIndex > 11){
19379                     this.vIndex = 11;
19380                 }
19381                 
19382                 if(isNaN(this.vIndex)){
19383                     this.vIndex = 0;
19384                 }
19385                 
19386                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19387                 
19388                 break;
19389             case 38: // up
19390             case 40: // down
19391                 
19392                 dir = e.keyCode == 38 ? -1 : 1;
19393                 
19394                 this.vIndex = this.vIndex + dir * 4;
19395                 
19396                 if(this.vIndex < 0){
19397                     this.vIndex = 0;
19398                 }
19399                 
19400                 if(this.vIndex > 11){
19401                     this.vIndex = 11;
19402                 }
19403                 
19404                 if(isNaN(this.vIndex)){
19405                     this.vIndex = 0;
19406                 }
19407                 
19408                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19409                 break;
19410                 
19411             case 13: // enter
19412                 
19413                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19414                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19415                 }
19416                 
19417                 this.hide();
19418                 e.preventDefault();
19419                 break;
19420             case 9: // tab
19421                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19422                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19423                 }
19424                 this.hide();
19425                 break;
19426             case 16: // shift
19427             case 17: // ctrl
19428             case 18: // alt
19429                 break;
19430             default :
19431                 this.hide();
19432                 
19433         }
19434     },
19435     
19436     remove: function() 
19437     {
19438         this.picker().remove();
19439     }
19440    
19441 });
19442
19443 Roo.apply(Roo.bootstrap.MonthField,  {
19444     
19445     content : {
19446         tag: 'tbody',
19447         cn: [
19448         {
19449             tag: 'tr',
19450             cn: [
19451             {
19452                 tag: 'td',
19453                 colspan: '7'
19454             }
19455             ]
19456         }
19457         ]
19458     },
19459     
19460     dates:{
19461         en: {
19462             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19463             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19464         }
19465     }
19466 });
19467
19468 Roo.apply(Roo.bootstrap.MonthField,  {
19469   
19470     template : {
19471         tag: 'div',
19472         cls: 'datepicker dropdown-menu roo-dynamic',
19473         cn: [
19474             {
19475                 tag: 'div',
19476                 cls: 'datepicker-months',
19477                 cn: [
19478                 {
19479                     tag: 'table',
19480                     cls: 'table-condensed',
19481                     cn:[
19482                         Roo.bootstrap.DateField.content
19483                     ]
19484                 }
19485                 ]
19486             }
19487         ]
19488     }
19489 });
19490
19491  
19492
19493  
19494  /*
19495  * - LGPL
19496  *
19497  * CheckBox
19498  * 
19499  */
19500
19501 /**
19502  * @class Roo.bootstrap.CheckBox
19503  * @extends Roo.bootstrap.Input
19504  * Bootstrap CheckBox class
19505  * 
19506  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19507  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19508  * @cfg {String} boxLabel The text that appears beside the checkbox
19509  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19510  * @cfg {Boolean} checked initnal the element
19511  * @cfg {Boolean} inline inline the element (default false)
19512  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19513  * 
19514  * @constructor
19515  * Create a new CheckBox
19516  * @param {Object} config The config object
19517  */
19518
19519 Roo.bootstrap.CheckBox = function(config){
19520     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19521    
19522     this.addEvents({
19523         /**
19524         * @event check
19525         * Fires when the element is checked or unchecked.
19526         * @param {Roo.bootstrap.CheckBox} this This input
19527         * @param {Boolean} checked The new checked value
19528         */
19529        check : true
19530     });
19531     
19532 };
19533
19534 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19535   
19536     inputType: 'checkbox',
19537     inputValue: 1,
19538     valueOff: 0,
19539     boxLabel: false,
19540     checked: false,
19541     weight : false,
19542     inline: false,
19543     
19544     getAutoCreate : function()
19545     {
19546         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19547         
19548         var id = Roo.id();
19549         
19550         var cfg = {};
19551         
19552         cfg.cls = 'form-group ' + this.inputType; //input-group
19553         
19554         if(this.inline){
19555             cfg.cls += ' ' + this.inputType + '-inline';
19556         }
19557         
19558         var input =  {
19559             tag: 'input',
19560             id : id,
19561             type : this.inputType,
19562             value : this.inputValue,
19563             cls : 'roo-' + this.inputType, //'form-box',
19564             placeholder : this.placeholder || ''
19565             
19566         };
19567         
19568         if(this.inputType != 'radio'){
19569             var hidden =  {
19570                 tag: 'input',
19571                 type : 'hidden',
19572                 cls : 'roo-hidden-value',
19573                 value : this.checked ? this.valueOff : this.inputValue
19574             };
19575         }
19576         
19577             
19578         if (this.weight) { // Validity check?
19579             cfg.cls += " " + this.inputType + "-" + this.weight;
19580         }
19581         
19582         if (this.disabled) {
19583             input.disabled=true;
19584         }
19585         
19586         if(this.checked){
19587             input.checked = this.checked;
19588             
19589         }
19590         
19591         
19592         if (this.name) {
19593             
19594             input.name = this.name;
19595             
19596             if(this.inputType != 'radio'){
19597                 hidden.name = this.name;
19598                 input.name = '_hidden_' + this.name;
19599             }
19600         }
19601         
19602         if (this.size) {
19603             input.cls += ' input-' + this.size;
19604         }
19605         
19606         var settings=this;
19607         
19608         ['xs','sm','md','lg'].map(function(size){
19609             if (settings[size]) {
19610                 cfg.cls += ' col-' + size + '-' + settings[size];
19611             }
19612         });
19613         
19614         var inputblock = input;
19615          
19616         if (this.before || this.after) {
19617             
19618             inputblock = {
19619                 cls : 'input-group',
19620                 cn :  [] 
19621             };
19622             
19623             if (this.before) {
19624                 inputblock.cn.push({
19625                     tag :'span',
19626                     cls : 'input-group-addon',
19627                     html : this.before
19628                 });
19629             }
19630             
19631             inputblock.cn.push(input);
19632             
19633             if(this.inputType != 'radio'){
19634                 inputblock.cn.push(hidden);
19635             }
19636             
19637             if (this.after) {
19638                 inputblock.cn.push({
19639                     tag :'span',
19640                     cls : 'input-group-addon',
19641                     html : this.after
19642                 });
19643             }
19644             
19645         }
19646         
19647         if (align ==='left' && this.fieldLabel.length) {
19648 //                Roo.log("left and has label");
19649                 cfg.cn = [
19650                     
19651                     {
19652                         tag: 'label',
19653                         'for' :  id,
19654                         cls : 'control-label col-md-' + this.labelWidth,
19655                         html : this.fieldLabel
19656                         
19657                     },
19658                     {
19659                         cls : "col-md-" + (12 - this.labelWidth), 
19660                         cn: [
19661                             inputblock
19662                         ]
19663                     }
19664                     
19665                 ];
19666         } else if ( this.fieldLabel.length) {
19667 //                Roo.log(" label");
19668                 cfg.cn = [
19669                    
19670                     {
19671                         tag: this.boxLabel ? 'span' : 'label',
19672                         'for': id,
19673                         cls: 'control-label box-input-label',
19674                         //cls : 'input-group-addon',
19675                         html : this.fieldLabel
19676                         
19677                     },
19678                     
19679                     inputblock
19680                     
19681                 ];
19682
19683         } else {
19684             
19685 //                Roo.log(" no label && no align");
19686                 cfg.cn = [  inputblock ] ;
19687                 
19688                 
19689         }
19690         
19691         if(this.boxLabel){
19692              var boxLabelCfg = {
19693                 tag: 'label',
19694                 //'for': id, // box label is handled by onclick - so no for...
19695                 cls: 'box-label',
19696                 html: this.boxLabel
19697             };
19698             
19699             if(this.tooltip){
19700                 boxLabelCfg.tooltip = this.tooltip;
19701             }
19702              
19703             cfg.cn.push(boxLabelCfg);
19704         }
19705         
19706         if(this.inputType != 'radio'){
19707             cfg.cn.push(hidden);
19708         }
19709         
19710         return cfg;
19711         
19712     },
19713     
19714     /**
19715      * return the real input element.
19716      */
19717     inputEl: function ()
19718     {
19719         return this.el.select('input.roo-' + this.inputType,true).first();
19720     },
19721     hiddenEl: function ()
19722     {
19723         return this.el.select('input.roo-hidden-value',true).first();
19724     },
19725     
19726     labelEl: function()
19727     {
19728         return this.el.select('label.control-label',true).first();
19729     },
19730     /* depricated... */
19731     
19732     label: function()
19733     {
19734         return this.labelEl();
19735     },
19736     
19737     boxLabelEl: function()
19738     {
19739         return this.el.select('label.box-label',true).first();
19740     },
19741     
19742     initEvents : function()
19743     {
19744 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19745         
19746         this.inputEl().on('click', this.onClick,  this);
19747         
19748         if (this.boxLabel) { 
19749             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19750         }
19751         
19752         this.startValue = this.getValue();
19753         
19754         if(this.groupId){
19755             Roo.bootstrap.CheckBox.register(this);
19756         }
19757     },
19758     
19759     onClick : function()
19760     {   
19761         this.setChecked(!this.checked);
19762     },
19763     
19764     setChecked : function(state,suppressEvent)
19765     {
19766         this.startValue = this.getValue();
19767         
19768         if(this.inputType == 'radio'){
19769             
19770             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19771                 e.dom.checked = false;
19772             });
19773             
19774             this.inputEl().dom.checked = true;
19775             
19776             this.inputEl().dom.value = this.inputValue;
19777             
19778             if(suppressEvent !== true){
19779                 this.fireEvent('check', this, true);
19780             }
19781             
19782             this.validate();
19783             
19784             return;
19785         }
19786         
19787         this.checked = state;
19788         
19789         this.inputEl().dom.checked = state;
19790         
19791         
19792         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19793         
19794         if(suppressEvent !== true){
19795             this.fireEvent('check', this, state);
19796         }
19797         
19798         this.validate();
19799     },
19800     
19801     getValue : function()
19802     {
19803         if(this.inputType == 'radio'){
19804             return this.getGroupValue();
19805         }
19806         
19807         return this.hiddenEl().dom.value;
19808         
19809     },
19810     
19811     getGroupValue : function()
19812     {
19813         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19814             return '';
19815         }
19816         
19817         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19818     },
19819     
19820     setValue : function(v,suppressEvent)
19821     {
19822         if(this.inputType == 'radio'){
19823             this.setGroupValue(v, suppressEvent);
19824             return;
19825         }
19826         
19827         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19828         
19829         this.validate();
19830     },
19831     
19832     setGroupValue : function(v, suppressEvent)
19833     {
19834         this.startValue = this.getValue();
19835         
19836         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19837             e.dom.checked = false;
19838             
19839             if(e.dom.value == v){
19840                 e.dom.checked = true;
19841             }
19842         });
19843         
19844         if(suppressEvent !== true){
19845             this.fireEvent('check', this, true);
19846         }
19847
19848         this.validate();
19849         
19850         return;
19851     },
19852     
19853     validate : function()
19854     {
19855         if(
19856                 this.disabled || 
19857                 (this.inputType == 'radio' && this.validateRadio()) ||
19858                 (this.inputType == 'checkbox' && this.validateCheckbox())
19859         ){
19860             this.markValid();
19861             return true;
19862         }
19863         
19864         this.markInvalid();
19865         return false;
19866     },
19867     
19868     validateRadio : function()
19869     {
19870         if(this.allowBlank){
19871             return true;
19872         }
19873         
19874         var valid = false;
19875         
19876         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19877             if(!e.dom.checked){
19878                 return;
19879             }
19880             
19881             valid = true;
19882             
19883             return false;
19884         });
19885         
19886         return valid;
19887     },
19888     
19889     validateCheckbox : function()
19890     {
19891         if(!this.groupId){
19892             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19893         }
19894         
19895         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19896         
19897         if(!group){
19898             return false;
19899         }
19900         
19901         var r = false;
19902         
19903         for(var i in group){
19904             if(r){
19905                 break;
19906             }
19907             
19908             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19909         }
19910         
19911         return r;
19912     },
19913     
19914     /**
19915      * Mark this field as valid
19916      */
19917     markValid : function()
19918     {
19919         if(this.allowBlank){
19920             return;
19921         }
19922         
19923         var _this = this;
19924         
19925         this.fireEvent('valid', this);
19926         
19927         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19928         
19929         if(this.groupId){
19930             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19931         }
19932         
19933         if(label){
19934             label.markValid();
19935         }
19936
19937         if(this.inputType == 'radio'){
19938             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19939                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19940                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19941             });
19942             
19943             return;
19944         }
19945         
19946         if(!this.groupId){
19947             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19948             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19949             return;
19950         }
19951         
19952         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19953             
19954         if(!group){
19955             return;
19956         }
19957         
19958         for(var i in group){
19959             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19960             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19961         }
19962     },
19963     
19964      /**
19965      * Mark this field as invalid
19966      * @param {String} msg The validation message
19967      */
19968     markInvalid : function(msg)
19969     {
19970         if(this.allowBlank){
19971             return;
19972         }
19973         
19974         var _this = this;
19975         
19976         this.fireEvent('invalid', this, msg);
19977         
19978         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19979         
19980         if(this.groupId){
19981             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19982         }
19983         
19984         if(label){
19985             label.markInvalid();
19986         }
19987             
19988         if(this.inputType == 'radio'){
19989             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19990                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19991                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19992             });
19993             
19994             return;
19995         }
19996         
19997         if(!this.groupId){
19998             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19999             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20000             return;
20001         }
20002         
20003         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20004         
20005         if(!group){
20006             return;
20007         }
20008         
20009         for(var i in group){
20010             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20011             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20012         }
20013         
20014     },
20015     
20016     disable : function()
20017     {
20018         if(this.inputType != 'radio'){
20019             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20020             return;
20021         }
20022         
20023         var _this = this;
20024         
20025         if(this.rendered){
20026             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20027                 _this.getActionEl().addClass(this.disabledClass);
20028                 e.dom.disabled = true;
20029             });
20030         }
20031         
20032         this.disabled = true;
20033         this.fireEvent("disable", this);
20034         return this;
20035     },
20036
20037     enable : function()
20038     {
20039         if(this.inputType != 'radio'){
20040             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20041             return;
20042         }
20043         
20044         var _this = this;
20045         
20046         if(this.rendered){
20047             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20048                 _this.getActionEl().removeClass(this.disabledClass);
20049                 e.dom.disabled = false;
20050             });
20051         }
20052         
20053         this.disabled = false;
20054         this.fireEvent("enable", this);
20055         return this;
20056     }
20057
20058 });
20059
20060 Roo.apply(Roo.bootstrap.CheckBox, {
20061     
20062     groups: {},
20063     
20064      /**
20065     * register a CheckBox Group
20066     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20067     */
20068     register : function(checkbox)
20069     {
20070         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20071             this.groups[checkbox.groupId] = {};
20072         }
20073         
20074         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20075             return;
20076         }
20077         
20078         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20079         
20080     },
20081     /**
20082     * fetch a CheckBox Group based on the group ID
20083     * @param {string} the group ID
20084     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20085     */
20086     get: function(groupId) {
20087         if (typeof(this.groups[groupId]) == 'undefined') {
20088             return false;
20089         }
20090         
20091         return this.groups[groupId] ;
20092     }
20093     
20094     
20095 });
20096 /*
20097  * - LGPL
20098  *
20099  * Radio
20100  *
20101  *
20102  * not inline
20103  *<div class="radio">
20104   <label>
20105     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20106     Option one is this and that&mdash;be sure to include why it's great
20107   </label>
20108 </div>
20109  *
20110  *
20111  *inline
20112  *<span>
20113  *<label class="radio-inline">fieldLabel</label>
20114  *<label class="radio-inline">
20115   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20116 </label>
20117 <span>
20118  *
20119  *
20120  */
20121
20122 /**
20123  * @class Roo.bootstrap.Radio
20124  * @extends Roo.bootstrap.CheckBox
20125  * Bootstrap Radio class
20126
20127  * @constructor
20128  * Create a new Radio
20129  * @param {Object} config The config object
20130  */
20131
20132 Roo.bootstrap.Radio = function(config){
20133     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20134
20135 };
20136
20137 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20138
20139     inputType: 'radio',
20140     inputValue: '',
20141     valueOff: '',
20142
20143     getAutoCreate : function()
20144     {
20145         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20146         align = align || 'left'; // default...
20147
20148
20149
20150         var id = Roo.id();
20151
20152         var cfg = {
20153                 tag : this.inline ? 'span' : 'div',
20154                 cls : 'form-group',
20155                 cn : []
20156         };
20157
20158         var inline = this.inline ? ' radio-inline' : '';
20159
20160         var lbl = {
20161                 tag: 'label' ,
20162                 // does not need for, as we wrap the input with it..
20163                 'for' : id,
20164                 cls : 'control-label box-label' + inline,
20165                 cn : []
20166         };
20167         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20168
20169         var fieldLabel = {
20170             tag: 'label' ,
20171             //cls : 'control-label' + inline,
20172             html : this.fieldLabel,
20173             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20174         };
20175
20176         var input =  {
20177             tag: 'input',
20178             id : id,
20179             type : this.inputType,
20180             //value : (!this.checked) ? this.valueOff : this.inputValue,
20181             value : this.inputValue,
20182             cls : 'roo-radio',
20183             placeholder : this.placeholder || '' // ?? needed????
20184
20185         };
20186         if (this.weight) { // Validity check?
20187             input.cls += " radio-" + this.weight;
20188         }
20189         if (this.disabled) {
20190             input.disabled=true;
20191         }
20192
20193         if(this.checked){
20194             input.checked = this.checked;
20195         }
20196
20197         if (this.name) {
20198             input.name = this.name;
20199         }
20200
20201         if (this.size) {
20202             input.cls += ' input-' + this.size;
20203         }
20204
20205         //?? can span's inline have a width??
20206
20207         var settings=this;
20208         ['xs','sm','md','lg'].map(function(size){
20209             if (settings[size]) {
20210                 cfg.cls += ' col-' + size + '-' + settings[size];
20211             }
20212         });
20213
20214         var inputblock = input;
20215
20216         if (this.before || this.after) {
20217
20218             inputblock = {
20219                 cls : 'input-group',
20220                 tag : 'span',
20221                 cn :  []
20222             };
20223             if (this.before) {
20224                 inputblock.cn.push({
20225                     tag :'span',
20226                     cls : 'input-group-addon',
20227                     html : this.before
20228                 });
20229             }
20230             inputblock.cn.push(input);
20231             if (this.after) {
20232                 inputblock.cn.push({
20233                     tag :'span',
20234                     cls : 'input-group-addon',
20235                     html : this.after
20236                 });
20237             }
20238
20239         };
20240
20241
20242         if (this.fieldLabel && this.fieldLabel.length) {
20243             cfg.cn.push(fieldLabel);
20244         }
20245
20246         // normal bootstrap puts the input inside the label.
20247         // however with our styled version - it has to go after the input.
20248
20249         //lbl.cn.push(inputblock);
20250
20251         var lblwrap =  {
20252             tag: 'span',
20253             cls: 'radio' + inline,
20254             cn: [
20255                 inputblock,
20256                 lbl
20257             ]
20258         };
20259
20260         cfg.cn.push( lblwrap);
20261
20262         if(this.boxLabel){
20263             lbl.cn.push({
20264                 tag: 'span',
20265                 html: this.boxLabel
20266             })
20267         }
20268
20269
20270         return cfg;
20271
20272     },
20273
20274     initEvents : function()
20275     {
20276 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20277
20278         this.inputEl().on('click', this.onClick,  this);
20279         if (this.boxLabel) {
20280             //Roo.log('find label');
20281             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20282         }
20283
20284     },
20285
20286     inputEl: function ()
20287     {
20288         return this.el.select('input.roo-radio',true).first();
20289     },
20290     onClick : function()
20291     {
20292         Roo.log("click");
20293         this.setChecked(true);
20294     },
20295
20296     setChecked : function(state,suppressEvent)
20297     {
20298         if(state){
20299             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20300                 v.dom.checked = false;
20301             });
20302         }
20303         Roo.log(this.inputEl().dom);
20304         this.checked = state;
20305         this.inputEl().dom.checked = state;
20306
20307         if(suppressEvent !== true){
20308             this.fireEvent('check', this, state);
20309         }
20310
20311         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20312
20313     },
20314
20315     getGroupValue : function()
20316     {
20317         var value = '';
20318         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20319             if(v.dom.checked == true){
20320                 value = v.dom.value;
20321             }
20322         });
20323
20324         return value;
20325     },
20326
20327     /**
20328      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20329      * @return {Mixed} value The field value
20330      */
20331     getValue : function(){
20332         return this.getGroupValue();
20333     }
20334
20335 });
20336 //<script type="text/javascript">
20337
20338 /*
20339  * Based  Ext JS Library 1.1.1
20340  * Copyright(c) 2006-2007, Ext JS, LLC.
20341  * LGPL
20342  *
20343  */
20344  
20345 /**
20346  * @class Roo.HtmlEditorCore
20347  * @extends Roo.Component
20348  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20349  *
20350  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20351  */
20352
20353 Roo.HtmlEditorCore = function(config){
20354     
20355     
20356     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20357     
20358     
20359     this.addEvents({
20360         /**
20361          * @event initialize
20362          * Fires when the editor is fully initialized (including the iframe)
20363          * @param {Roo.HtmlEditorCore} this
20364          */
20365         initialize: true,
20366         /**
20367          * @event activate
20368          * Fires when the editor is first receives the focus. Any insertion must wait
20369          * until after this event.
20370          * @param {Roo.HtmlEditorCore} this
20371          */
20372         activate: true,
20373          /**
20374          * @event beforesync
20375          * Fires before the textarea is updated with content from the editor iframe. Return false
20376          * to cancel the sync.
20377          * @param {Roo.HtmlEditorCore} this
20378          * @param {String} html
20379          */
20380         beforesync: true,
20381          /**
20382          * @event beforepush
20383          * Fires before the iframe editor is updated with content from the textarea. Return false
20384          * to cancel the push.
20385          * @param {Roo.HtmlEditorCore} this
20386          * @param {String} html
20387          */
20388         beforepush: true,
20389          /**
20390          * @event sync
20391          * Fires when the textarea is updated with content from the editor iframe.
20392          * @param {Roo.HtmlEditorCore} this
20393          * @param {String} html
20394          */
20395         sync: true,
20396          /**
20397          * @event push
20398          * Fires when the iframe editor is updated with content from the textarea.
20399          * @param {Roo.HtmlEditorCore} this
20400          * @param {String} html
20401          */
20402         push: true,
20403         
20404         /**
20405          * @event editorevent
20406          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20407          * @param {Roo.HtmlEditorCore} this
20408          */
20409         editorevent: true
20410         
20411     });
20412     
20413     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20414     
20415     // defaults : white / black...
20416     this.applyBlacklists();
20417     
20418     
20419     
20420 };
20421
20422
20423 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20424
20425
20426      /**
20427      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20428      */
20429     
20430     owner : false,
20431     
20432      /**
20433      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20434      *                        Roo.resizable.
20435      */
20436     resizable : false,
20437      /**
20438      * @cfg {Number} height (in pixels)
20439      */   
20440     height: 300,
20441    /**
20442      * @cfg {Number} width (in pixels)
20443      */   
20444     width: 500,
20445     
20446     /**
20447      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20448      * 
20449      */
20450     stylesheets: false,
20451     
20452     // id of frame..
20453     frameId: false,
20454     
20455     // private properties
20456     validationEvent : false,
20457     deferHeight: true,
20458     initialized : false,
20459     activated : false,
20460     sourceEditMode : false,
20461     onFocus : Roo.emptyFn,
20462     iframePad:3,
20463     hideMode:'offsets',
20464     
20465     clearUp: true,
20466     
20467     // blacklist + whitelisted elements..
20468     black: false,
20469     white: false,
20470      
20471     
20472
20473     /**
20474      * Protected method that will not generally be called directly. It
20475      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20476      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20477      */
20478     getDocMarkup : function(){
20479         // body styles..
20480         var st = '';
20481         
20482         // inherit styels from page...?? 
20483         if (this.stylesheets === false) {
20484             
20485             Roo.get(document.head).select('style').each(function(node) {
20486                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20487             });
20488             
20489             Roo.get(document.head).select('link').each(function(node) { 
20490                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20491             });
20492             
20493         } else if (!this.stylesheets.length) {
20494                 // simple..
20495                 st = '<style type="text/css">' +
20496                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20497                    '</style>';
20498         } else { 
20499             
20500         }
20501         
20502         st +=  '<style type="text/css">' +
20503             'IMG { cursor: pointer } ' +
20504         '</style>';
20505
20506         
20507         return '<html><head>' + st  +
20508             //<style type="text/css">' +
20509             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20510             //'</style>' +
20511             ' </head><body class="roo-htmleditor-body"></body></html>';
20512     },
20513
20514     // private
20515     onRender : function(ct, position)
20516     {
20517         var _t = this;
20518         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20519         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20520         
20521         
20522         this.el.dom.style.border = '0 none';
20523         this.el.dom.setAttribute('tabIndex', -1);
20524         this.el.addClass('x-hidden hide');
20525         
20526         
20527         
20528         if(Roo.isIE){ // fix IE 1px bogus margin
20529             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20530         }
20531        
20532         
20533         this.frameId = Roo.id();
20534         
20535          
20536         
20537         var iframe = this.owner.wrap.createChild({
20538             tag: 'iframe',
20539             cls: 'form-control', // bootstrap..
20540             id: this.frameId,
20541             name: this.frameId,
20542             frameBorder : 'no',
20543             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20544         }, this.el
20545         );
20546         
20547         
20548         this.iframe = iframe.dom;
20549
20550          this.assignDocWin();
20551         
20552         this.doc.designMode = 'on';
20553        
20554         this.doc.open();
20555         this.doc.write(this.getDocMarkup());
20556         this.doc.close();
20557
20558         
20559         var task = { // must defer to wait for browser to be ready
20560             run : function(){
20561                 //console.log("run task?" + this.doc.readyState);
20562                 this.assignDocWin();
20563                 if(this.doc.body || this.doc.readyState == 'complete'){
20564                     try {
20565                         this.doc.designMode="on";
20566                     } catch (e) {
20567                         return;
20568                     }
20569                     Roo.TaskMgr.stop(task);
20570                     this.initEditor.defer(10, this);
20571                 }
20572             },
20573             interval : 10,
20574             duration: 10000,
20575             scope: this
20576         };
20577         Roo.TaskMgr.start(task);
20578
20579     },
20580
20581     // private
20582     onResize : function(w, h)
20583     {
20584          Roo.log('resize: ' +w + ',' + h );
20585         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20586         if(!this.iframe){
20587             return;
20588         }
20589         if(typeof w == 'number'){
20590             
20591             this.iframe.style.width = w + 'px';
20592         }
20593         if(typeof h == 'number'){
20594             
20595             this.iframe.style.height = h + 'px';
20596             if(this.doc){
20597                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20598             }
20599         }
20600         
20601     },
20602
20603     /**
20604      * Toggles the editor between standard and source edit mode.
20605      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20606      */
20607     toggleSourceEdit : function(sourceEditMode){
20608         
20609         this.sourceEditMode = sourceEditMode === true;
20610         
20611         if(this.sourceEditMode){
20612  
20613             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20614             
20615         }else{
20616             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20617             //this.iframe.className = '';
20618             this.deferFocus();
20619         }
20620         //this.setSize(this.owner.wrap.getSize());
20621         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20622     },
20623
20624     
20625   
20626
20627     /**
20628      * Protected method that will not generally be called directly. If you need/want
20629      * custom HTML cleanup, this is the method you should override.
20630      * @param {String} html The HTML to be cleaned
20631      * return {String} The cleaned HTML
20632      */
20633     cleanHtml : function(html){
20634         html = String(html);
20635         if(html.length > 5){
20636             if(Roo.isSafari){ // strip safari nonsense
20637                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20638             }
20639         }
20640         if(html == '&nbsp;'){
20641             html = '';
20642         }
20643         return html;
20644     },
20645
20646     /**
20647      * HTML Editor -> Textarea
20648      * Protected method that will not generally be called directly. Syncs the contents
20649      * of the editor iframe with the textarea.
20650      */
20651     syncValue : function(){
20652         if(this.initialized){
20653             var bd = (this.doc.body || this.doc.documentElement);
20654             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20655             var html = bd.innerHTML;
20656             if(Roo.isSafari){
20657                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20658                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20659                 if(m && m[1]){
20660                     html = '<div style="'+m[0]+'">' + html + '</div>';
20661                 }
20662             }
20663             html = this.cleanHtml(html);
20664             // fix up the special chars.. normaly like back quotes in word...
20665             // however we do not want to do this with chinese..
20666             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20667                 var cc = b.charCodeAt();
20668                 if (
20669                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20670                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20671                     (cc >= 0xf900 && cc < 0xfb00 )
20672                 ) {
20673                         return b;
20674                 }
20675                 return "&#"+cc+";" 
20676             });
20677             if(this.owner.fireEvent('beforesync', this, html) !== false){
20678                 this.el.dom.value = html;
20679                 this.owner.fireEvent('sync', this, html);
20680             }
20681         }
20682     },
20683
20684     /**
20685      * Protected method that will not generally be called directly. Pushes the value of the textarea
20686      * into the iframe editor.
20687      */
20688     pushValue : function(){
20689         if(this.initialized){
20690             var v = this.el.dom.value.trim();
20691             
20692 //            if(v.length < 1){
20693 //                v = '&#160;';
20694 //            }
20695             
20696             if(this.owner.fireEvent('beforepush', this, v) !== false){
20697                 var d = (this.doc.body || this.doc.documentElement);
20698                 d.innerHTML = v;
20699                 this.cleanUpPaste();
20700                 this.el.dom.value = d.innerHTML;
20701                 this.owner.fireEvent('push', this, v);
20702             }
20703         }
20704     },
20705
20706     // private
20707     deferFocus : function(){
20708         this.focus.defer(10, this);
20709     },
20710
20711     // doc'ed in Field
20712     focus : function(){
20713         if(this.win && !this.sourceEditMode){
20714             this.win.focus();
20715         }else{
20716             this.el.focus();
20717         }
20718     },
20719     
20720     assignDocWin: function()
20721     {
20722         var iframe = this.iframe;
20723         
20724          if(Roo.isIE){
20725             this.doc = iframe.contentWindow.document;
20726             this.win = iframe.contentWindow;
20727         } else {
20728 //            if (!Roo.get(this.frameId)) {
20729 //                return;
20730 //            }
20731 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20732 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20733             
20734             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20735                 return;
20736             }
20737             
20738             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20739             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20740         }
20741     },
20742     
20743     // private
20744     initEditor : function(){
20745         //console.log("INIT EDITOR");
20746         this.assignDocWin();
20747         
20748         
20749         
20750         this.doc.designMode="on";
20751         this.doc.open();
20752         this.doc.write(this.getDocMarkup());
20753         this.doc.close();
20754         
20755         var dbody = (this.doc.body || this.doc.documentElement);
20756         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20757         // this copies styles from the containing element into thsi one..
20758         // not sure why we need all of this..
20759         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20760         
20761         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20762         //ss['background-attachment'] = 'fixed'; // w3c
20763         dbody.bgProperties = 'fixed'; // ie
20764         //Roo.DomHelper.applyStyles(dbody, ss);
20765         Roo.EventManager.on(this.doc, {
20766             //'mousedown': this.onEditorEvent,
20767             'mouseup': this.onEditorEvent,
20768             'dblclick': this.onEditorEvent,
20769             'click': this.onEditorEvent,
20770             'keyup': this.onEditorEvent,
20771             buffer:100,
20772             scope: this
20773         });
20774         if(Roo.isGecko){
20775             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20776         }
20777         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20778             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20779         }
20780         this.initialized = true;
20781
20782         this.owner.fireEvent('initialize', this);
20783         this.pushValue();
20784     },
20785
20786     // private
20787     onDestroy : function(){
20788         
20789         
20790         
20791         if(this.rendered){
20792             
20793             //for (var i =0; i < this.toolbars.length;i++) {
20794             //    // fixme - ask toolbars for heights?
20795             //    this.toolbars[i].onDestroy();
20796            // }
20797             
20798             //this.wrap.dom.innerHTML = '';
20799             //this.wrap.remove();
20800         }
20801     },
20802
20803     // private
20804     onFirstFocus : function(){
20805         
20806         this.assignDocWin();
20807         
20808         
20809         this.activated = true;
20810          
20811     
20812         if(Roo.isGecko){ // prevent silly gecko errors
20813             this.win.focus();
20814             var s = this.win.getSelection();
20815             if(!s.focusNode || s.focusNode.nodeType != 3){
20816                 var r = s.getRangeAt(0);
20817                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20818                 r.collapse(true);
20819                 this.deferFocus();
20820             }
20821             try{
20822                 this.execCmd('useCSS', true);
20823                 this.execCmd('styleWithCSS', false);
20824             }catch(e){}
20825         }
20826         this.owner.fireEvent('activate', this);
20827     },
20828
20829     // private
20830     adjustFont: function(btn){
20831         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20832         //if(Roo.isSafari){ // safari
20833         //    adjust *= 2;
20834        // }
20835         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20836         if(Roo.isSafari){ // safari
20837             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20838             v =  (v < 10) ? 10 : v;
20839             v =  (v > 48) ? 48 : v;
20840             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20841             
20842         }
20843         
20844         
20845         v = Math.max(1, v+adjust);
20846         
20847         this.execCmd('FontSize', v  );
20848     },
20849
20850     onEditorEvent : function(e)
20851     {
20852         this.owner.fireEvent('editorevent', this, e);
20853       //  this.updateToolbar();
20854         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20855     },
20856
20857     insertTag : function(tg)
20858     {
20859         // could be a bit smarter... -> wrap the current selected tRoo..
20860         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20861             
20862             range = this.createRange(this.getSelection());
20863             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20864             wrappingNode.appendChild(range.extractContents());
20865             range.insertNode(wrappingNode);
20866
20867             return;
20868             
20869             
20870             
20871         }
20872         this.execCmd("formatblock",   tg);
20873         
20874     },
20875     
20876     insertText : function(txt)
20877     {
20878         
20879         
20880         var range = this.createRange();
20881         range.deleteContents();
20882                //alert(Sender.getAttribute('label'));
20883                
20884         range.insertNode(this.doc.createTextNode(txt));
20885     } ,
20886     
20887      
20888
20889     /**
20890      * Executes a Midas editor command on the editor document and performs necessary focus and
20891      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20892      * @param {String} cmd The Midas command
20893      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20894      */
20895     relayCmd : function(cmd, value){
20896         this.win.focus();
20897         this.execCmd(cmd, value);
20898         this.owner.fireEvent('editorevent', this);
20899         //this.updateToolbar();
20900         this.owner.deferFocus();
20901     },
20902
20903     /**
20904      * Executes a Midas editor command directly on the editor document.
20905      * For visual commands, you should use {@link #relayCmd} instead.
20906      * <b>This should only be called after the editor is initialized.</b>
20907      * @param {String} cmd The Midas command
20908      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20909      */
20910     execCmd : function(cmd, value){
20911         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20912         this.syncValue();
20913     },
20914  
20915  
20916    
20917     /**
20918      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20919      * to insert tRoo.
20920      * @param {String} text | dom node.. 
20921      */
20922     insertAtCursor : function(text)
20923     {
20924         
20925         
20926         
20927         if(!this.activated){
20928             return;
20929         }
20930         /*
20931         if(Roo.isIE){
20932             this.win.focus();
20933             var r = this.doc.selection.createRange();
20934             if(r){
20935                 r.collapse(true);
20936                 r.pasteHTML(text);
20937                 this.syncValue();
20938                 this.deferFocus();
20939             
20940             }
20941             return;
20942         }
20943         */
20944         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20945             this.win.focus();
20946             
20947             
20948             // from jquery ui (MIT licenced)
20949             var range, node;
20950             var win = this.win;
20951             
20952             if (win.getSelection && win.getSelection().getRangeAt) {
20953                 range = win.getSelection().getRangeAt(0);
20954                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20955                 range.insertNode(node);
20956             } else if (win.document.selection && win.document.selection.createRange) {
20957                 // no firefox support
20958                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20959                 win.document.selection.createRange().pasteHTML(txt);
20960             } else {
20961                 // no firefox support
20962                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20963                 this.execCmd('InsertHTML', txt);
20964             } 
20965             
20966             this.syncValue();
20967             
20968             this.deferFocus();
20969         }
20970     },
20971  // private
20972     mozKeyPress : function(e){
20973         if(e.ctrlKey){
20974             var c = e.getCharCode(), cmd;
20975           
20976             if(c > 0){
20977                 c = String.fromCharCode(c).toLowerCase();
20978                 switch(c){
20979                     case 'b':
20980                         cmd = 'bold';
20981                         break;
20982                     case 'i':
20983                         cmd = 'italic';
20984                         break;
20985                     
20986                     case 'u':
20987                         cmd = 'underline';
20988                         break;
20989                     
20990                     case 'v':
20991                         this.cleanUpPaste.defer(100, this);
20992                         return;
20993                         
20994                 }
20995                 if(cmd){
20996                     this.win.focus();
20997                     this.execCmd(cmd);
20998                     this.deferFocus();
20999                     e.preventDefault();
21000                 }
21001                 
21002             }
21003         }
21004     },
21005
21006     // private
21007     fixKeys : function(){ // load time branching for fastest keydown performance
21008         if(Roo.isIE){
21009             return function(e){
21010                 var k = e.getKey(), r;
21011                 if(k == e.TAB){
21012                     e.stopEvent();
21013                     r = this.doc.selection.createRange();
21014                     if(r){
21015                         r.collapse(true);
21016                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21017                         this.deferFocus();
21018                     }
21019                     return;
21020                 }
21021                 
21022                 if(k == e.ENTER){
21023                     r = this.doc.selection.createRange();
21024                     if(r){
21025                         var target = r.parentElement();
21026                         if(!target || target.tagName.toLowerCase() != 'li'){
21027                             e.stopEvent();
21028                             r.pasteHTML('<br />');
21029                             r.collapse(false);
21030                             r.select();
21031                         }
21032                     }
21033                 }
21034                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21035                     this.cleanUpPaste.defer(100, this);
21036                     return;
21037                 }
21038                 
21039                 
21040             };
21041         }else if(Roo.isOpera){
21042             return function(e){
21043                 var k = e.getKey();
21044                 if(k == e.TAB){
21045                     e.stopEvent();
21046                     this.win.focus();
21047                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21048                     this.deferFocus();
21049                 }
21050                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21051                     this.cleanUpPaste.defer(100, this);
21052                     return;
21053                 }
21054                 
21055             };
21056         }else if(Roo.isSafari){
21057             return function(e){
21058                 var k = e.getKey();
21059                 
21060                 if(k == e.TAB){
21061                     e.stopEvent();
21062                     this.execCmd('InsertText','\t');
21063                     this.deferFocus();
21064                     return;
21065                 }
21066                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21067                     this.cleanUpPaste.defer(100, this);
21068                     return;
21069                 }
21070                 
21071              };
21072         }
21073     }(),
21074     
21075     getAllAncestors: function()
21076     {
21077         var p = this.getSelectedNode();
21078         var a = [];
21079         if (!p) {
21080             a.push(p); // push blank onto stack..
21081             p = this.getParentElement();
21082         }
21083         
21084         
21085         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21086             a.push(p);
21087             p = p.parentNode;
21088         }
21089         a.push(this.doc.body);
21090         return a;
21091     },
21092     lastSel : false,
21093     lastSelNode : false,
21094     
21095     
21096     getSelection : function() 
21097     {
21098         this.assignDocWin();
21099         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21100     },
21101     
21102     getSelectedNode: function() 
21103     {
21104         // this may only work on Gecko!!!
21105         
21106         // should we cache this!!!!
21107         
21108         
21109         
21110          
21111         var range = this.createRange(this.getSelection()).cloneRange();
21112         
21113         if (Roo.isIE) {
21114             var parent = range.parentElement();
21115             while (true) {
21116                 var testRange = range.duplicate();
21117                 testRange.moveToElementText(parent);
21118                 if (testRange.inRange(range)) {
21119                     break;
21120                 }
21121                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21122                     break;
21123                 }
21124                 parent = parent.parentElement;
21125             }
21126             return parent;
21127         }
21128         
21129         // is ancestor a text element.
21130         var ac =  range.commonAncestorContainer;
21131         if (ac.nodeType == 3) {
21132             ac = ac.parentNode;
21133         }
21134         
21135         var ar = ac.childNodes;
21136          
21137         var nodes = [];
21138         var other_nodes = [];
21139         var has_other_nodes = false;
21140         for (var i=0;i<ar.length;i++) {
21141             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21142                 continue;
21143             }
21144             // fullly contained node.
21145             
21146             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21147                 nodes.push(ar[i]);
21148                 continue;
21149             }
21150             
21151             // probably selected..
21152             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21153                 other_nodes.push(ar[i]);
21154                 continue;
21155             }
21156             // outer..
21157             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21158                 continue;
21159             }
21160             
21161             
21162             has_other_nodes = true;
21163         }
21164         if (!nodes.length && other_nodes.length) {
21165             nodes= other_nodes;
21166         }
21167         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21168             return false;
21169         }
21170         
21171         return nodes[0];
21172     },
21173     createRange: function(sel)
21174     {
21175         // this has strange effects when using with 
21176         // top toolbar - not sure if it's a great idea.
21177         //this.editor.contentWindow.focus();
21178         if (typeof sel != "undefined") {
21179             try {
21180                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21181             } catch(e) {
21182                 return this.doc.createRange();
21183             }
21184         } else {
21185             return this.doc.createRange();
21186         }
21187     },
21188     getParentElement: function()
21189     {
21190         
21191         this.assignDocWin();
21192         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21193         
21194         var range = this.createRange(sel);
21195          
21196         try {
21197             var p = range.commonAncestorContainer;
21198             while (p.nodeType == 3) { // text node
21199                 p = p.parentNode;
21200             }
21201             return p;
21202         } catch (e) {
21203             return null;
21204         }
21205     
21206     },
21207     /***
21208      *
21209      * Range intersection.. the hard stuff...
21210      *  '-1' = before
21211      *  '0' = hits..
21212      *  '1' = after.
21213      *         [ -- selected range --- ]
21214      *   [fail]                        [fail]
21215      *
21216      *    basically..
21217      *      if end is before start or  hits it. fail.
21218      *      if start is after end or hits it fail.
21219      *
21220      *   if either hits (but other is outside. - then it's not 
21221      *   
21222      *    
21223      **/
21224     
21225     
21226     // @see http://www.thismuchiknow.co.uk/?p=64.
21227     rangeIntersectsNode : function(range, node)
21228     {
21229         var nodeRange = node.ownerDocument.createRange();
21230         try {
21231             nodeRange.selectNode(node);
21232         } catch (e) {
21233             nodeRange.selectNodeContents(node);
21234         }
21235     
21236         var rangeStartRange = range.cloneRange();
21237         rangeStartRange.collapse(true);
21238     
21239         var rangeEndRange = range.cloneRange();
21240         rangeEndRange.collapse(false);
21241     
21242         var nodeStartRange = nodeRange.cloneRange();
21243         nodeStartRange.collapse(true);
21244     
21245         var nodeEndRange = nodeRange.cloneRange();
21246         nodeEndRange.collapse(false);
21247     
21248         return rangeStartRange.compareBoundaryPoints(
21249                  Range.START_TO_START, nodeEndRange) == -1 &&
21250                rangeEndRange.compareBoundaryPoints(
21251                  Range.START_TO_START, nodeStartRange) == 1;
21252         
21253          
21254     },
21255     rangeCompareNode : function(range, node)
21256     {
21257         var nodeRange = node.ownerDocument.createRange();
21258         try {
21259             nodeRange.selectNode(node);
21260         } catch (e) {
21261             nodeRange.selectNodeContents(node);
21262         }
21263         
21264         
21265         range.collapse(true);
21266     
21267         nodeRange.collapse(true);
21268      
21269         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21270         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21271          
21272         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21273         
21274         var nodeIsBefore   =  ss == 1;
21275         var nodeIsAfter    = ee == -1;
21276         
21277         if (nodeIsBefore && nodeIsAfter) {
21278             return 0; // outer
21279         }
21280         if (!nodeIsBefore && nodeIsAfter) {
21281             return 1; //right trailed.
21282         }
21283         
21284         if (nodeIsBefore && !nodeIsAfter) {
21285             return 2;  // left trailed.
21286         }
21287         // fully contined.
21288         return 3;
21289     },
21290
21291     // private? - in a new class?
21292     cleanUpPaste :  function()
21293     {
21294         // cleans up the whole document..
21295         Roo.log('cleanuppaste');
21296         
21297         this.cleanUpChildren(this.doc.body);
21298         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21299         if (clean != this.doc.body.innerHTML) {
21300             this.doc.body.innerHTML = clean;
21301         }
21302         
21303     },
21304     
21305     cleanWordChars : function(input) {// change the chars to hex code
21306         var he = Roo.HtmlEditorCore;
21307         
21308         var output = input;
21309         Roo.each(he.swapCodes, function(sw) { 
21310             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21311             
21312             output = output.replace(swapper, sw[1]);
21313         });
21314         
21315         return output;
21316     },
21317     
21318     
21319     cleanUpChildren : function (n)
21320     {
21321         if (!n.childNodes.length) {
21322             return;
21323         }
21324         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21325            this.cleanUpChild(n.childNodes[i]);
21326         }
21327     },
21328     
21329     
21330         
21331     
21332     cleanUpChild : function (node)
21333     {
21334         var ed = this;
21335         //console.log(node);
21336         if (node.nodeName == "#text") {
21337             // clean up silly Windows -- stuff?
21338             return; 
21339         }
21340         if (node.nodeName == "#comment") {
21341             node.parentNode.removeChild(node);
21342             // clean up silly Windows -- stuff?
21343             return; 
21344         }
21345         var lcname = node.tagName.toLowerCase();
21346         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21347         // whitelist of tags..
21348         
21349         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21350             // remove node.
21351             node.parentNode.removeChild(node);
21352             return;
21353             
21354         }
21355         
21356         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21357         
21358         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21359         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21360         
21361         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21362         //    remove_keep_children = true;
21363         //}
21364         
21365         if (remove_keep_children) {
21366             this.cleanUpChildren(node);
21367             // inserts everything just before this node...
21368             while (node.childNodes.length) {
21369                 var cn = node.childNodes[0];
21370                 node.removeChild(cn);
21371                 node.parentNode.insertBefore(cn, node);
21372             }
21373             node.parentNode.removeChild(node);
21374             return;
21375         }
21376         
21377         if (!node.attributes || !node.attributes.length) {
21378             this.cleanUpChildren(node);
21379             return;
21380         }
21381         
21382         function cleanAttr(n,v)
21383         {
21384             
21385             if (v.match(/^\./) || v.match(/^\//)) {
21386                 return;
21387             }
21388             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21389                 return;
21390             }
21391             if (v.match(/^#/)) {
21392                 return;
21393             }
21394 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21395             node.removeAttribute(n);
21396             
21397         }
21398         
21399         var cwhite = this.cwhite;
21400         var cblack = this.cblack;
21401             
21402         function cleanStyle(n,v)
21403         {
21404             if (v.match(/expression/)) { //XSS?? should we even bother..
21405                 node.removeAttribute(n);
21406                 return;
21407             }
21408             
21409             var parts = v.split(/;/);
21410             var clean = [];
21411             
21412             Roo.each(parts, function(p) {
21413                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21414                 if (!p.length) {
21415                     return true;
21416                 }
21417                 var l = p.split(':').shift().replace(/\s+/g,'');
21418                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21419                 
21420                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21421 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21422                     //node.removeAttribute(n);
21423                     return true;
21424                 }
21425                 //Roo.log()
21426                 // only allow 'c whitelisted system attributes'
21427                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21428 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21429                     //node.removeAttribute(n);
21430                     return true;
21431                 }
21432                 
21433                 
21434                  
21435                 
21436                 clean.push(p);
21437                 return true;
21438             });
21439             if (clean.length) { 
21440                 node.setAttribute(n, clean.join(';'));
21441             } else {
21442                 node.removeAttribute(n);
21443             }
21444             
21445         }
21446         
21447         
21448         for (var i = node.attributes.length-1; i > -1 ; i--) {
21449             var a = node.attributes[i];
21450             //console.log(a);
21451             
21452             if (a.name.toLowerCase().substr(0,2)=='on')  {
21453                 node.removeAttribute(a.name);
21454                 continue;
21455             }
21456             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21457                 node.removeAttribute(a.name);
21458                 continue;
21459             }
21460             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21461                 cleanAttr(a.name,a.value); // fixme..
21462                 continue;
21463             }
21464             if (a.name == 'style') {
21465                 cleanStyle(a.name,a.value);
21466                 continue;
21467             }
21468             /// clean up MS crap..
21469             // tecnically this should be a list of valid class'es..
21470             
21471             
21472             if (a.name == 'class') {
21473                 if (a.value.match(/^Mso/)) {
21474                     node.className = '';
21475                 }
21476                 
21477                 if (a.value.match(/body/)) {
21478                     node.className = '';
21479                 }
21480                 continue;
21481             }
21482             
21483             // style cleanup!?
21484             // class cleanup?
21485             
21486         }
21487         
21488         
21489         this.cleanUpChildren(node);
21490         
21491         
21492     },
21493     
21494     /**
21495      * Clean up MS wordisms...
21496      */
21497     cleanWord : function(node)
21498     {
21499         
21500         
21501         if (!node) {
21502             this.cleanWord(this.doc.body);
21503             return;
21504         }
21505         if (node.nodeName == "#text") {
21506             // clean up silly Windows -- stuff?
21507             return; 
21508         }
21509         if (node.nodeName == "#comment") {
21510             node.parentNode.removeChild(node);
21511             // clean up silly Windows -- stuff?
21512             return; 
21513         }
21514         
21515         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21516             node.parentNode.removeChild(node);
21517             return;
21518         }
21519         
21520         // remove - but keep children..
21521         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21522             while (node.childNodes.length) {
21523                 var cn = node.childNodes[0];
21524                 node.removeChild(cn);
21525                 node.parentNode.insertBefore(cn, node);
21526             }
21527             node.parentNode.removeChild(node);
21528             this.iterateChildren(node, this.cleanWord);
21529             return;
21530         }
21531         // clean styles
21532         if (node.className.length) {
21533             
21534             var cn = node.className.split(/\W+/);
21535             var cna = [];
21536             Roo.each(cn, function(cls) {
21537                 if (cls.match(/Mso[a-zA-Z]+/)) {
21538                     return;
21539                 }
21540                 cna.push(cls);
21541             });
21542             node.className = cna.length ? cna.join(' ') : '';
21543             if (!cna.length) {
21544                 node.removeAttribute("class");
21545             }
21546         }
21547         
21548         if (node.hasAttribute("lang")) {
21549             node.removeAttribute("lang");
21550         }
21551         
21552         if (node.hasAttribute("style")) {
21553             
21554             var styles = node.getAttribute("style").split(";");
21555             var nstyle = [];
21556             Roo.each(styles, function(s) {
21557                 if (!s.match(/:/)) {
21558                     return;
21559                 }
21560                 var kv = s.split(":");
21561                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21562                     return;
21563                 }
21564                 // what ever is left... we allow.
21565                 nstyle.push(s);
21566             });
21567             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21568             if (!nstyle.length) {
21569                 node.removeAttribute('style');
21570             }
21571         }
21572         this.iterateChildren(node, this.cleanWord);
21573         
21574         
21575         
21576     },
21577     /**
21578      * iterateChildren of a Node, calling fn each time, using this as the scole..
21579      * @param {DomNode} node node to iterate children of.
21580      * @param {Function} fn method of this class to call on each item.
21581      */
21582     iterateChildren : function(node, fn)
21583     {
21584         if (!node.childNodes.length) {
21585                 return;
21586         }
21587         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21588            fn.call(this, node.childNodes[i])
21589         }
21590     },
21591     
21592     
21593     /**
21594      * cleanTableWidths.
21595      *
21596      * Quite often pasting from word etc.. results in tables with column and widths.
21597      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21598      *
21599      */
21600     cleanTableWidths : function(node)
21601     {
21602          
21603          
21604         if (!node) {
21605             this.cleanTableWidths(this.doc.body);
21606             return;
21607         }
21608         
21609         // ignore list...
21610         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21611             return; 
21612         }
21613         Roo.log(node.tagName);
21614         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21615             this.iterateChildren(node, this.cleanTableWidths);
21616             return;
21617         }
21618         if (node.hasAttribute('width')) {
21619             node.removeAttribute('width');
21620         }
21621         
21622          
21623         if (node.hasAttribute("style")) {
21624             // pretty basic...
21625             
21626             var styles = node.getAttribute("style").split(";");
21627             var nstyle = [];
21628             Roo.each(styles, function(s) {
21629                 if (!s.match(/:/)) {
21630                     return;
21631                 }
21632                 var kv = s.split(":");
21633                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21634                     return;
21635                 }
21636                 // what ever is left... we allow.
21637                 nstyle.push(s);
21638             });
21639             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21640             if (!nstyle.length) {
21641                 node.removeAttribute('style');
21642             }
21643         }
21644         
21645         this.iterateChildren(node, this.cleanTableWidths);
21646         
21647         
21648     },
21649     
21650     
21651     
21652     
21653     domToHTML : function(currentElement, depth, nopadtext) {
21654         
21655         depth = depth || 0;
21656         nopadtext = nopadtext || false;
21657     
21658         if (!currentElement) {
21659             return this.domToHTML(this.doc.body);
21660         }
21661         
21662         //Roo.log(currentElement);
21663         var j;
21664         var allText = false;
21665         var nodeName = currentElement.nodeName;
21666         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21667         
21668         if  (nodeName == '#text') {
21669             
21670             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21671         }
21672         
21673         
21674         var ret = '';
21675         if (nodeName != 'BODY') {
21676              
21677             var i = 0;
21678             // Prints the node tagName, such as <A>, <IMG>, etc
21679             if (tagName) {
21680                 var attr = [];
21681                 for(i = 0; i < currentElement.attributes.length;i++) {
21682                     // quoting?
21683                     var aname = currentElement.attributes.item(i).name;
21684                     if (!currentElement.attributes.item(i).value.length) {
21685                         continue;
21686                     }
21687                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21688                 }
21689                 
21690                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21691             } 
21692             else {
21693                 
21694                 // eack
21695             }
21696         } else {
21697             tagName = false;
21698         }
21699         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21700             return ret;
21701         }
21702         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21703             nopadtext = true;
21704         }
21705         
21706         
21707         // Traverse the tree
21708         i = 0;
21709         var currentElementChild = currentElement.childNodes.item(i);
21710         var allText = true;
21711         var innerHTML  = '';
21712         lastnode = '';
21713         while (currentElementChild) {
21714             // Formatting code (indent the tree so it looks nice on the screen)
21715             var nopad = nopadtext;
21716             if (lastnode == 'SPAN') {
21717                 nopad  = true;
21718             }
21719             // text
21720             if  (currentElementChild.nodeName == '#text') {
21721                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21722                 toadd = nopadtext ? toadd : toadd.trim();
21723                 if (!nopad && toadd.length > 80) {
21724                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21725                 }
21726                 innerHTML  += toadd;
21727                 
21728                 i++;
21729                 currentElementChild = currentElement.childNodes.item(i);
21730                 lastNode = '';
21731                 continue;
21732             }
21733             allText = false;
21734             
21735             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21736                 
21737             // Recursively traverse the tree structure of the child node
21738             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21739             lastnode = currentElementChild.nodeName;
21740             i++;
21741             currentElementChild=currentElement.childNodes.item(i);
21742         }
21743         
21744         ret += innerHTML;
21745         
21746         if (!allText) {
21747                 // The remaining code is mostly for formatting the tree
21748             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21749         }
21750         
21751         
21752         if (tagName) {
21753             ret+= "</"+tagName+">";
21754         }
21755         return ret;
21756         
21757     },
21758         
21759     applyBlacklists : function()
21760     {
21761         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21762         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21763         
21764         this.white = [];
21765         this.black = [];
21766         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21767             if (b.indexOf(tag) > -1) {
21768                 return;
21769             }
21770             this.white.push(tag);
21771             
21772         }, this);
21773         
21774         Roo.each(w, function(tag) {
21775             if (b.indexOf(tag) > -1) {
21776                 return;
21777             }
21778             if (this.white.indexOf(tag) > -1) {
21779                 return;
21780             }
21781             this.white.push(tag);
21782             
21783         }, this);
21784         
21785         
21786         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21787             if (w.indexOf(tag) > -1) {
21788                 return;
21789             }
21790             this.black.push(tag);
21791             
21792         }, this);
21793         
21794         Roo.each(b, function(tag) {
21795             if (w.indexOf(tag) > -1) {
21796                 return;
21797             }
21798             if (this.black.indexOf(tag) > -1) {
21799                 return;
21800             }
21801             this.black.push(tag);
21802             
21803         }, this);
21804         
21805         
21806         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21807         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21808         
21809         this.cwhite = [];
21810         this.cblack = [];
21811         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21812             if (b.indexOf(tag) > -1) {
21813                 return;
21814             }
21815             this.cwhite.push(tag);
21816             
21817         }, this);
21818         
21819         Roo.each(w, function(tag) {
21820             if (b.indexOf(tag) > -1) {
21821                 return;
21822             }
21823             if (this.cwhite.indexOf(tag) > -1) {
21824                 return;
21825             }
21826             this.cwhite.push(tag);
21827             
21828         }, this);
21829         
21830         
21831         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21832             if (w.indexOf(tag) > -1) {
21833                 return;
21834             }
21835             this.cblack.push(tag);
21836             
21837         }, this);
21838         
21839         Roo.each(b, function(tag) {
21840             if (w.indexOf(tag) > -1) {
21841                 return;
21842             }
21843             if (this.cblack.indexOf(tag) > -1) {
21844                 return;
21845             }
21846             this.cblack.push(tag);
21847             
21848         }, this);
21849     },
21850     
21851     setStylesheets : function(stylesheets)
21852     {
21853         if(typeof(stylesheets) == 'string'){
21854             Roo.get(this.iframe.contentDocument.head).createChild({
21855                 tag : 'link',
21856                 rel : 'stylesheet',
21857                 type : 'text/css',
21858                 href : stylesheets
21859             });
21860             
21861             return;
21862         }
21863         var _this = this;
21864      
21865         Roo.each(stylesheets, function(s) {
21866             if(!s.length){
21867                 return;
21868             }
21869             
21870             Roo.get(_this.iframe.contentDocument.head).createChild({
21871                 tag : 'link',
21872                 rel : 'stylesheet',
21873                 type : 'text/css',
21874                 href : s
21875             });
21876         });
21877
21878         
21879     },
21880     
21881     removeStylesheets : function()
21882     {
21883         var _this = this;
21884         
21885         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21886             s.remove();
21887         });
21888     }
21889     
21890     // hide stuff that is not compatible
21891     /**
21892      * @event blur
21893      * @hide
21894      */
21895     /**
21896      * @event change
21897      * @hide
21898      */
21899     /**
21900      * @event focus
21901      * @hide
21902      */
21903     /**
21904      * @event specialkey
21905      * @hide
21906      */
21907     /**
21908      * @cfg {String} fieldClass @hide
21909      */
21910     /**
21911      * @cfg {String} focusClass @hide
21912      */
21913     /**
21914      * @cfg {String} autoCreate @hide
21915      */
21916     /**
21917      * @cfg {String} inputType @hide
21918      */
21919     /**
21920      * @cfg {String} invalidClass @hide
21921      */
21922     /**
21923      * @cfg {String} invalidText @hide
21924      */
21925     /**
21926      * @cfg {String} msgFx @hide
21927      */
21928     /**
21929      * @cfg {String} validateOnBlur @hide
21930      */
21931 });
21932
21933 Roo.HtmlEditorCore.white = [
21934         'area', 'br', 'img', 'input', 'hr', 'wbr',
21935         
21936        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21937        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21938        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21939        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21940        'table',   'ul',         'xmp', 
21941        
21942        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21943       'thead',   'tr', 
21944      
21945       'dir', 'menu', 'ol', 'ul', 'dl',
21946        
21947       'embed',  'object'
21948 ];
21949
21950
21951 Roo.HtmlEditorCore.black = [
21952     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21953         'applet', // 
21954         'base',   'basefont', 'bgsound', 'blink',  'body', 
21955         'frame',  'frameset', 'head',    'html',   'ilayer', 
21956         'iframe', 'layer',  'link',     'meta',    'object',   
21957         'script', 'style' ,'title',  'xml' // clean later..
21958 ];
21959 Roo.HtmlEditorCore.clean = [
21960     'script', 'style', 'title', 'xml'
21961 ];
21962 Roo.HtmlEditorCore.remove = [
21963     'font'
21964 ];
21965 // attributes..
21966
21967 Roo.HtmlEditorCore.ablack = [
21968     'on'
21969 ];
21970     
21971 Roo.HtmlEditorCore.aclean = [ 
21972     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21973 ];
21974
21975 // protocols..
21976 Roo.HtmlEditorCore.pwhite= [
21977         'http',  'https',  'mailto'
21978 ];
21979
21980 // white listed style attributes.
21981 Roo.HtmlEditorCore.cwhite= [
21982       //  'text-align', /// default is to allow most things..
21983       
21984          
21985 //        'font-size'//??
21986 ];
21987
21988 // black listed style attributes.
21989 Roo.HtmlEditorCore.cblack= [
21990       //  'font-size' -- this can be set by the project 
21991 ];
21992
21993
21994 Roo.HtmlEditorCore.swapCodes   =[ 
21995     [    8211, "--" ], 
21996     [    8212, "--" ], 
21997     [    8216,  "'" ],  
21998     [    8217, "'" ],  
21999     [    8220, '"' ],  
22000     [    8221, '"' ],  
22001     [    8226, "*" ],  
22002     [    8230, "..." ]
22003 ]; 
22004
22005     /*
22006  * - LGPL
22007  *
22008  * HtmlEditor
22009  * 
22010  */
22011
22012 /**
22013  * @class Roo.bootstrap.HtmlEditor
22014  * @extends Roo.bootstrap.TextArea
22015  * Bootstrap HtmlEditor class
22016
22017  * @constructor
22018  * Create a new HtmlEditor
22019  * @param {Object} config The config object
22020  */
22021
22022 Roo.bootstrap.HtmlEditor = function(config){
22023     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22024     if (!this.toolbars) {
22025         this.toolbars = [];
22026     }
22027     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22028     this.addEvents({
22029             /**
22030              * @event initialize
22031              * Fires when the editor is fully initialized (including the iframe)
22032              * @param {HtmlEditor} this
22033              */
22034             initialize: true,
22035             /**
22036              * @event activate
22037              * Fires when the editor is first receives the focus. Any insertion must wait
22038              * until after this event.
22039              * @param {HtmlEditor} this
22040              */
22041             activate: true,
22042              /**
22043              * @event beforesync
22044              * Fires before the textarea is updated with content from the editor iframe. Return false
22045              * to cancel the sync.
22046              * @param {HtmlEditor} this
22047              * @param {String} html
22048              */
22049             beforesync: true,
22050              /**
22051              * @event beforepush
22052              * Fires before the iframe editor is updated with content from the textarea. Return false
22053              * to cancel the push.
22054              * @param {HtmlEditor} this
22055              * @param {String} html
22056              */
22057             beforepush: true,
22058              /**
22059              * @event sync
22060              * Fires when the textarea is updated with content from the editor iframe.
22061              * @param {HtmlEditor} this
22062              * @param {String} html
22063              */
22064             sync: true,
22065              /**
22066              * @event push
22067              * Fires when the iframe editor is updated with content from the textarea.
22068              * @param {HtmlEditor} this
22069              * @param {String} html
22070              */
22071             push: true,
22072              /**
22073              * @event editmodechange
22074              * Fires when the editor switches edit modes
22075              * @param {HtmlEditor} this
22076              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22077              */
22078             editmodechange: true,
22079             /**
22080              * @event editorevent
22081              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22082              * @param {HtmlEditor} this
22083              */
22084             editorevent: true,
22085             /**
22086              * @event firstfocus
22087              * Fires when on first focus - needed by toolbars..
22088              * @param {HtmlEditor} this
22089              */
22090             firstfocus: true,
22091             /**
22092              * @event autosave
22093              * Auto save the htmlEditor value as a file into Events
22094              * @param {HtmlEditor} this
22095              */
22096             autosave: true,
22097             /**
22098              * @event savedpreview
22099              * preview the saved version of htmlEditor
22100              * @param {HtmlEditor} this
22101              */
22102             savedpreview: true
22103         });
22104 };
22105
22106
22107 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22108     
22109     
22110       /**
22111      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22112      */
22113     toolbars : false,
22114    
22115      /**
22116      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22117      *                        Roo.resizable.
22118      */
22119     resizable : false,
22120      /**
22121      * @cfg {Number} height (in pixels)
22122      */   
22123     height: 300,
22124    /**
22125      * @cfg {Number} width (in pixels)
22126      */   
22127     width: false,
22128     
22129     /**
22130      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22131      * 
22132      */
22133     stylesheets: false,
22134     
22135     // id of frame..
22136     frameId: false,
22137     
22138     // private properties
22139     validationEvent : false,
22140     deferHeight: true,
22141     initialized : false,
22142     activated : false,
22143     
22144     onFocus : Roo.emptyFn,
22145     iframePad:3,
22146     hideMode:'offsets',
22147     
22148     
22149     tbContainer : false,
22150     
22151     toolbarContainer :function() {
22152         return this.wrap.select('.x-html-editor-tb',true).first();
22153     },
22154
22155     /**
22156      * Protected method that will not generally be called directly. It
22157      * is called when the editor creates its toolbar. Override this method if you need to
22158      * add custom toolbar buttons.
22159      * @param {HtmlEditor} editor
22160      */
22161     createToolbar : function(){
22162         
22163         Roo.log("create toolbars");
22164         
22165         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22166         this.toolbars[0].render(this.toolbarContainer());
22167         
22168         return;
22169         
22170 //        if (!editor.toolbars || !editor.toolbars.length) {
22171 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22172 //        }
22173 //        
22174 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22175 //            editor.toolbars[i] = Roo.factory(
22176 //                    typeof(editor.toolbars[i]) == 'string' ?
22177 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22178 //                Roo.bootstrap.HtmlEditor);
22179 //            editor.toolbars[i].init(editor);
22180 //        }
22181     },
22182
22183      
22184     // private
22185     onRender : function(ct, position)
22186     {
22187        // Roo.log("Call onRender: " + this.xtype);
22188         var _t = this;
22189         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22190       
22191         this.wrap = this.inputEl().wrap({
22192             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22193         });
22194         
22195         this.editorcore.onRender(ct, position);
22196          
22197         if (this.resizable) {
22198             this.resizeEl = new Roo.Resizable(this.wrap, {
22199                 pinned : true,
22200                 wrap: true,
22201                 dynamic : true,
22202                 minHeight : this.height,
22203                 height: this.height,
22204                 handles : this.resizable,
22205                 width: this.width,
22206                 listeners : {
22207                     resize : function(r, w, h) {
22208                         _t.onResize(w,h); // -something
22209                     }
22210                 }
22211             });
22212             
22213         }
22214         this.createToolbar(this);
22215        
22216         
22217         if(!this.width && this.resizable){
22218             this.setSize(this.wrap.getSize());
22219         }
22220         if (this.resizeEl) {
22221             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22222             // should trigger onReize..
22223         }
22224         
22225     },
22226
22227     // private
22228     onResize : function(w, h)
22229     {
22230         Roo.log('resize: ' +w + ',' + h );
22231         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22232         var ew = false;
22233         var eh = false;
22234         
22235         if(this.inputEl() ){
22236             if(typeof w == 'number'){
22237                 var aw = w - this.wrap.getFrameWidth('lr');
22238                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22239                 ew = aw;
22240             }
22241             if(typeof h == 'number'){
22242                  var tbh = -11;  // fixme it needs to tool bar size!
22243                 for (var i =0; i < this.toolbars.length;i++) {
22244                     // fixme - ask toolbars for heights?
22245                     tbh += this.toolbars[i].el.getHeight();
22246                     //if (this.toolbars[i].footer) {
22247                     //    tbh += this.toolbars[i].footer.el.getHeight();
22248                     //}
22249                 }
22250               
22251                 
22252                 
22253                 
22254                 
22255                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22256                 ah -= 5; // knock a few pixes off for look..
22257                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22258                 var eh = ah;
22259             }
22260         }
22261         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22262         this.editorcore.onResize(ew,eh);
22263         
22264     },
22265
22266     /**
22267      * Toggles the editor between standard and source edit mode.
22268      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22269      */
22270     toggleSourceEdit : function(sourceEditMode)
22271     {
22272         this.editorcore.toggleSourceEdit(sourceEditMode);
22273         
22274         if(this.editorcore.sourceEditMode){
22275             Roo.log('editor - showing textarea');
22276             
22277 //            Roo.log('in');
22278 //            Roo.log(this.syncValue());
22279             this.syncValue();
22280             this.inputEl().removeClass(['hide', 'x-hidden']);
22281             this.inputEl().dom.removeAttribute('tabIndex');
22282             this.inputEl().focus();
22283         }else{
22284             Roo.log('editor - hiding textarea');
22285 //            Roo.log('out')
22286 //            Roo.log(this.pushValue()); 
22287             this.pushValue();
22288             
22289             this.inputEl().addClass(['hide', 'x-hidden']);
22290             this.inputEl().dom.setAttribute('tabIndex', -1);
22291             //this.deferFocus();
22292         }
22293          
22294         if(this.resizable){
22295             this.setSize(this.wrap.getSize());
22296         }
22297         
22298         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22299     },
22300  
22301     // private (for BoxComponent)
22302     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22303
22304     // private (for BoxComponent)
22305     getResizeEl : function(){
22306         return this.wrap;
22307     },
22308
22309     // private (for BoxComponent)
22310     getPositionEl : function(){
22311         return this.wrap;
22312     },
22313
22314     // private
22315     initEvents : function(){
22316         this.originalValue = this.getValue();
22317     },
22318
22319 //    /**
22320 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22321 //     * @method
22322 //     */
22323 //    markInvalid : Roo.emptyFn,
22324 //    /**
22325 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22326 //     * @method
22327 //     */
22328 //    clearInvalid : Roo.emptyFn,
22329
22330     setValue : function(v){
22331         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22332         this.editorcore.pushValue();
22333     },
22334
22335      
22336     // private
22337     deferFocus : function(){
22338         this.focus.defer(10, this);
22339     },
22340
22341     // doc'ed in Field
22342     focus : function(){
22343         this.editorcore.focus();
22344         
22345     },
22346       
22347
22348     // private
22349     onDestroy : function(){
22350         
22351         
22352         
22353         if(this.rendered){
22354             
22355             for (var i =0; i < this.toolbars.length;i++) {
22356                 // fixme - ask toolbars for heights?
22357                 this.toolbars[i].onDestroy();
22358             }
22359             
22360             this.wrap.dom.innerHTML = '';
22361             this.wrap.remove();
22362         }
22363     },
22364
22365     // private
22366     onFirstFocus : function(){
22367         //Roo.log("onFirstFocus");
22368         this.editorcore.onFirstFocus();
22369          for (var i =0; i < this.toolbars.length;i++) {
22370             this.toolbars[i].onFirstFocus();
22371         }
22372         
22373     },
22374     
22375     // private
22376     syncValue : function()
22377     {   
22378         this.editorcore.syncValue();
22379     },
22380     
22381     pushValue : function()
22382     {   
22383         this.editorcore.pushValue();
22384     }
22385      
22386     
22387     // hide stuff that is not compatible
22388     /**
22389      * @event blur
22390      * @hide
22391      */
22392     /**
22393      * @event change
22394      * @hide
22395      */
22396     /**
22397      * @event focus
22398      * @hide
22399      */
22400     /**
22401      * @event specialkey
22402      * @hide
22403      */
22404     /**
22405      * @cfg {String} fieldClass @hide
22406      */
22407     /**
22408      * @cfg {String} focusClass @hide
22409      */
22410     /**
22411      * @cfg {String} autoCreate @hide
22412      */
22413     /**
22414      * @cfg {String} inputType @hide
22415      */
22416     /**
22417      * @cfg {String} invalidClass @hide
22418      */
22419     /**
22420      * @cfg {String} invalidText @hide
22421      */
22422     /**
22423      * @cfg {String} msgFx @hide
22424      */
22425     /**
22426      * @cfg {String} validateOnBlur @hide
22427      */
22428 });
22429  
22430     
22431    
22432    
22433    
22434       
22435 Roo.namespace('Roo.bootstrap.htmleditor');
22436 /**
22437  * @class Roo.bootstrap.HtmlEditorToolbar1
22438  * Basic Toolbar
22439  * 
22440  * Usage:
22441  *
22442  new Roo.bootstrap.HtmlEditor({
22443     ....
22444     toolbars : [
22445         new Roo.bootstrap.HtmlEditorToolbar1({
22446             disable : { fonts: 1 , format: 1, ..., ... , ...],
22447             btns : [ .... ]
22448         })
22449     }
22450      
22451  * 
22452  * @cfg {Object} disable List of elements to disable..
22453  * @cfg {Array} btns List of additional buttons.
22454  * 
22455  * 
22456  * NEEDS Extra CSS? 
22457  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22458  */
22459  
22460 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22461 {
22462     
22463     Roo.apply(this, config);
22464     
22465     // default disabled, based on 'good practice'..
22466     this.disable = this.disable || {};
22467     Roo.applyIf(this.disable, {
22468         fontSize : true,
22469         colors : true,
22470         specialElements : true
22471     });
22472     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22473     
22474     this.editor = config.editor;
22475     this.editorcore = config.editor.editorcore;
22476     
22477     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22478     
22479     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22480     // dont call parent... till later.
22481 }
22482 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22483      
22484     bar : true,
22485     
22486     editor : false,
22487     editorcore : false,
22488     
22489     
22490     formats : [
22491         "p" ,  
22492         "h1","h2","h3","h4","h5","h6", 
22493         "pre", "code", 
22494         "abbr", "acronym", "address", "cite", "samp", "var",
22495         'div','span'
22496     ],
22497     
22498     onRender : function(ct, position)
22499     {
22500        // Roo.log("Call onRender: " + this.xtype);
22501         
22502        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22503        Roo.log(this.el);
22504        this.el.dom.style.marginBottom = '0';
22505        var _this = this;
22506        var editorcore = this.editorcore;
22507        var editor= this.editor;
22508        
22509        var children = [];
22510        var btn = function(id,cmd , toggle, handler){
22511        
22512             var  event = toggle ? 'toggle' : 'click';
22513        
22514             var a = {
22515                 size : 'sm',
22516                 xtype: 'Button',
22517                 xns: Roo.bootstrap,
22518                 glyphicon : id,
22519                 cmd : id || cmd,
22520                 enableToggle:toggle !== false,
22521                 //html : 'submit'
22522                 pressed : toggle ? false : null,
22523                 listeners : {}
22524             };
22525             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22526                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22527             };
22528             children.push(a);
22529             return a;
22530        }
22531         
22532         var style = {
22533                 xtype: 'Button',
22534                 size : 'sm',
22535                 xns: Roo.bootstrap,
22536                 glyphicon : 'font',
22537                 //html : 'submit'
22538                 menu : {
22539                     xtype: 'Menu',
22540                     xns: Roo.bootstrap,
22541                     items:  []
22542                 }
22543         };
22544         Roo.each(this.formats, function(f) {
22545             style.menu.items.push({
22546                 xtype :'MenuItem',
22547                 xns: Roo.bootstrap,
22548                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22549                 tagname : f,
22550                 listeners : {
22551                     click : function()
22552                     {
22553                         editorcore.insertTag(this.tagname);
22554                         editor.focus();
22555                     }
22556                 }
22557                 
22558             });
22559         });
22560          children.push(style);   
22561             
22562             
22563         btn('bold',false,true);
22564         btn('italic',false,true);
22565         btn('align-left', 'justifyleft',true);
22566         btn('align-center', 'justifycenter',true);
22567         btn('align-right' , 'justifyright',true);
22568         btn('link', false, false, function(btn) {
22569             //Roo.log("create link?");
22570             var url = prompt(this.createLinkText, this.defaultLinkValue);
22571             if(url && url != 'http:/'+'/'){
22572                 this.editorcore.relayCmd('createlink', url);
22573             }
22574         }),
22575         btn('list','insertunorderedlist',true);
22576         btn('pencil', false,true, function(btn){
22577                 Roo.log(this);
22578                 
22579                 this.toggleSourceEdit(btn.pressed);
22580         });
22581         /*
22582         var cog = {
22583                 xtype: 'Button',
22584                 size : 'sm',
22585                 xns: Roo.bootstrap,
22586                 glyphicon : 'cog',
22587                 //html : 'submit'
22588                 menu : {
22589                     xtype: 'Menu',
22590                     xns: Roo.bootstrap,
22591                     items:  []
22592                 }
22593         };
22594         
22595         cog.menu.items.push({
22596             xtype :'MenuItem',
22597             xns: Roo.bootstrap,
22598             html : Clean styles,
22599             tagname : f,
22600             listeners : {
22601                 click : function()
22602                 {
22603                     editorcore.insertTag(this.tagname);
22604                     editor.focus();
22605                 }
22606             }
22607             
22608         });
22609        */
22610         
22611          
22612        this.xtype = 'NavSimplebar';
22613         
22614         for(var i=0;i< children.length;i++) {
22615             
22616             this.buttons.add(this.addxtypeChild(children[i]));
22617             
22618         }
22619         
22620         editor.on('editorevent', this.updateToolbar, this);
22621     },
22622     onBtnClick : function(id)
22623     {
22624        this.editorcore.relayCmd(id);
22625        this.editorcore.focus();
22626     },
22627     
22628     /**
22629      * Protected method that will not generally be called directly. It triggers
22630      * a toolbar update by reading the markup state of the current selection in the editor.
22631      */
22632     updateToolbar: function(){
22633
22634         if(!this.editorcore.activated){
22635             this.editor.onFirstFocus(); // is this neeed?
22636             return;
22637         }
22638
22639         var btns = this.buttons; 
22640         var doc = this.editorcore.doc;
22641         btns.get('bold').setActive(doc.queryCommandState('bold'));
22642         btns.get('italic').setActive(doc.queryCommandState('italic'));
22643         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22644         
22645         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22646         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22647         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22648         
22649         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22650         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22651          /*
22652         
22653         var ans = this.editorcore.getAllAncestors();
22654         if (this.formatCombo) {
22655             
22656             
22657             var store = this.formatCombo.store;
22658             this.formatCombo.setValue("");
22659             for (var i =0; i < ans.length;i++) {
22660                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22661                     // select it..
22662                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22663                     break;
22664                 }
22665             }
22666         }
22667         
22668         
22669         
22670         // hides menus... - so this cant be on a menu...
22671         Roo.bootstrap.MenuMgr.hideAll();
22672         */
22673         Roo.bootstrap.MenuMgr.hideAll();
22674         //this.editorsyncValue();
22675     },
22676     onFirstFocus: function() {
22677         this.buttons.each(function(item){
22678            item.enable();
22679         });
22680     },
22681     toggleSourceEdit : function(sourceEditMode){
22682         
22683           
22684         if(sourceEditMode){
22685             Roo.log("disabling buttons");
22686            this.buttons.each( function(item){
22687                 if(item.cmd != 'pencil'){
22688                     item.disable();
22689                 }
22690             });
22691           
22692         }else{
22693             Roo.log("enabling buttons");
22694             if(this.editorcore.initialized){
22695                 this.buttons.each( function(item){
22696                     item.enable();
22697                 });
22698             }
22699             
22700         }
22701         Roo.log("calling toggole on editor");
22702         // tell the editor that it's been pressed..
22703         this.editor.toggleSourceEdit(sourceEditMode);
22704        
22705     }
22706 });
22707
22708
22709
22710
22711
22712 /**
22713  * @class Roo.bootstrap.Table.AbstractSelectionModel
22714  * @extends Roo.util.Observable
22715  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22716  * implemented by descendant classes.  This class should not be directly instantiated.
22717  * @constructor
22718  */
22719 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22720     this.locked = false;
22721     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22722 };
22723
22724
22725 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22726     /** @ignore Called by the grid automatically. Do not call directly. */
22727     init : function(grid){
22728         this.grid = grid;
22729         this.initEvents();
22730     },
22731
22732     /**
22733      * Locks the selections.
22734      */
22735     lock : function(){
22736         this.locked = true;
22737     },
22738
22739     /**
22740      * Unlocks the selections.
22741      */
22742     unlock : function(){
22743         this.locked = false;
22744     },
22745
22746     /**
22747      * Returns true if the selections are locked.
22748      * @return {Boolean}
22749      */
22750     isLocked : function(){
22751         return this.locked;
22752     }
22753 });
22754 /**
22755  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22756  * @class Roo.bootstrap.Table.RowSelectionModel
22757  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22758  * It supports multiple selections and keyboard selection/navigation. 
22759  * @constructor
22760  * @param {Object} config
22761  */
22762
22763 Roo.bootstrap.Table.RowSelectionModel = function(config){
22764     Roo.apply(this, config);
22765     this.selections = new Roo.util.MixedCollection(false, function(o){
22766         return o.id;
22767     });
22768
22769     this.last = false;
22770     this.lastActive = false;
22771
22772     this.addEvents({
22773         /**
22774              * @event selectionchange
22775              * Fires when the selection changes
22776              * @param {SelectionModel} this
22777              */
22778             "selectionchange" : true,
22779         /**
22780              * @event afterselectionchange
22781              * Fires after the selection changes (eg. by key press or clicking)
22782              * @param {SelectionModel} this
22783              */
22784             "afterselectionchange" : true,
22785         /**
22786              * @event beforerowselect
22787              * Fires when a row is selected being selected, return false to cancel.
22788              * @param {SelectionModel} this
22789              * @param {Number} rowIndex The selected index
22790              * @param {Boolean} keepExisting False if other selections will be cleared
22791              */
22792             "beforerowselect" : true,
22793         /**
22794              * @event rowselect
22795              * Fires when a row is selected.
22796              * @param {SelectionModel} this
22797              * @param {Number} rowIndex The selected index
22798              * @param {Roo.data.Record} r The record
22799              */
22800             "rowselect" : true,
22801         /**
22802              * @event rowdeselect
22803              * Fires when a row is deselected.
22804              * @param {SelectionModel} this
22805              * @param {Number} rowIndex The selected index
22806              */
22807         "rowdeselect" : true
22808     });
22809     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22810     this.locked = false;
22811  };
22812
22813 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22814     /**
22815      * @cfg {Boolean} singleSelect
22816      * True to allow selection of only one row at a time (defaults to false)
22817      */
22818     singleSelect : false,
22819
22820     // private
22821     initEvents : function()
22822     {
22823
22824         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22825         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22826         //}else{ // allow click to work like normal
22827          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22828         //}
22829         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22830         this.grid.on("rowclick", this.handleMouseDown, this);
22831         
22832         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22833             "up" : function(e){
22834                 if(!e.shiftKey){
22835                     this.selectPrevious(e.shiftKey);
22836                 }else if(this.last !== false && this.lastActive !== false){
22837                     var last = this.last;
22838                     this.selectRange(this.last,  this.lastActive-1);
22839                     this.grid.getView().focusRow(this.lastActive);
22840                     if(last !== false){
22841                         this.last = last;
22842                     }
22843                 }else{
22844                     this.selectFirstRow();
22845                 }
22846                 this.fireEvent("afterselectionchange", this);
22847             },
22848             "down" : function(e){
22849                 if(!e.shiftKey){
22850                     this.selectNext(e.shiftKey);
22851                 }else if(this.last !== false && this.lastActive !== false){
22852                     var last = this.last;
22853                     this.selectRange(this.last,  this.lastActive+1);
22854                     this.grid.getView().focusRow(this.lastActive);
22855                     if(last !== false){
22856                         this.last = last;
22857                     }
22858                 }else{
22859                     this.selectFirstRow();
22860                 }
22861                 this.fireEvent("afterselectionchange", this);
22862             },
22863             scope: this
22864         });
22865         this.grid.store.on('load', function(){
22866             this.selections.clear();
22867         },this);
22868         /*
22869         var view = this.grid.view;
22870         view.on("refresh", this.onRefresh, this);
22871         view.on("rowupdated", this.onRowUpdated, this);
22872         view.on("rowremoved", this.onRemove, this);
22873         */
22874     },
22875
22876     // private
22877     onRefresh : function()
22878     {
22879         var ds = this.grid.store, i, v = this.grid.view;
22880         var s = this.selections;
22881         s.each(function(r){
22882             if((i = ds.indexOfId(r.id)) != -1){
22883                 v.onRowSelect(i);
22884             }else{
22885                 s.remove(r);
22886             }
22887         });
22888     },
22889
22890     // private
22891     onRemove : function(v, index, r){
22892         this.selections.remove(r);
22893     },
22894
22895     // private
22896     onRowUpdated : function(v, index, r){
22897         if(this.isSelected(r)){
22898             v.onRowSelect(index);
22899         }
22900     },
22901
22902     /**
22903      * Select records.
22904      * @param {Array} records The records to select
22905      * @param {Boolean} keepExisting (optional) True to keep existing selections
22906      */
22907     selectRecords : function(records, keepExisting)
22908     {
22909         if(!keepExisting){
22910             this.clearSelections();
22911         }
22912             var ds = this.grid.store;
22913         for(var i = 0, len = records.length; i < len; i++){
22914             this.selectRow(ds.indexOf(records[i]), true);
22915         }
22916     },
22917
22918     /**
22919      * Gets the number of selected rows.
22920      * @return {Number}
22921      */
22922     getCount : function(){
22923         return this.selections.length;
22924     },
22925
22926     /**
22927      * Selects the first row in the grid.
22928      */
22929     selectFirstRow : function(){
22930         this.selectRow(0);
22931     },
22932
22933     /**
22934      * Select the last row.
22935      * @param {Boolean} keepExisting (optional) True to keep existing selections
22936      */
22937     selectLastRow : function(keepExisting){
22938         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22939         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22940     },
22941
22942     /**
22943      * Selects the row immediately following the last selected row.
22944      * @param {Boolean} keepExisting (optional) True to keep existing selections
22945      */
22946     selectNext : function(keepExisting)
22947     {
22948             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22949             this.selectRow(this.last+1, keepExisting);
22950             this.grid.getView().focusRow(this.last);
22951         }
22952     },
22953
22954     /**
22955      * Selects the row that precedes the last selected row.
22956      * @param {Boolean} keepExisting (optional) True to keep existing selections
22957      */
22958     selectPrevious : function(keepExisting){
22959         if(this.last){
22960             this.selectRow(this.last-1, keepExisting);
22961             this.grid.getView().focusRow(this.last);
22962         }
22963     },
22964
22965     /**
22966      * Returns the selected records
22967      * @return {Array} Array of selected records
22968      */
22969     getSelections : function(){
22970         return [].concat(this.selections.items);
22971     },
22972
22973     /**
22974      * Returns the first selected record.
22975      * @return {Record}
22976      */
22977     getSelected : function(){
22978         return this.selections.itemAt(0);
22979     },
22980
22981
22982     /**
22983      * Clears all selections.
22984      */
22985     clearSelections : function(fast)
22986     {
22987         if(this.locked) {
22988             return;
22989         }
22990         if(fast !== true){
22991                 var ds = this.grid.store;
22992             var s = this.selections;
22993             s.each(function(r){
22994                 this.deselectRow(ds.indexOfId(r.id));
22995             }, this);
22996             s.clear();
22997         }else{
22998             this.selections.clear();
22999         }
23000         this.last = false;
23001     },
23002
23003
23004     /**
23005      * Selects all rows.
23006      */
23007     selectAll : function(){
23008         if(this.locked) {
23009             return;
23010         }
23011         this.selections.clear();
23012         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23013             this.selectRow(i, true);
23014         }
23015     },
23016
23017     /**
23018      * Returns True if there is a selection.
23019      * @return {Boolean}
23020      */
23021     hasSelection : function(){
23022         return this.selections.length > 0;
23023     },
23024
23025     /**
23026      * Returns True if the specified row is selected.
23027      * @param {Number/Record} record The record or index of the record to check
23028      * @return {Boolean}
23029      */
23030     isSelected : function(index){
23031             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23032         return (r && this.selections.key(r.id) ? true : false);
23033     },
23034
23035     /**
23036      * Returns True if the specified record id is selected.
23037      * @param {String} id The id of record to check
23038      * @return {Boolean}
23039      */
23040     isIdSelected : function(id){
23041         return (this.selections.key(id) ? true : false);
23042     },
23043
23044
23045     // private
23046     handleMouseDBClick : function(e, t){
23047         
23048     },
23049     // private
23050     handleMouseDown : function(e, t)
23051     {
23052             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23053         if(this.isLocked() || rowIndex < 0 ){
23054             return;
23055         };
23056         if(e.shiftKey && this.last !== false){
23057             var last = this.last;
23058             this.selectRange(last, rowIndex, e.ctrlKey);
23059             this.last = last; // reset the last
23060             t.focus();
23061     
23062         }else{
23063             var isSelected = this.isSelected(rowIndex);
23064             //Roo.log("select row:" + rowIndex);
23065             if(isSelected){
23066                 this.deselectRow(rowIndex);
23067             } else {
23068                         this.selectRow(rowIndex, true);
23069             }
23070     
23071             /*
23072                 if(e.button !== 0 && isSelected){
23073                 alert('rowIndex 2: ' + rowIndex);
23074                     view.focusRow(rowIndex);
23075                 }else if(e.ctrlKey && isSelected){
23076                     this.deselectRow(rowIndex);
23077                 }else if(!isSelected){
23078                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23079                     view.focusRow(rowIndex);
23080                 }
23081             */
23082         }
23083         this.fireEvent("afterselectionchange", this);
23084     },
23085     // private
23086     handleDragableRowClick :  function(grid, rowIndex, e) 
23087     {
23088         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23089             this.selectRow(rowIndex, false);
23090             grid.view.focusRow(rowIndex);
23091              this.fireEvent("afterselectionchange", this);
23092         }
23093     },
23094     
23095     /**
23096      * Selects multiple rows.
23097      * @param {Array} rows Array of the indexes of the row to select
23098      * @param {Boolean} keepExisting (optional) True to keep existing selections
23099      */
23100     selectRows : function(rows, keepExisting){
23101         if(!keepExisting){
23102             this.clearSelections();
23103         }
23104         for(var i = 0, len = rows.length; i < len; i++){
23105             this.selectRow(rows[i], true);
23106         }
23107     },
23108
23109     /**
23110      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23111      * @param {Number} startRow The index of the first row in the range
23112      * @param {Number} endRow The index of the last row in the range
23113      * @param {Boolean} keepExisting (optional) True to retain existing selections
23114      */
23115     selectRange : function(startRow, endRow, keepExisting){
23116         if(this.locked) {
23117             return;
23118         }
23119         if(!keepExisting){
23120             this.clearSelections();
23121         }
23122         if(startRow <= endRow){
23123             for(var i = startRow; i <= endRow; i++){
23124                 this.selectRow(i, true);
23125             }
23126         }else{
23127             for(var i = startRow; i >= endRow; i--){
23128                 this.selectRow(i, true);
23129             }
23130         }
23131     },
23132
23133     /**
23134      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23135      * @param {Number} startRow The index of the first row in the range
23136      * @param {Number} endRow The index of the last row in the range
23137      */
23138     deselectRange : function(startRow, endRow, preventViewNotify){
23139         if(this.locked) {
23140             return;
23141         }
23142         for(var i = startRow; i <= endRow; i++){
23143             this.deselectRow(i, preventViewNotify);
23144         }
23145     },
23146
23147     /**
23148      * Selects a row.
23149      * @param {Number} row The index of the row to select
23150      * @param {Boolean} keepExisting (optional) True to keep existing selections
23151      */
23152     selectRow : function(index, keepExisting, preventViewNotify)
23153     {
23154             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23155             return;
23156         }
23157         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23158             if(!keepExisting || this.singleSelect){
23159                 this.clearSelections();
23160             }
23161             
23162             var r = this.grid.store.getAt(index);
23163             //console.log('selectRow - record id :' + r.id);
23164             
23165             this.selections.add(r);
23166             this.last = this.lastActive = index;
23167             if(!preventViewNotify){
23168                 var proxy = new Roo.Element(
23169                                 this.grid.getRowDom(index)
23170                 );
23171                 proxy.addClass('bg-info info');
23172             }
23173             this.fireEvent("rowselect", this, index, r);
23174             this.fireEvent("selectionchange", this);
23175         }
23176     },
23177
23178     /**
23179      * Deselects a row.
23180      * @param {Number} row The index of the row to deselect
23181      */
23182     deselectRow : function(index, preventViewNotify)
23183     {
23184         if(this.locked) {
23185             return;
23186         }
23187         if(this.last == index){
23188             this.last = false;
23189         }
23190         if(this.lastActive == index){
23191             this.lastActive = false;
23192         }
23193         
23194         var r = this.grid.store.getAt(index);
23195         if (!r) {
23196             return;
23197         }
23198         
23199         this.selections.remove(r);
23200         //.console.log('deselectRow - record id :' + r.id);
23201         if(!preventViewNotify){
23202         
23203             var proxy = new Roo.Element(
23204                 this.grid.getRowDom(index)
23205             );
23206             proxy.removeClass('bg-info info');
23207         }
23208         this.fireEvent("rowdeselect", this, index);
23209         this.fireEvent("selectionchange", this);
23210     },
23211
23212     // private
23213     restoreLast : function(){
23214         if(this._last){
23215             this.last = this._last;
23216         }
23217     },
23218
23219     // private
23220     acceptsNav : function(row, col, cm){
23221         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23222     },
23223
23224     // private
23225     onEditorKey : function(field, e){
23226         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23227         if(k == e.TAB){
23228             e.stopEvent();
23229             ed.completeEdit();
23230             if(e.shiftKey){
23231                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23232             }else{
23233                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23234             }
23235         }else if(k == e.ENTER && !e.ctrlKey){
23236             e.stopEvent();
23237             ed.completeEdit();
23238             if(e.shiftKey){
23239                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23240             }else{
23241                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23242             }
23243         }else if(k == e.ESC){
23244             ed.cancelEdit();
23245         }
23246         if(newCell){
23247             g.startEditing(newCell[0], newCell[1]);
23248         }
23249     }
23250 });
23251 /*
23252  * Based on:
23253  * Ext JS Library 1.1.1
23254  * Copyright(c) 2006-2007, Ext JS, LLC.
23255  *
23256  * Originally Released Under LGPL - original licence link has changed is not relivant.
23257  *
23258  * Fork - LGPL
23259  * <script type="text/javascript">
23260  */
23261  
23262 /**
23263  * @class Roo.bootstrap.PagingToolbar
23264  * @extends Roo.bootstrap.NavSimplebar
23265  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23266  * @constructor
23267  * Create a new PagingToolbar
23268  * @param {Object} config The config object
23269  * @param {Roo.data.Store} store
23270  */
23271 Roo.bootstrap.PagingToolbar = function(config)
23272 {
23273     // old args format still supported... - xtype is prefered..
23274         // created from xtype...
23275     
23276     this.ds = config.dataSource;
23277     
23278     if (config.store && !this.ds) {
23279         this.store= Roo.factory(config.store, Roo.data);
23280         this.ds = this.store;
23281         this.ds.xmodule = this.xmodule || false;
23282     }
23283     
23284     this.toolbarItems = [];
23285     if (config.items) {
23286         this.toolbarItems = config.items;
23287     }
23288     
23289     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23290     
23291     this.cursor = 0;
23292     
23293     if (this.ds) { 
23294         this.bind(this.ds);
23295     }
23296     
23297     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23298     
23299 };
23300
23301 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23302     /**
23303      * @cfg {Roo.data.Store} dataSource
23304      * The underlying data store providing the paged data
23305      */
23306     /**
23307      * @cfg {String/HTMLElement/Element} container
23308      * container The id or element that will contain the toolbar
23309      */
23310     /**
23311      * @cfg {Boolean} displayInfo
23312      * True to display the displayMsg (defaults to false)
23313      */
23314     /**
23315      * @cfg {Number} pageSize
23316      * The number of records to display per page (defaults to 20)
23317      */
23318     pageSize: 20,
23319     /**
23320      * @cfg {String} displayMsg
23321      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23322      */
23323     displayMsg : 'Displaying {0} - {1} of {2}',
23324     /**
23325      * @cfg {String} emptyMsg
23326      * The message to display when no records are found (defaults to "No data to display")
23327      */
23328     emptyMsg : 'No data to display',
23329     /**
23330      * Customizable piece of the default paging text (defaults to "Page")
23331      * @type String
23332      */
23333     beforePageText : "Page",
23334     /**
23335      * Customizable piece of the default paging text (defaults to "of %0")
23336      * @type String
23337      */
23338     afterPageText : "of {0}",
23339     /**
23340      * Customizable piece of the default paging text (defaults to "First Page")
23341      * @type String
23342      */
23343     firstText : "First Page",
23344     /**
23345      * Customizable piece of the default paging text (defaults to "Previous Page")
23346      * @type String
23347      */
23348     prevText : "Previous Page",
23349     /**
23350      * Customizable piece of the default paging text (defaults to "Next Page")
23351      * @type String
23352      */
23353     nextText : "Next Page",
23354     /**
23355      * Customizable piece of the default paging text (defaults to "Last Page")
23356      * @type String
23357      */
23358     lastText : "Last Page",
23359     /**
23360      * Customizable piece of the default paging text (defaults to "Refresh")
23361      * @type String
23362      */
23363     refreshText : "Refresh",
23364
23365     buttons : false,
23366     // private
23367     onRender : function(ct, position) 
23368     {
23369         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23370         this.navgroup.parentId = this.id;
23371         this.navgroup.onRender(this.el, null);
23372         // add the buttons to the navgroup
23373         
23374         if(this.displayInfo){
23375             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23376             this.displayEl = this.el.select('.x-paging-info', true).first();
23377 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23378 //            this.displayEl = navel.el.select('span',true).first();
23379         }
23380         
23381         var _this = this;
23382         
23383         if(this.buttons){
23384             Roo.each(_this.buttons, function(e){ // this might need to use render????
23385                Roo.factory(e).onRender(_this.el, null);
23386             });
23387         }
23388             
23389         Roo.each(_this.toolbarItems, function(e) {
23390             _this.navgroup.addItem(e);
23391         });
23392         
23393         
23394         this.first = this.navgroup.addItem({
23395             tooltip: this.firstText,
23396             cls: "prev",
23397             icon : 'fa fa-backward',
23398             disabled: true,
23399             preventDefault: true,
23400             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23401         });
23402         
23403         this.prev =  this.navgroup.addItem({
23404             tooltip: this.prevText,
23405             cls: "prev",
23406             icon : 'fa fa-step-backward',
23407             disabled: true,
23408             preventDefault: true,
23409             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23410         });
23411     //this.addSeparator();
23412         
23413         
23414         var field = this.navgroup.addItem( {
23415             tagtype : 'span',
23416             cls : 'x-paging-position',
23417             
23418             html : this.beforePageText  +
23419                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23420                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23421          } ); //?? escaped?
23422         
23423         this.field = field.el.select('input', true).first();
23424         this.field.on("keydown", this.onPagingKeydown, this);
23425         this.field.on("focus", function(){this.dom.select();});
23426     
23427     
23428         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23429         //this.field.setHeight(18);
23430         //this.addSeparator();
23431         this.next = this.navgroup.addItem({
23432             tooltip: this.nextText,
23433             cls: "next",
23434             html : ' <i class="fa fa-step-forward">',
23435             disabled: true,
23436             preventDefault: true,
23437             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23438         });
23439         this.last = this.navgroup.addItem({
23440             tooltip: this.lastText,
23441             icon : 'fa fa-forward',
23442             cls: "next",
23443             disabled: true,
23444             preventDefault: true,
23445             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23446         });
23447     //this.addSeparator();
23448         this.loading = this.navgroup.addItem({
23449             tooltip: this.refreshText,
23450             icon: 'fa fa-refresh',
23451             preventDefault: true,
23452             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23453         });
23454         
23455     },
23456
23457     // private
23458     updateInfo : function(){
23459         if(this.displayEl){
23460             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23461             var msg = count == 0 ?
23462                 this.emptyMsg :
23463                 String.format(
23464                     this.displayMsg,
23465                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23466                 );
23467             this.displayEl.update(msg);
23468         }
23469     },
23470
23471     // private
23472     onLoad : function(ds, r, o){
23473        this.cursor = o.params ? o.params.start : 0;
23474        var d = this.getPageData(),
23475             ap = d.activePage,
23476             ps = d.pages;
23477         
23478        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23479        this.field.dom.value = ap;
23480        this.first.setDisabled(ap == 1);
23481        this.prev.setDisabled(ap == 1);
23482        this.next.setDisabled(ap == ps);
23483        this.last.setDisabled(ap == ps);
23484        this.loading.enable();
23485        this.updateInfo();
23486     },
23487
23488     // private
23489     getPageData : function(){
23490         var total = this.ds.getTotalCount();
23491         return {
23492             total : total,
23493             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23494             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23495         };
23496     },
23497
23498     // private
23499     onLoadError : function(){
23500         this.loading.enable();
23501     },
23502
23503     // private
23504     onPagingKeydown : function(e){
23505         var k = e.getKey();
23506         var d = this.getPageData();
23507         if(k == e.RETURN){
23508             var v = this.field.dom.value, pageNum;
23509             if(!v || isNaN(pageNum = parseInt(v, 10))){
23510                 this.field.dom.value = d.activePage;
23511                 return;
23512             }
23513             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23514             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23515             e.stopEvent();
23516         }
23517         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))
23518         {
23519           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23520           this.field.dom.value = pageNum;
23521           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23522           e.stopEvent();
23523         }
23524         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23525         {
23526           var v = this.field.dom.value, pageNum; 
23527           var increment = (e.shiftKey) ? 10 : 1;
23528           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23529                 increment *= -1;
23530           }
23531           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23532             this.field.dom.value = d.activePage;
23533             return;
23534           }
23535           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23536           {
23537             this.field.dom.value = parseInt(v, 10) + increment;
23538             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23539             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23540           }
23541           e.stopEvent();
23542         }
23543     },
23544
23545     // private
23546     beforeLoad : function(){
23547         if(this.loading){
23548             this.loading.disable();
23549         }
23550     },
23551
23552     // private
23553     onClick : function(which){
23554         
23555         var ds = this.ds;
23556         if (!ds) {
23557             return;
23558         }
23559         
23560         switch(which){
23561             case "first":
23562                 ds.load({params:{start: 0, limit: this.pageSize}});
23563             break;
23564             case "prev":
23565                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23566             break;
23567             case "next":
23568                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23569             break;
23570             case "last":
23571                 var total = ds.getTotalCount();
23572                 var extra = total % this.pageSize;
23573                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23574                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23575             break;
23576             case "refresh":
23577                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23578             break;
23579         }
23580     },
23581
23582     /**
23583      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23584      * @param {Roo.data.Store} store The data store to unbind
23585      */
23586     unbind : function(ds){
23587         ds.un("beforeload", this.beforeLoad, this);
23588         ds.un("load", this.onLoad, this);
23589         ds.un("loadexception", this.onLoadError, this);
23590         ds.un("remove", this.updateInfo, this);
23591         ds.un("add", this.updateInfo, this);
23592         this.ds = undefined;
23593     },
23594
23595     /**
23596      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23597      * @param {Roo.data.Store} store The data store to bind
23598      */
23599     bind : function(ds){
23600         ds.on("beforeload", this.beforeLoad, this);
23601         ds.on("load", this.onLoad, this);
23602         ds.on("loadexception", this.onLoadError, this);
23603         ds.on("remove", this.updateInfo, this);
23604         ds.on("add", this.updateInfo, this);
23605         this.ds = ds;
23606     }
23607 });/*
23608  * - LGPL
23609  *
23610  * element
23611  * 
23612  */
23613
23614 /**
23615  * @class Roo.bootstrap.MessageBar
23616  * @extends Roo.bootstrap.Component
23617  * Bootstrap MessageBar class
23618  * @cfg {String} html contents of the MessageBar
23619  * @cfg {String} weight (info | success | warning | danger) default info
23620  * @cfg {String} beforeClass insert the bar before the given class
23621  * @cfg {Boolean} closable (true | false) default false
23622  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23623  * 
23624  * @constructor
23625  * Create a new Element
23626  * @param {Object} config The config object
23627  */
23628
23629 Roo.bootstrap.MessageBar = function(config){
23630     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23631 };
23632
23633 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23634     
23635     html: '',
23636     weight: 'info',
23637     closable: false,
23638     fixed: false,
23639     beforeClass: 'bootstrap-sticky-wrap',
23640     
23641     getAutoCreate : function(){
23642         
23643         var cfg = {
23644             tag: 'div',
23645             cls: 'alert alert-dismissable alert-' + this.weight,
23646             cn: [
23647                 {
23648                     tag: 'span',
23649                     cls: 'message',
23650                     html: this.html || ''
23651                 }
23652             ]
23653         };
23654         
23655         if(this.fixed){
23656             cfg.cls += ' alert-messages-fixed';
23657         }
23658         
23659         if(this.closable){
23660             cfg.cn.push({
23661                 tag: 'button',
23662                 cls: 'close',
23663                 html: 'x'
23664             });
23665         }
23666         
23667         return cfg;
23668     },
23669     
23670     onRender : function(ct, position)
23671     {
23672         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23673         
23674         if(!this.el){
23675             var cfg = Roo.apply({},  this.getAutoCreate());
23676             cfg.id = Roo.id();
23677             
23678             if (this.cls) {
23679                 cfg.cls += ' ' + this.cls;
23680             }
23681             if (this.style) {
23682                 cfg.style = this.style;
23683             }
23684             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23685             
23686             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23687         }
23688         
23689         this.el.select('>button.close').on('click', this.hide, this);
23690         
23691     },
23692     
23693     show : function()
23694     {
23695         if (!this.rendered) {
23696             this.render();
23697         }
23698         
23699         this.el.show();
23700         
23701         this.fireEvent('show', this);
23702         
23703     },
23704     
23705     hide : function()
23706     {
23707         if (!this.rendered) {
23708             this.render();
23709         }
23710         
23711         this.el.hide();
23712         
23713         this.fireEvent('hide', this);
23714     },
23715     
23716     update : function()
23717     {
23718 //        var e = this.el.dom.firstChild;
23719 //        
23720 //        if(this.closable){
23721 //            e = e.nextSibling;
23722 //        }
23723 //        
23724 //        e.data = this.html || '';
23725
23726         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23727     }
23728    
23729 });
23730
23731  
23732
23733      /*
23734  * - LGPL
23735  *
23736  * Graph
23737  * 
23738  */
23739
23740
23741 /**
23742  * @class Roo.bootstrap.Graph
23743  * @extends Roo.bootstrap.Component
23744  * Bootstrap Graph class
23745 > Prameters
23746  -sm {number} sm 4
23747  -md {number} md 5
23748  @cfg {String} graphtype  bar | vbar | pie
23749  @cfg {number} g_x coodinator | centre x (pie)
23750  @cfg {number} g_y coodinator | centre y (pie)
23751  @cfg {number} g_r radius (pie)
23752  @cfg {number} g_height height of the chart (respected by all elements in the set)
23753  @cfg {number} g_width width of the chart (respected by all elements in the set)
23754  @cfg {Object} title The title of the chart
23755     
23756  -{Array}  values
23757  -opts (object) options for the chart 
23758      o {
23759      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23760      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23761      o vgutter (number)
23762      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.
23763      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23764      o to
23765      o stretch (boolean)
23766      o }
23767  -opts (object) options for the pie
23768      o{
23769      o cut
23770      o startAngle (number)
23771      o endAngle (number)
23772      } 
23773  *
23774  * @constructor
23775  * Create a new Input
23776  * @param {Object} config The config object
23777  */
23778
23779 Roo.bootstrap.Graph = function(config){
23780     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23781     
23782     this.addEvents({
23783         // img events
23784         /**
23785          * @event click
23786          * The img click event for the img.
23787          * @param {Roo.EventObject} e
23788          */
23789         "click" : true
23790     });
23791 };
23792
23793 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23794     
23795     sm: 4,
23796     md: 5,
23797     graphtype: 'bar',
23798     g_height: 250,
23799     g_width: 400,
23800     g_x: 50,
23801     g_y: 50,
23802     g_r: 30,
23803     opts:{
23804         //g_colors: this.colors,
23805         g_type: 'soft',
23806         g_gutter: '20%'
23807
23808     },
23809     title : false,
23810
23811     getAutoCreate : function(){
23812         
23813         var cfg = {
23814             tag: 'div',
23815             html : null
23816         };
23817         
23818         
23819         return  cfg;
23820     },
23821
23822     onRender : function(ct,position){
23823         
23824         
23825         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23826         
23827         if (typeof(Raphael) == 'undefined') {
23828             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23829             return;
23830         }
23831         
23832         this.raphael = Raphael(this.el.dom);
23833         
23834                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23835                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23836                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23837                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23838                 /*
23839                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23840                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23841                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23842                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23843                 
23844                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23845                 r.barchart(330, 10, 300, 220, data1);
23846                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23847                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23848                 */
23849                 
23850                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23851                 // r.barchart(30, 30, 560, 250,  xdata, {
23852                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23853                 //     axis : "0 0 1 1",
23854                 //     axisxlabels :  xdata
23855                 //     //yvalues : cols,
23856                    
23857                 // });
23858 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23859 //        
23860 //        this.load(null,xdata,{
23861 //                axis : "0 0 1 1",
23862 //                axisxlabels :  xdata
23863 //                });
23864
23865     },
23866
23867     load : function(graphtype,xdata,opts)
23868     {
23869         this.raphael.clear();
23870         if(!graphtype) {
23871             graphtype = this.graphtype;
23872         }
23873         if(!opts){
23874             opts = this.opts;
23875         }
23876         var r = this.raphael,
23877             fin = function () {
23878                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23879             },
23880             fout = function () {
23881                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23882             },
23883             pfin = function() {
23884                 this.sector.stop();
23885                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23886
23887                 if (this.label) {
23888                     this.label[0].stop();
23889                     this.label[0].attr({ r: 7.5 });
23890                     this.label[1].attr({ "font-weight": 800 });
23891                 }
23892             },
23893             pfout = function() {
23894                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23895
23896                 if (this.label) {
23897                     this.label[0].animate({ r: 5 }, 500, "bounce");
23898                     this.label[1].attr({ "font-weight": 400 });
23899                 }
23900             };
23901
23902         switch(graphtype){
23903             case 'bar':
23904                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23905                 break;
23906             case 'hbar':
23907                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23908                 break;
23909             case 'pie':
23910 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23911 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23912 //            
23913                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23914                 
23915                 break;
23916
23917         }
23918         
23919         if(this.title){
23920             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23921         }
23922         
23923     },
23924     
23925     setTitle: function(o)
23926     {
23927         this.title = o;
23928     },
23929     
23930     initEvents: function() {
23931         
23932         if(!this.href){
23933             this.el.on('click', this.onClick, this);
23934         }
23935     },
23936     
23937     onClick : function(e)
23938     {
23939         Roo.log('img onclick');
23940         this.fireEvent('click', this, e);
23941     }
23942    
23943 });
23944
23945  
23946 /*
23947  * - LGPL
23948  *
23949  * numberBox
23950  * 
23951  */
23952 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23953
23954 /**
23955  * @class Roo.bootstrap.dash.NumberBox
23956  * @extends Roo.bootstrap.Component
23957  * Bootstrap NumberBox class
23958  * @cfg {String} headline Box headline
23959  * @cfg {String} content Box content
23960  * @cfg {String} icon Box icon
23961  * @cfg {String} footer Footer text
23962  * @cfg {String} fhref Footer href
23963  * 
23964  * @constructor
23965  * Create a new NumberBox
23966  * @param {Object} config The config object
23967  */
23968
23969
23970 Roo.bootstrap.dash.NumberBox = function(config){
23971     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23972     
23973 };
23974
23975 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23976     
23977     headline : '',
23978     content : '',
23979     icon : '',
23980     footer : '',
23981     fhref : '',
23982     ficon : '',
23983     
23984     getAutoCreate : function(){
23985         
23986         var cfg = {
23987             tag : 'div',
23988             cls : 'small-box ',
23989             cn : [
23990                 {
23991                     tag : 'div',
23992                     cls : 'inner',
23993                     cn :[
23994                         {
23995                             tag : 'h3',
23996                             cls : 'roo-headline',
23997                             html : this.headline
23998                         },
23999                         {
24000                             tag : 'p',
24001                             cls : 'roo-content',
24002                             html : this.content
24003                         }
24004                     ]
24005                 }
24006             ]
24007         };
24008         
24009         if(this.icon){
24010             cfg.cn.push({
24011                 tag : 'div',
24012                 cls : 'icon',
24013                 cn :[
24014                     {
24015                         tag : 'i',
24016                         cls : 'ion ' + this.icon
24017                     }
24018                 ]
24019             });
24020         }
24021         
24022         if(this.footer){
24023             var footer = {
24024                 tag : 'a',
24025                 cls : 'small-box-footer',
24026                 href : this.fhref || '#',
24027                 html : this.footer
24028             };
24029             
24030             cfg.cn.push(footer);
24031             
24032         }
24033         
24034         return  cfg;
24035     },
24036
24037     onRender : function(ct,position){
24038         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24039
24040
24041        
24042                 
24043     },
24044
24045     setHeadline: function (value)
24046     {
24047         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24048     },
24049     
24050     setFooter: function (value, href)
24051     {
24052         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24053         
24054         if(href){
24055             this.el.select('a.small-box-footer',true).first().attr('href', href);
24056         }
24057         
24058     },
24059
24060     setContent: function (value)
24061     {
24062         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24063     },
24064
24065     initEvents: function() 
24066     {   
24067         
24068     }
24069     
24070 });
24071
24072  
24073 /*
24074  * - LGPL
24075  *
24076  * TabBox
24077  * 
24078  */
24079 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24080
24081 /**
24082  * @class Roo.bootstrap.dash.TabBox
24083  * @extends Roo.bootstrap.Component
24084  * Bootstrap TabBox class
24085  * @cfg {String} title Title of the TabBox
24086  * @cfg {String} icon Icon of the TabBox
24087  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24088  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24089  * 
24090  * @constructor
24091  * Create a new TabBox
24092  * @param {Object} config The config object
24093  */
24094
24095
24096 Roo.bootstrap.dash.TabBox = function(config){
24097     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24098     this.addEvents({
24099         // raw events
24100         /**
24101          * @event addpane
24102          * When a pane is added
24103          * @param {Roo.bootstrap.dash.TabPane} pane
24104          */
24105         "addpane" : true,
24106         /**
24107          * @event activatepane
24108          * When a pane is activated
24109          * @param {Roo.bootstrap.dash.TabPane} pane
24110          */
24111         "activatepane" : true
24112         
24113          
24114     });
24115     
24116     this.panes = [];
24117 };
24118
24119 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24120
24121     title : '',
24122     icon : false,
24123     showtabs : true,
24124     tabScrollable : false,
24125     
24126     getChildContainer : function()
24127     {
24128         return this.el.select('.tab-content', true).first();
24129     },
24130     
24131     getAutoCreate : function(){
24132         
24133         var header = {
24134             tag: 'li',
24135             cls: 'pull-left header',
24136             html: this.title,
24137             cn : []
24138         };
24139         
24140         if(this.icon){
24141             header.cn.push({
24142                 tag: 'i',
24143                 cls: 'fa ' + this.icon
24144             });
24145         }
24146         
24147         var h = {
24148             tag: 'ul',
24149             cls: 'nav nav-tabs pull-right',
24150             cn: [
24151                 header
24152             ]
24153         };
24154         
24155         if(this.tabScrollable){
24156             h = {
24157                 tag: 'div',
24158                 cls: 'tab-header',
24159                 cn: [
24160                     {
24161                         tag: 'ul',
24162                         cls: 'nav nav-tabs pull-right',
24163                         cn: [
24164                             header
24165                         ]
24166                     }
24167                 ]
24168             };
24169         }
24170         
24171         var cfg = {
24172             tag: 'div',
24173             cls: 'nav-tabs-custom',
24174             cn: [
24175                 h,
24176                 {
24177                     tag: 'div',
24178                     cls: 'tab-content no-padding',
24179                     cn: []
24180                 }
24181             ]
24182         };
24183
24184         return  cfg;
24185     },
24186     initEvents : function()
24187     {
24188         //Roo.log('add add pane handler');
24189         this.on('addpane', this.onAddPane, this);
24190     },
24191      /**
24192      * Updates the box title
24193      * @param {String} html to set the title to.
24194      */
24195     setTitle : function(value)
24196     {
24197         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24198     },
24199     onAddPane : function(pane)
24200     {
24201         this.panes.push(pane);
24202         //Roo.log('addpane');
24203         //Roo.log(pane);
24204         // tabs are rendere left to right..
24205         if(!this.showtabs){
24206             return;
24207         }
24208         
24209         var ctr = this.el.select('.nav-tabs', true).first();
24210          
24211          
24212         var existing = ctr.select('.nav-tab',true);
24213         var qty = existing.getCount();;
24214         
24215         
24216         var tab = ctr.createChild({
24217             tag : 'li',
24218             cls : 'nav-tab' + (qty ? '' : ' active'),
24219             cn : [
24220                 {
24221                     tag : 'a',
24222                     href:'#',
24223                     html : pane.title
24224                 }
24225             ]
24226         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24227         pane.tab = tab;
24228         
24229         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24230         if (!qty) {
24231             pane.el.addClass('active');
24232         }
24233         
24234                 
24235     },
24236     onTabClick : function(ev,un,ob,pane)
24237     {
24238         //Roo.log('tab - prev default');
24239         ev.preventDefault();
24240         
24241         
24242         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24243         pane.tab.addClass('active');
24244         //Roo.log(pane.title);
24245         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24246         // technically we should have a deactivate event.. but maybe add later.
24247         // and it should not de-activate the selected tab...
24248         this.fireEvent('activatepane', pane);
24249         pane.el.addClass('active');
24250         pane.fireEvent('activate');
24251         
24252         
24253     },
24254     
24255     getActivePane : function()
24256     {
24257         var r = false;
24258         Roo.each(this.panes, function(p) {
24259             if(p.el.hasClass('active')){
24260                 r = p;
24261                 return false;
24262             }
24263             
24264             return;
24265         });
24266         
24267         return r;
24268     }
24269     
24270     
24271 });
24272
24273  
24274 /*
24275  * - LGPL
24276  *
24277  * Tab pane
24278  * 
24279  */
24280 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24281 /**
24282  * @class Roo.bootstrap.TabPane
24283  * @extends Roo.bootstrap.Component
24284  * Bootstrap TabPane class
24285  * @cfg {Boolean} active (false | true) Default false
24286  * @cfg {String} title title of panel
24287
24288  * 
24289  * @constructor
24290  * Create a new TabPane
24291  * @param {Object} config The config object
24292  */
24293
24294 Roo.bootstrap.dash.TabPane = function(config){
24295     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24296     
24297     this.addEvents({
24298         // raw events
24299         /**
24300          * @event activate
24301          * When a pane is activated
24302          * @param {Roo.bootstrap.dash.TabPane} pane
24303          */
24304         "activate" : true
24305          
24306     });
24307 };
24308
24309 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24310     
24311     active : false,
24312     title : '',
24313     
24314     // the tabBox that this is attached to.
24315     tab : false,
24316      
24317     getAutoCreate : function() 
24318     {
24319         var cfg = {
24320             tag: 'div',
24321             cls: 'tab-pane'
24322         };
24323         
24324         if(this.active){
24325             cfg.cls += ' active';
24326         }
24327         
24328         return cfg;
24329     },
24330     initEvents  : function()
24331     {
24332         //Roo.log('trigger add pane handler');
24333         this.parent().fireEvent('addpane', this)
24334     },
24335     
24336      /**
24337      * Updates the tab title 
24338      * @param {String} html to set the title to.
24339      */
24340     setTitle: function(str)
24341     {
24342         if (!this.tab) {
24343             return;
24344         }
24345         this.title = str;
24346         this.tab.select('a', true).first().dom.innerHTML = str;
24347         
24348     }
24349     
24350     
24351     
24352 });
24353
24354  
24355
24356
24357  /*
24358  * - LGPL
24359  *
24360  * menu
24361  * 
24362  */
24363 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24364
24365 /**
24366  * @class Roo.bootstrap.menu.Menu
24367  * @extends Roo.bootstrap.Component
24368  * Bootstrap Menu class - container for Menu
24369  * @cfg {String} html Text of the menu
24370  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24371  * @cfg {String} icon Font awesome icon
24372  * @cfg {String} pos Menu align to (top | bottom) default bottom
24373  * 
24374  * 
24375  * @constructor
24376  * Create a new Menu
24377  * @param {Object} config The config object
24378  */
24379
24380
24381 Roo.bootstrap.menu.Menu = function(config){
24382     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24383     
24384     this.addEvents({
24385         /**
24386          * @event beforeshow
24387          * Fires before this menu is displayed
24388          * @param {Roo.bootstrap.menu.Menu} this
24389          */
24390         beforeshow : true,
24391         /**
24392          * @event beforehide
24393          * Fires before this menu is hidden
24394          * @param {Roo.bootstrap.menu.Menu} this
24395          */
24396         beforehide : true,
24397         /**
24398          * @event show
24399          * Fires after this menu is displayed
24400          * @param {Roo.bootstrap.menu.Menu} this
24401          */
24402         show : true,
24403         /**
24404          * @event hide
24405          * Fires after this menu is hidden
24406          * @param {Roo.bootstrap.menu.Menu} this
24407          */
24408         hide : true,
24409         /**
24410          * @event click
24411          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24412          * @param {Roo.bootstrap.menu.Menu} this
24413          * @param {Roo.EventObject} e
24414          */
24415         click : true
24416     });
24417     
24418 };
24419
24420 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24421     
24422     submenu : false,
24423     html : '',
24424     weight : 'default',
24425     icon : false,
24426     pos : 'bottom',
24427     
24428     
24429     getChildContainer : function() {
24430         if(this.isSubMenu){
24431             return this.el;
24432         }
24433         
24434         return this.el.select('ul.dropdown-menu', true).first();  
24435     },
24436     
24437     getAutoCreate : function()
24438     {
24439         var text = [
24440             {
24441                 tag : 'span',
24442                 cls : 'roo-menu-text',
24443                 html : this.html
24444             }
24445         ];
24446         
24447         if(this.icon){
24448             text.unshift({
24449                 tag : 'i',
24450                 cls : 'fa ' + this.icon
24451             })
24452         }
24453         
24454         
24455         var cfg = {
24456             tag : 'div',
24457             cls : 'btn-group',
24458             cn : [
24459                 {
24460                     tag : 'button',
24461                     cls : 'dropdown-button btn btn-' + this.weight,
24462                     cn : text
24463                 },
24464                 {
24465                     tag : 'button',
24466                     cls : 'dropdown-toggle btn btn-' + this.weight,
24467                     cn : [
24468                         {
24469                             tag : 'span',
24470                             cls : 'caret'
24471                         }
24472                     ]
24473                 },
24474                 {
24475                     tag : 'ul',
24476                     cls : 'dropdown-menu'
24477                 }
24478             ]
24479             
24480         };
24481         
24482         if(this.pos == 'top'){
24483             cfg.cls += ' dropup';
24484         }
24485         
24486         if(this.isSubMenu){
24487             cfg = {
24488                 tag : 'ul',
24489                 cls : 'dropdown-menu'
24490             }
24491         }
24492         
24493         return cfg;
24494     },
24495     
24496     onRender : function(ct, position)
24497     {
24498         this.isSubMenu = ct.hasClass('dropdown-submenu');
24499         
24500         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24501     },
24502     
24503     initEvents : function() 
24504     {
24505         if(this.isSubMenu){
24506             return;
24507         }
24508         
24509         this.hidden = true;
24510         
24511         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24512         this.triggerEl.on('click', this.onTriggerPress, this);
24513         
24514         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24515         this.buttonEl.on('click', this.onClick, this);
24516         
24517     },
24518     
24519     list : function()
24520     {
24521         if(this.isSubMenu){
24522             return this.el;
24523         }
24524         
24525         return this.el.select('ul.dropdown-menu', true).first();
24526     },
24527     
24528     onClick : function(e)
24529     {
24530         this.fireEvent("click", this, e);
24531     },
24532     
24533     onTriggerPress  : function(e)
24534     {   
24535         if (this.isVisible()) {
24536             this.hide();
24537         } else {
24538             this.show();
24539         }
24540     },
24541     
24542     isVisible : function(){
24543         return !this.hidden;
24544     },
24545     
24546     show : function()
24547     {
24548         this.fireEvent("beforeshow", this);
24549         
24550         this.hidden = false;
24551         this.el.addClass('open');
24552         
24553         Roo.get(document).on("mouseup", this.onMouseUp, this);
24554         
24555         this.fireEvent("show", this);
24556         
24557         
24558     },
24559     
24560     hide : function()
24561     {
24562         this.fireEvent("beforehide", this);
24563         
24564         this.hidden = true;
24565         this.el.removeClass('open');
24566         
24567         Roo.get(document).un("mouseup", this.onMouseUp);
24568         
24569         this.fireEvent("hide", this);
24570     },
24571     
24572     onMouseUp : function()
24573     {
24574         this.hide();
24575     }
24576     
24577 });
24578
24579  
24580  /*
24581  * - LGPL
24582  *
24583  * menu item
24584  * 
24585  */
24586 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24587
24588 /**
24589  * @class Roo.bootstrap.menu.Item
24590  * @extends Roo.bootstrap.Component
24591  * Bootstrap MenuItem class
24592  * @cfg {Boolean} submenu (true | false) default false
24593  * @cfg {String} html text of the item
24594  * @cfg {String} href the link
24595  * @cfg {Boolean} disable (true | false) default false
24596  * @cfg {Boolean} preventDefault (true | false) default true
24597  * @cfg {String} icon Font awesome icon
24598  * @cfg {String} pos Submenu align to (left | right) default right 
24599  * 
24600  * 
24601  * @constructor
24602  * Create a new Item
24603  * @param {Object} config The config object
24604  */
24605
24606
24607 Roo.bootstrap.menu.Item = function(config){
24608     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24609     this.addEvents({
24610         /**
24611          * @event mouseover
24612          * Fires when the mouse is hovering over this menu
24613          * @param {Roo.bootstrap.menu.Item} this
24614          * @param {Roo.EventObject} e
24615          */
24616         mouseover : true,
24617         /**
24618          * @event mouseout
24619          * Fires when the mouse exits this menu
24620          * @param {Roo.bootstrap.menu.Item} this
24621          * @param {Roo.EventObject} e
24622          */
24623         mouseout : true,
24624         // raw events
24625         /**
24626          * @event click
24627          * The raw click event for the entire grid.
24628          * @param {Roo.EventObject} e
24629          */
24630         click : true
24631     });
24632 };
24633
24634 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24635     
24636     submenu : false,
24637     href : '',
24638     html : '',
24639     preventDefault: true,
24640     disable : false,
24641     icon : false,
24642     pos : 'right',
24643     
24644     getAutoCreate : function()
24645     {
24646         var text = [
24647             {
24648                 tag : 'span',
24649                 cls : 'roo-menu-item-text',
24650                 html : this.html
24651             }
24652         ];
24653         
24654         if(this.icon){
24655             text.unshift({
24656                 tag : 'i',
24657                 cls : 'fa ' + this.icon
24658             })
24659         }
24660         
24661         var cfg = {
24662             tag : 'li',
24663             cn : [
24664                 {
24665                     tag : 'a',
24666                     href : this.href || '#',
24667                     cn : text
24668                 }
24669             ]
24670         };
24671         
24672         if(this.disable){
24673             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24674         }
24675         
24676         if(this.submenu){
24677             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24678             
24679             if(this.pos == 'left'){
24680                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24681             }
24682         }
24683         
24684         return cfg;
24685     },
24686     
24687     initEvents : function() 
24688     {
24689         this.el.on('mouseover', this.onMouseOver, this);
24690         this.el.on('mouseout', this.onMouseOut, this);
24691         
24692         this.el.select('a', true).first().on('click', this.onClick, this);
24693         
24694     },
24695     
24696     onClick : function(e)
24697     {
24698         if(this.preventDefault){
24699             e.preventDefault();
24700         }
24701         
24702         this.fireEvent("click", this, e);
24703     },
24704     
24705     onMouseOver : function(e)
24706     {
24707         if(this.submenu && this.pos == 'left'){
24708             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24709         }
24710         
24711         this.fireEvent("mouseover", this, e);
24712     },
24713     
24714     onMouseOut : function(e)
24715     {
24716         this.fireEvent("mouseout", this, e);
24717     }
24718 });
24719
24720  
24721
24722  /*
24723  * - LGPL
24724  *
24725  * menu separator
24726  * 
24727  */
24728 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24729
24730 /**
24731  * @class Roo.bootstrap.menu.Separator
24732  * @extends Roo.bootstrap.Component
24733  * Bootstrap Separator class
24734  * 
24735  * @constructor
24736  * Create a new Separator
24737  * @param {Object} config The config object
24738  */
24739
24740
24741 Roo.bootstrap.menu.Separator = function(config){
24742     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24743 };
24744
24745 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24746     
24747     getAutoCreate : function(){
24748         var cfg = {
24749             tag : 'li',
24750             cls: 'divider'
24751         };
24752         
24753         return cfg;
24754     }
24755    
24756 });
24757
24758  
24759
24760  /*
24761  * - LGPL
24762  *
24763  * Tooltip
24764  * 
24765  */
24766
24767 /**
24768  * @class Roo.bootstrap.Tooltip
24769  * Bootstrap Tooltip class
24770  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24771  * to determine which dom element triggers the tooltip.
24772  * 
24773  * It needs to add support for additional attributes like tooltip-position
24774  * 
24775  * @constructor
24776  * Create a new Toolti
24777  * @param {Object} config The config object
24778  */
24779
24780 Roo.bootstrap.Tooltip = function(config){
24781     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24782 };
24783
24784 Roo.apply(Roo.bootstrap.Tooltip, {
24785     /**
24786      * @function init initialize tooltip monitoring.
24787      * @static
24788      */
24789     currentEl : false,
24790     currentTip : false,
24791     currentRegion : false,
24792     
24793     //  init : delay?
24794     
24795     init : function()
24796     {
24797         Roo.get(document).on('mouseover', this.enter ,this);
24798         Roo.get(document).on('mouseout', this.leave, this);
24799          
24800         
24801         this.currentTip = new Roo.bootstrap.Tooltip();
24802     },
24803     
24804     enter : function(ev)
24805     {
24806         var dom = ev.getTarget();
24807         
24808         //Roo.log(['enter',dom]);
24809         var el = Roo.fly(dom);
24810         if (this.currentEl) {
24811             //Roo.log(dom);
24812             //Roo.log(this.currentEl);
24813             //Roo.log(this.currentEl.contains(dom));
24814             if (this.currentEl == el) {
24815                 return;
24816             }
24817             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24818                 return;
24819             }
24820
24821         }
24822         
24823         if (this.currentTip.el) {
24824             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24825         }    
24826         //Roo.log(ev);
24827         
24828         if(!el || el.dom == document){
24829             return;
24830         }
24831         
24832         var bindEl = el;
24833         
24834         // you can not look for children, as if el is the body.. then everythign is the child..
24835         if (!el.attr('tooltip')) { //
24836             if (!el.select("[tooltip]").elements.length) {
24837                 return;
24838             }
24839             // is the mouse over this child...?
24840             bindEl = el.select("[tooltip]").first();
24841             var xy = ev.getXY();
24842             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24843                 //Roo.log("not in region.");
24844                 return;
24845             }
24846             //Roo.log("child element over..");
24847             
24848         }
24849         this.currentEl = bindEl;
24850         this.currentTip.bind(bindEl);
24851         this.currentRegion = Roo.lib.Region.getRegion(dom);
24852         this.currentTip.enter();
24853         
24854     },
24855     leave : function(ev)
24856     {
24857         var dom = ev.getTarget();
24858         //Roo.log(['leave',dom]);
24859         if (!this.currentEl) {
24860             return;
24861         }
24862         
24863         
24864         if (dom != this.currentEl.dom) {
24865             return;
24866         }
24867         var xy = ev.getXY();
24868         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24869             return;
24870         }
24871         // only activate leave if mouse cursor is outside... bounding box..
24872         
24873         
24874         
24875         
24876         if (this.currentTip) {
24877             this.currentTip.leave();
24878         }
24879         //Roo.log('clear currentEl');
24880         this.currentEl = false;
24881         
24882         
24883     },
24884     alignment : {
24885         'left' : ['r-l', [-2,0], 'right'],
24886         'right' : ['l-r', [2,0], 'left'],
24887         'bottom' : ['t-b', [0,2], 'top'],
24888         'top' : [ 'b-t', [0,-2], 'bottom']
24889     }
24890     
24891 });
24892
24893
24894 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24895     
24896     
24897     bindEl : false,
24898     
24899     delay : null, // can be { show : 300 , hide: 500}
24900     
24901     timeout : null,
24902     
24903     hoverState : null, //???
24904     
24905     placement : 'bottom', 
24906     
24907     getAutoCreate : function(){
24908     
24909         var cfg = {
24910            cls : 'tooltip',
24911            role : 'tooltip',
24912            cn : [
24913                 {
24914                     cls : 'tooltip-arrow'
24915                 },
24916                 {
24917                     cls : 'tooltip-inner'
24918                 }
24919            ]
24920         };
24921         
24922         return cfg;
24923     },
24924     bind : function(el)
24925     {
24926         this.bindEl = el;
24927     },
24928       
24929     
24930     enter : function () {
24931        
24932         if (this.timeout != null) {
24933             clearTimeout(this.timeout);
24934         }
24935         
24936         this.hoverState = 'in';
24937          //Roo.log("enter - show");
24938         if (!this.delay || !this.delay.show) {
24939             this.show();
24940             return;
24941         }
24942         var _t = this;
24943         this.timeout = setTimeout(function () {
24944             if (_t.hoverState == 'in') {
24945                 _t.show();
24946             }
24947         }, this.delay.show);
24948     },
24949     leave : function()
24950     {
24951         clearTimeout(this.timeout);
24952     
24953         this.hoverState = 'out';
24954          if (!this.delay || !this.delay.hide) {
24955             this.hide();
24956             return;
24957         }
24958        
24959         var _t = this;
24960         this.timeout = setTimeout(function () {
24961             //Roo.log("leave - timeout");
24962             
24963             if (_t.hoverState == 'out') {
24964                 _t.hide();
24965                 Roo.bootstrap.Tooltip.currentEl = false;
24966             }
24967         }, delay);
24968     },
24969     
24970     show : function ()
24971     {
24972         if (!this.el) {
24973             this.render(document.body);
24974         }
24975         // set content.
24976         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24977         
24978         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24979         
24980         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24981         
24982         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24983         
24984         var placement = typeof this.placement == 'function' ?
24985             this.placement.call(this, this.el, on_el) :
24986             this.placement;
24987             
24988         var autoToken = /\s?auto?\s?/i;
24989         var autoPlace = autoToken.test(placement);
24990         if (autoPlace) {
24991             placement = placement.replace(autoToken, '') || 'top';
24992         }
24993         
24994         //this.el.detach()
24995         //this.el.setXY([0,0]);
24996         this.el.show();
24997         //this.el.dom.style.display='block';
24998         
24999         //this.el.appendTo(on_el);
25000         
25001         var p = this.getPosition();
25002         var box = this.el.getBox();
25003         
25004         if (autoPlace) {
25005             // fixme..
25006         }
25007         
25008         var align = Roo.bootstrap.Tooltip.alignment[placement];
25009         
25010         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25011         
25012         if(placement == 'top' || placement == 'bottom'){
25013             if(xy[0] < 0){
25014                 placement = 'right';
25015             }
25016             
25017             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25018                 placement = 'left';
25019             }
25020             
25021             var scroll = Roo.select('body', true).first().getScroll();
25022             
25023             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25024                 placement = 'top';
25025             }
25026             
25027         }
25028         
25029         align = Roo.bootstrap.Tooltip.alignment[placement];
25030         
25031         this.el.alignTo(this.bindEl, align[0],align[1]);
25032         //var arrow = this.el.select('.arrow',true).first();
25033         //arrow.set(align[2], 
25034         
25035         this.el.addClass(placement);
25036         
25037         this.el.addClass('in fade');
25038         
25039         this.hoverState = null;
25040         
25041         if (this.el.hasClass('fade')) {
25042             // fade it?
25043         }
25044         
25045     },
25046     hide : function()
25047     {
25048          
25049         if (!this.el) {
25050             return;
25051         }
25052         //this.el.setXY([0,0]);
25053         this.el.removeClass('in');
25054         //this.el.hide();
25055         
25056     }
25057     
25058 });
25059  
25060
25061  /*
25062  * - LGPL
25063  *
25064  * Location Picker
25065  * 
25066  */
25067
25068 /**
25069  * @class Roo.bootstrap.LocationPicker
25070  * @extends Roo.bootstrap.Component
25071  * Bootstrap LocationPicker class
25072  * @cfg {Number} latitude Position when init default 0
25073  * @cfg {Number} longitude Position when init default 0
25074  * @cfg {Number} zoom default 15
25075  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25076  * @cfg {Boolean} mapTypeControl default false
25077  * @cfg {Boolean} disableDoubleClickZoom default false
25078  * @cfg {Boolean} scrollwheel default true
25079  * @cfg {Boolean} streetViewControl default false
25080  * @cfg {Number} radius default 0
25081  * @cfg {String} locationName
25082  * @cfg {Boolean} draggable default true
25083  * @cfg {Boolean} enableAutocomplete default false
25084  * @cfg {Boolean} enableReverseGeocode default true
25085  * @cfg {String} markerTitle
25086  * 
25087  * @constructor
25088  * Create a new LocationPicker
25089  * @param {Object} config The config object
25090  */
25091
25092
25093 Roo.bootstrap.LocationPicker = function(config){
25094     
25095     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25096     
25097     this.addEvents({
25098         /**
25099          * @event initial
25100          * Fires when the picker initialized.
25101          * @param {Roo.bootstrap.LocationPicker} this
25102          * @param {Google Location} location
25103          */
25104         initial : true,
25105         /**
25106          * @event positionchanged
25107          * Fires when the picker position changed.
25108          * @param {Roo.bootstrap.LocationPicker} this
25109          * @param {Google Location} location
25110          */
25111         positionchanged : true,
25112         /**
25113          * @event resize
25114          * Fires when the map resize.
25115          * @param {Roo.bootstrap.LocationPicker} this
25116          */
25117         resize : true,
25118         /**
25119          * @event show
25120          * Fires when the map show.
25121          * @param {Roo.bootstrap.LocationPicker} this
25122          */
25123         show : true,
25124         /**
25125          * @event hide
25126          * Fires when the map hide.
25127          * @param {Roo.bootstrap.LocationPicker} this
25128          */
25129         hide : true,
25130         /**
25131          * @event mapClick
25132          * Fires when click the map.
25133          * @param {Roo.bootstrap.LocationPicker} this
25134          * @param {Map event} e
25135          */
25136         mapClick : true,
25137         /**
25138          * @event mapRightClick
25139          * Fires when right click the map.
25140          * @param {Roo.bootstrap.LocationPicker} this
25141          * @param {Map event} e
25142          */
25143         mapRightClick : true,
25144         /**
25145          * @event markerClick
25146          * Fires when click the marker.
25147          * @param {Roo.bootstrap.LocationPicker} this
25148          * @param {Map event} e
25149          */
25150         markerClick : true,
25151         /**
25152          * @event markerRightClick
25153          * Fires when right click the marker.
25154          * @param {Roo.bootstrap.LocationPicker} this
25155          * @param {Map event} e
25156          */
25157         markerRightClick : true,
25158         /**
25159          * @event OverlayViewDraw
25160          * Fires when OverlayView Draw
25161          * @param {Roo.bootstrap.LocationPicker} this
25162          */
25163         OverlayViewDraw : true,
25164         /**
25165          * @event OverlayViewOnAdd
25166          * Fires when OverlayView Draw
25167          * @param {Roo.bootstrap.LocationPicker} this
25168          */
25169         OverlayViewOnAdd : true,
25170         /**
25171          * @event OverlayViewOnRemove
25172          * Fires when OverlayView Draw
25173          * @param {Roo.bootstrap.LocationPicker} this
25174          */
25175         OverlayViewOnRemove : true,
25176         /**
25177          * @event OverlayViewShow
25178          * Fires when OverlayView Draw
25179          * @param {Roo.bootstrap.LocationPicker} this
25180          * @param {Pixel} cpx
25181          */
25182         OverlayViewShow : true,
25183         /**
25184          * @event OverlayViewHide
25185          * Fires when OverlayView Draw
25186          * @param {Roo.bootstrap.LocationPicker} this
25187          */
25188         OverlayViewHide : true,
25189         /**
25190          * @event loadexception
25191          * Fires when load google lib failed.
25192          * @param {Roo.bootstrap.LocationPicker} this
25193          */
25194         loadexception : true
25195     });
25196         
25197 };
25198
25199 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25200     
25201     gMapContext: false,
25202     
25203     latitude: 0,
25204     longitude: 0,
25205     zoom: 15,
25206     mapTypeId: false,
25207     mapTypeControl: false,
25208     disableDoubleClickZoom: false,
25209     scrollwheel: true,
25210     streetViewControl: false,
25211     radius: 0,
25212     locationName: '',
25213     draggable: true,
25214     enableAutocomplete: false,
25215     enableReverseGeocode: true,
25216     markerTitle: '',
25217     
25218     getAutoCreate: function()
25219     {
25220
25221         var cfg = {
25222             tag: 'div',
25223             cls: 'roo-location-picker'
25224         };
25225         
25226         return cfg
25227     },
25228     
25229     initEvents: function(ct, position)
25230     {       
25231         if(!this.el.getWidth() || this.isApplied()){
25232             return;
25233         }
25234         
25235         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25236         
25237         this.initial();
25238     },
25239     
25240     initial: function()
25241     {
25242         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25243             this.fireEvent('loadexception', this);
25244             return;
25245         }
25246         
25247         if(!this.mapTypeId){
25248             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25249         }
25250         
25251         this.gMapContext = this.GMapContext();
25252         
25253         this.initOverlayView();
25254         
25255         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25256         
25257         var _this = this;
25258                 
25259         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25260             _this.setPosition(_this.gMapContext.marker.position);
25261         });
25262         
25263         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25264             _this.fireEvent('mapClick', this, event);
25265             
25266         });
25267
25268         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25269             _this.fireEvent('mapRightClick', this, event);
25270             
25271         });
25272         
25273         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25274             _this.fireEvent('markerClick', this, event);
25275             
25276         });
25277
25278         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25279             _this.fireEvent('markerRightClick', this, event);
25280             
25281         });
25282         
25283         this.setPosition(this.gMapContext.location);
25284         
25285         this.fireEvent('initial', this, this.gMapContext.location);
25286     },
25287     
25288     initOverlayView: function()
25289     {
25290         var _this = this;
25291         
25292         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25293             
25294             draw: function()
25295             {
25296                 _this.fireEvent('OverlayViewDraw', _this);
25297             },
25298             
25299             onAdd: function()
25300             {
25301                 _this.fireEvent('OverlayViewOnAdd', _this);
25302             },
25303             
25304             onRemove: function()
25305             {
25306                 _this.fireEvent('OverlayViewOnRemove', _this);
25307             },
25308             
25309             show: function(cpx)
25310             {
25311                 _this.fireEvent('OverlayViewShow', _this, cpx);
25312             },
25313             
25314             hide: function()
25315             {
25316                 _this.fireEvent('OverlayViewHide', _this);
25317             }
25318             
25319         });
25320     },
25321     
25322     fromLatLngToContainerPixel: function(event)
25323     {
25324         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25325     },
25326     
25327     isApplied: function() 
25328     {
25329         return this.getGmapContext() == false ? false : true;
25330     },
25331     
25332     getGmapContext: function() 
25333     {
25334         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25335     },
25336     
25337     GMapContext: function() 
25338     {
25339         var position = new google.maps.LatLng(this.latitude, this.longitude);
25340         
25341         var _map = new google.maps.Map(this.el.dom, {
25342             center: position,
25343             zoom: this.zoom,
25344             mapTypeId: this.mapTypeId,
25345             mapTypeControl: this.mapTypeControl,
25346             disableDoubleClickZoom: this.disableDoubleClickZoom,
25347             scrollwheel: this.scrollwheel,
25348             streetViewControl: this.streetViewControl,
25349             locationName: this.locationName,
25350             draggable: this.draggable,
25351             enableAutocomplete: this.enableAutocomplete,
25352             enableReverseGeocode: this.enableReverseGeocode
25353         });
25354         
25355         var _marker = new google.maps.Marker({
25356             position: position,
25357             map: _map,
25358             title: this.markerTitle,
25359             draggable: this.draggable
25360         });
25361         
25362         return {
25363             map: _map,
25364             marker: _marker,
25365             circle: null,
25366             location: position,
25367             radius: this.radius,
25368             locationName: this.locationName,
25369             addressComponents: {
25370                 formatted_address: null,
25371                 addressLine1: null,
25372                 addressLine2: null,
25373                 streetName: null,
25374                 streetNumber: null,
25375                 city: null,
25376                 district: null,
25377                 state: null,
25378                 stateOrProvince: null
25379             },
25380             settings: this,
25381             domContainer: this.el.dom,
25382             geodecoder: new google.maps.Geocoder()
25383         };
25384     },
25385     
25386     drawCircle: function(center, radius, options) 
25387     {
25388         if (this.gMapContext.circle != null) {
25389             this.gMapContext.circle.setMap(null);
25390         }
25391         if (radius > 0) {
25392             radius *= 1;
25393             options = Roo.apply({}, options, {
25394                 strokeColor: "#0000FF",
25395                 strokeOpacity: .35,
25396                 strokeWeight: 2,
25397                 fillColor: "#0000FF",
25398                 fillOpacity: .2
25399             });
25400             
25401             options.map = this.gMapContext.map;
25402             options.radius = radius;
25403             options.center = center;
25404             this.gMapContext.circle = new google.maps.Circle(options);
25405             return this.gMapContext.circle;
25406         }
25407         
25408         return null;
25409     },
25410     
25411     setPosition: function(location) 
25412     {
25413         this.gMapContext.location = location;
25414         this.gMapContext.marker.setPosition(location);
25415         this.gMapContext.map.panTo(location);
25416         this.drawCircle(location, this.gMapContext.radius, {});
25417         
25418         var _this = this;
25419         
25420         if (this.gMapContext.settings.enableReverseGeocode) {
25421             this.gMapContext.geodecoder.geocode({
25422                 latLng: this.gMapContext.location
25423             }, function(results, status) {
25424                 
25425                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25426                     _this.gMapContext.locationName = results[0].formatted_address;
25427                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25428                     
25429                     _this.fireEvent('positionchanged', this, location);
25430                 }
25431             });
25432             
25433             return;
25434         }
25435         
25436         this.fireEvent('positionchanged', this, location);
25437     },
25438     
25439     resize: function()
25440     {
25441         google.maps.event.trigger(this.gMapContext.map, "resize");
25442         
25443         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25444         
25445         this.fireEvent('resize', this);
25446     },
25447     
25448     setPositionByLatLng: function(latitude, longitude)
25449     {
25450         this.setPosition(new google.maps.LatLng(latitude, longitude));
25451     },
25452     
25453     getCurrentPosition: function() 
25454     {
25455         return {
25456             latitude: this.gMapContext.location.lat(),
25457             longitude: this.gMapContext.location.lng()
25458         };
25459     },
25460     
25461     getAddressName: function() 
25462     {
25463         return this.gMapContext.locationName;
25464     },
25465     
25466     getAddressComponents: function() 
25467     {
25468         return this.gMapContext.addressComponents;
25469     },
25470     
25471     address_component_from_google_geocode: function(address_components) 
25472     {
25473         var result = {};
25474         
25475         for (var i = 0; i < address_components.length; i++) {
25476             var component = address_components[i];
25477             if (component.types.indexOf("postal_code") >= 0) {
25478                 result.postalCode = component.short_name;
25479             } else if (component.types.indexOf("street_number") >= 0) {
25480                 result.streetNumber = component.short_name;
25481             } else if (component.types.indexOf("route") >= 0) {
25482                 result.streetName = component.short_name;
25483             } else if (component.types.indexOf("neighborhood") >= 0) {
25484                 result.city = component.short_name;
25485             } else if (component.types.indexOf("locality") >= 0) {
25486                 result.city = component.short_name;
25487             } else if (component.types.indexOf("sublocality") >= 0) {
25488                 result.district = component.short_name;
25489             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25490                 result.stateOrProvince = component.short_name;
25491             } else if (component.types.indexOf("country") >= 0) {
25492                 result.country = component.short_name;
25493             }
25494         }
25495         
25496         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25497         result.addressLine2 = "";
25498         return result;
25499     },
25500     
25501     setZoomLevel: function(zoom)
25502     {
25503         this.gMapContext.map.setZoom(zoom);
25504     },
25505     
25506     show: function()
25507     {
25508         if(!this.el){
25509             return;
25510         }
25511         
25512         this.el.show();
25513         
25514         this.resize();
25515         
25516         this.fireEvent('show', this);
25517     },
25518     
25519     hide: function()
25520     {
25521         if(!this.el){
25522             return;
25523         }
25524         
25525         this.el.hide();
25526         
25527         this.fireEvent('hide', this);
25528     }
25529     
25530 });
25531
25532 Roo.apply(Roo.bootstrap.LocationPicker, {
25533     
25534     OverlayView : function(map, options)
25535     {
25536         options = options || {};
25537         
25538         this.setMap(map);
25539     }
25540     
25541     
25542 });/*
25543  * - LGPL
25544  *
25545  * Alert
25546  * 
25547  */
25548
25549 /**
25550  * @class Roo.bootstrap.Alert
25551  * @extends Roo.bootstrap.Component
25552  * Bootstrap Alert class
25553  * @cfg {String} title The title of alert
25554  * @cfg {String} html The content of alert
25555  * @cfg {String} weight (  success | info | warning | danger )
25556  * @cfg {String} faicon font-awesomeicon
25557  * 
25558  * @constructor
25559  * Create a new alert
25560  * @param {Object} config The config object
25561  */
25562
25563
25564 Roo.bootstrap.Alert = function(config){
25565     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25566     
25567 };
25568
25569 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25570     
25571     title: '',
25572     html: '',
25573     weight: false,
25574     faicon: false,
25575     
25576     getAutoCreate : function()
25577     {
25578         
25579         var cfg = {
25580             tag : 'div',
25581             cls : 'alert',
25582             cn : [
25583                 {
25584                     tag : 'i',
25585                     cls : 'roo-alert-icon'
25586                     
25587                 },
25588                 {
25589                     tag : 'b',
25590                     cls : 'roo-alert-title',
25591                     html : this.title
25592                 },
25593                 {
25594                     tag : 'span',
25595                     cls : 'roo-alert-text',
25596                     html : this.html
25597                 }
25598             ]
25599         };
25600         
25601         if(this.faicon){
25602             cfg.cn[0].cls += ' fa ' + this.faicon;
25603         }
25604         
25605         if(this.weight){
25606             cfg.cls += ' alert-' + this.weight;
25607         }
25608         
25609         return cfg;
25610     },
25611     
25612     initEvents: function() 
25613     {
25614         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25615     },
25616     
25617     setTitle : function(str)
25618     {
25619         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25620     },
25621     
25622     setText : function(str)
25623     {
25624         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25625     },
25626     
25627     setWeight : function(weight)
25628     {
25629         if(this.weight){
25630             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25631         }
25632         
25633         this.weight = weight;
25634         
25635         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25636     },
25637     
25638     setIcon : function(icon)
25639     {
25640         if(this.faicon){
25641             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25642         }
25643         
25644         this.faicon = icon;
25645         
25646         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25647     },
25648     
25649     hide: function() 
25650     {
25651         this.el.hide();   
25652     },
25653     
25654     show: function() 
25655     {  
25656         this.el.show();   
25657     }
25658     
25659 });
25660
25661  
25662 /*
25663 * Licence: LGPL
25664 */
25665
25666 /**
25667  * @class Roo.bootstrap.UploadCropbox
25668  * @extends Roo.bootstrap.Component
25669  * Bootstrap UploadCropbox class
25670  * @cfg {String} emptyText show when image has been loaded
25671  * @cfg {String} rotateNotify show when image too small to rotate
25672  * @cfg {Number} errorTimeout default 3000
25673  * @cfg {Number} minWidth default 300
25674  * @cfg {Number} minHeight default 300
25675  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25676  * @cfg {Boolean} isDocument (true|false) default false
25677  * @cfg {String} url action url
25678  * @cfg {String} paramName default 'imageUpload'
25679  * @cfg {String} method default POST
25680  * @cfg {Boolean} loadMask (true|false) default true
25681  * @cfg {Boolean} loadingText default 'Loading...'
25682  * 
25683  * @constructor
25684  * Create a new UploadCropbox
25685  * @param {Object} config The config object
25686  */
25687
25688 Roo.bootstrap.UploadCropbox = function(config){
25689     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25690     
25691     this.addEvents({
25692         /**
25693          * @event beforeselectfile
25694          * Fire before select file
25695          * @param {Roo.bootstrap.UploadCropbox} this
25696          */
25697         "beforeselectfile" : true,
25698         /**
25699          * @event initial
25700          * Fire after initEvent
25701          * @param {Roo.bootstrap.UploadCropbox} this
25702          */
25703         "initial" : true,
25704         /**
25705          * @event crop
25706          * Fire after initEvent
25707          * @param {Roo.bootstrap.UploadCropbox} this
25708          * @param {String} data
25709          */
25710         "crop" : true,
25711         /**
25712          * @event prepare
25713          * Fire when preparing the file data
25714          * @param {Roo.bootstrap.UploadCropbox} this
25715          * @param {Object} file
25716          */
25717         "prepare" : true,
25718         /**
25719          * @event exception
25720          * Fire when get exception
25721          * @param {Roo.bootstrap.UploadCropbox} this
25722          * @param {XMLHttpRequest} xhr
25723          */
25724         "exception" : true,
25725         /**
25726          * @event beforeloadcanvas
25727          * Fire before load the canvas
25728          * @param {Roo.bootstrap.UploadCropbox} this
25729          * @param {String} src
25730          */
25731         "beforeloadcanvas" : true,
25732         /**
25733          * @event trash
25734          * Fire when trash image
25735          * @param {Roo.bootstrap.UploadCropbox} this
25736          */
25737         "trash" : true,
25738         /**
25739          * @event download
25740          * Fire when download the image
25741          * @param {Roo.bootstrap.UploadCropbox} this
25742          */
25743         "download" : true,
25744         /**
25745          * @event footerbuttonclick
25746          * Fire when footerbuttonclick
25747          * @param {Roo.bootstrap.UploadCropbox} this
25748          * @param {String} type
25749          */
25750         "footerbuttonclick" : true,
25751         /**
25752          * @event resize
25753          * Fire when resize
25754          * @param {Roo.bootstrap.UploadCropbox} this
25755          */
25756         "resize" : true,
25757         /**
25758          * @event rotate
25759          * Fire when rotate the image
25760          * @param {Roo.bootstrap.UploadCropbox} this
25761          * @param {String} pos
25762          */
25763         "rotate" : true,
25764         /**
25765          * @event inspect
25766          * Fire when inspect the file
25767          * @param {Roo.bootstrap.UploadCropbox} this
25768          * @param {Object} file
25769          */
25770         "inspect" : true,
25771         /**
25772          * @event upload
25773          * Fire when xhr upload the file
25774          * @param {Roo.bootstrap.UploadCropbox} this
25775          * @param {Object} data
25776          */
25777         "upload" : true,
25778         /**
25779          * @event arrange
25780          * Fire when arrange the file data
25781          * @param {Roo.bootstrap.UploadCropbox} this
25782          * @param {Object} formData
25783          */
25784         "arrange" : true
25785     });
25786     
25787     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25788 };
25789
25790 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25791     
25792     emptyText : 'Click to upload image',
25793     rotateNotify : 'Image is too small to rotate',
25794     errorTimeout : 3000,
25795     scale : 0,
25796     baseScale : 1,
25797     rotate : 0,
25798     dragable : false,
25799     pinching : false,
25800     mouseX : 0,
25801     mouseY : 0,
25802     cropData : false,
25803     minWidth : 300,
25804     minHeight : 300,
25805     file : false,
25806     exif : {},
25807     baseRotate : 1,
25808     cropType : 'image/jpeg',
25809     buttons : false,
25810     canvasLoaded : false,
25811     isDocument : false,
25812     method : 'POST',
25813     paramName : 'imageUpload',
25814     loadMask : true,
25815     loadingText : 'Loading...',
25816     maskEl : false,
25817     
25818     getAutoCreate : function()
25819     {
25820         var cfg = {
25821             tag : 'div',
25822             cls : 'roo-upload-cropbox',
25823             cn : [
25824                 {
25825                     tag : 'input',
25826                     cls : 'roo-upload-cropbox-selector',
25827                     type : 'file'
25828                 },
25829                 {
25830                     tag : 'div',
25831                     cls : 'roo-upload-cropbox-body',
25832                     style : 'cursor:pointer',
25833                     cn : [
25834                         {
25835                             tag : 'div',
25836                             cls : 'roo-upload-cropbox-preview'
25837                         },
25838                         {
25839                             tag : 'div',
25840                             cls : 'roo-upload-cropbox-thumb'
25841                         },
25842                         {
25843                             tag : 'div',
25844                             cls : 'roo-upload-cropbox-empty-notify',
25845                             html : this.emptyText
25846                         },
25847                         {
25848                             tag : 'div',
25849                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25850                             html : this.rotateNotify
25851                         }
25852                     ]
25853                 },
25854                 {
25855                     tag : 'div',
25856                     cls : 'roo-upload-cropbox-footer',
25857                     cn : {
25858                         tag : 'div',
25859                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25860                         cn : []
25861                     }
25862                 }
25863             ]
25864         };
25865         
25866         return cfg;
25867     },
25868     
25869     onRender : function(ct, position)
25870     {
25871         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25872         
25873         if (this.buttons.length) {
25874             
25875             Roo.each(this.buttons, function(bb) {
25876                 
25877                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25878                 
25879                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25880                 
25881             }, this);
25882         }
25883         
25884         if(this.loadMask){
25885             this.maskEl = this.el;
25886         }
25887     },
25888     
25889     initEvents : function()
25890     {
25891         this.urlAPI = (window.createObjectURL && window) || 
25892                                 (window.URL && URL.revokeObjectURL && URL) || 
25893                                 (window.webkitURL && webkitURL);
25894                         
25895         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25896         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25897         
25898         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25899         this.selectorEl.hide();
25900         
25901         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25902         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25903         
25904         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25905         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25906         this.thumbEl.hide();
25907         
25908         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25909         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25910         
25911         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25912         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25913         this.errorEl.hide();
25914         
25915         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25916         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25917         this.footerEl.hide();
25918         
25919         this.setThumbBoxSize();
25920         
25921         this.bind();
25922         
25923         this.resize();
25924         
25925         this.fireEvent('initial', this);
25926     },
25927
25928     bind : function()
25929     {
25930         var _this = this;
25931         
25932         window.addEventListener("resize", function() { _this.resize(); } );
25933         
25934         this.bodyEl.on('click', this.beforeSelectFile, this);
25935         
25936         if(Roo.isTouch){
25937             this.bodyEl.on('touchstart', this.onTouchStart, this);
25938             this.bodyEl.on('touchmove', this.onTouchMove, this);
25939             this.bodyEl.on('touchend', this.onTouchEnd, this);
25940         }
25941         
25942         if(!Roo.isTouch){
25943             this.bodyEl.on('mousedown', this.onMouseDown, this);
25944             this.bodyEl.on('mousemove', this.onMouseMove, this);
25945             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25946             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25947             Roo.get(document).on('mouseup', this.onMouseUp, this);
25948         }
25949         
25950         this.selectorEl.on('change', this.onFileSelected, this);
25951     },
25952     
25953     reset : function()
25954     {    
25955         this.scale = 0;
25956         this.baseScale = 1;
25957         this.rotate = 0;
25958         this.baseRotate = 1;
25959         this.dragable = false;
25960         this.pinching = false;
25961         this.mouseX = 0;
25962         this.mouseY = 0;
25963         this.cropData = false;
25964         this.notifyEl.dom.innerHTML = this.emptyText;
25965         
25966         this.selectorEl.dom.value = '';
25967         
25968     },
25969     
25970     resize : function()
25971     {
25972         if(this.fireEvent('resize', this) != false){
25973             this.setThumbBoxPosition();
25974             this.setCanvasPosition();
25975         }
25976     },
25977     
25978     onFooterButtonClick : function(e, el, o, type)
25979     {
25980         switch (type) {
25981             case 'rotate-left' :
25982                 this.onRotateLeft(e);
25983                 break;
25984             case 'rotate-right' :
25985                 this.onRotateRight(e);
25986                 break;
25987             case 'picture' :
25988                 this.beforeSelectFile(e);
25989                 break;
25990             case 'trash' :
25991                 this.trash(e);
25992                 break;
25993             case 'crop' :
25994                 this.crop(e);
25995                 break;
25996             case 'download' :
25997                 this.download(e);
25998                 break;
25999             default :
26000                 break;
26001         }
26002         
26003         this.fireEvent('footerbuttonclick', this, type);
26004     },
26005     
26006     beforeSelectFile : function(e)
26007     {
26008         e.preventDefault();
26009         
26010         if(this.fireEvent('beforeselectfile', this) != false){
26011             this.selectorEl.dom.click();
26012         }
26013     },
26014     
26015     onFileSelected : function(e)
26016     {
26017         e.preventDefault();
26018         
26019         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26020             return;
26021         }
26022         
26023         var file = this.selectorEl.dom.files[0];
26024         
26025         if(this.fireEvent('inspect', this, file) != false){
26026             this.prepare(file);
26027         }
26028         
26029     },
26030     
26031     trash : function(e)
26032     {
26033         this.fireEvent('trash', this);
26034     },
26035     
26036     download : function(e)
26037     {
26038         this.fireEvent('download', this);
26039     },
26040     
26041     loadCanvas : function(src)
26042     {   
26043         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26044             
26045             this.reset();
26046             
26047             this.imageEl = document.createElement('img');
26048             
26049             var _this = this;
26050             
26051             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26052             
26053             this.imageEl.src = src;
26054         }
26055     },
26056     
26057     onLoadCanvas : function()
26058     {   
26059         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26060         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26061         
26062         this.bodyEl.un('click', this.beforeSelectFile, this);
26063         
26064         this.notifyEl.hide();
26065         this.thumbEl.show();
26066         this.footerEl.show();
26067         
26068         this.baseRotateLevel();
26069         
26070         if(this.isDocument){
26071             this.setThumbBoxSize();
26072         }
26073         
26074         this.setThumbBoxPosition();
26075         
26076         this.baseScaleLevel();
26077         
26078         this.draw();
26079         
26080         this.resize();
26081         
26082         this.canvasLoaded = true;
26083         
26084         if(this.loadMask){
26085             this.maskEl.unmask();
26086         }
26087         
26088     },
26089     
26090     setCanvasPosition : function()
26091     {   
26092         if(!this.canvasEl){
26093             return;
26094         }
26095         
26096         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26097         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26098         
26099         this.previewEl.setLeft(pw);
26100         this.previewEl.setTop(ph);
26101         
26102     },
26103     
26104     onMouseDown : function(e)
26105     {   
26106         e.stopEvent();
26107         
26108         this.dragable = true;
26109         this.pinching = false;
26110         
26111         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26112             this.dragable = false;
26113             return;
26114         }
26115         
26116         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26117         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26118         
26119     },
26120     
26121     onMouseMove : function(e)
26122     {   
26123         e.stopEvent();
26124         
26125         if(!this.canvasLoaded){
26126             return;
26127         }
26128         
26129         if (!this.dragable){
26130             return;
26131         }
26132         
26133         var minX = Math.ceil(this.thumbEl.getLeft(true));
26134         var minY = Math.ceil(this.thumbEl.getTop(true));
26135         
26136         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26137         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26138         
26139         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26140         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26141         
26142         x = x - this.mouseX;
26143         y = y - this.mouseY;
26144         
26145         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26146         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26147         
26148         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26149         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26150         
26151         this.previewEl.setLeft(bgX);
26152         this.previewEl.setTop(bgY);
26153         
26154         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26155         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26156     },
26157     
26158     onMouseUp : function(e)
26159     {   
26160         e.stopEvent();
26161         
26162         this.dragable = false;
26163     },
26164     
26165     onMouseWheel : function(e)
26166     {   
26167         e.stopEvent();
26168         
26169         this.startScale = this.scale;
26170         
26171         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26172         
26173         if(!this.zoomable()){
26174             this.scale = this.startScale;
26175             return;
26176         }
26177         
26178         this.draw();
26179         
26180         return;
26181     },
26182     
26183     zoomable : function()
26184     {
26185         var minScale = this.thumbEl.getWidth() / this.minWidth;
26186         
26187         if(this.minWidth < this.minHeight){
26188             minScale = this.thumbEl.getHeight() / this.minHeight;
26189         }
26190         
26191         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26192         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26193         
26194         if(
26195                 this.isDocument &&
26196                 (this.rotate == 0 || this.rotate == 180) && 
26197                 (
26198                     width > this.imageEl.OriginWidth || 
26199                     height > this.imageEl.OriginHeight ||
26200                     (width < this.minWidth && height < this.minHeight)
26201                 )
26202         ){
26203             return false;
26204         }
26205         
26206         if(
26207                 this.isDocument &&
26208                 (this.rotate == 90 || this.rotate == 270) && 
26209                 (
26210                     width > this.imageEl.OriginWidth || 
26211                     height > this.imageEl.OriginHeight ||
26212                     (width < this.minHeight && height < this.minWidth)
26213                 )
26214         ){
26215             return false;
26216         }
26217         
26218         if(
26219                 !this.isDocument &&
26220                 (this.rotate == 0 || this.rotate == 180) && 
26221                 (
26222                     width < this.minWidth || 
26223                     width > this.imageEl.OriginWidth || 
26224                     height < this.minHeight || 
26225                     height > this.imageEl.OriginHeight
26226                 )
26227         ){
26228             return false;
26229         }
26230         
26231         if(
26232                 !this.isDocument &&
26233                 (this.rotate == 90 || this.rotate == 270) && 
26234                 (
26235                     width < this.minHeight || 
26236                     width > this.imageEl.OriginWidth || 
26237                     height < this.minWidth || 
26238                     height > this.imageEl.OriginHeight
26239                 )
26240         ){
26241             return false;
26242         }
26243         
26244         return true;
26245         
26246     },
26247     
26248     onRotateLeft : function(e)
26249     {   
26250         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26251             
26252             var minScale = this.thumbEl.getWidth() / this.minWidth;
26253             
26254             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26255             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26256             
26257             this.startScale = this.scale;
26258             
26259             while (this.getScaleLevel() < minScale){
26260             
26261                 this.scale = this.scale + 1;
26262                 
26263                 if(!this.zoomable()){
26264                     break;
26265                 }
26266                 
26267                 if(
26268                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26269                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26270                 ){
26271                     continue;
26272                 }
26273                 
26274                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26275
26276                 this.draw();
26277                 
26278                 return;
26279             }
26280             
26281             this.scale = this.startScale;
26282             
26283             this.onRotateFail();
26284             
26285             return false;
26286         }
26287         
26288         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26289
26290         if(this.isDocument){
26291             this.setThumbBoxSize();
26292             this.setThumbBoxPosition();
26293             this.setCanvasPosition();
26294         }
26295         
26296         this.draw();
26297         
26298         this.fireEvent('rotate', this, 'left');
26299         
26300     },
26301     
26302     onRotateRight : function(e)
26303     {
26304         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26305             
26306             var minScale = this.thumbEl.getWidth() / this.minWidth;
26307         
26308             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26309             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26310             
26311             this.startScale = this.scale;
26312             
26313             while (this.getScaleLevel() < minScale){
26314             
26315                 this.scale = this.scale + 1;
26316                 
26317                 if(!this.zoomable()){
26318                     break;
26319                 }
26320                 
26321                 if(
26322                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26323                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26324                 ){
26325                     continue;
26326                 }
26327                 
26328                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26329
26330                 this.draw();
26331                 
26332                 return;
26333             }
26334             
26335             this.scale = this.startScale;
26336             
26337             this.onRotateFail();
26338             
26339             return false;
26340         }
26341         
26342         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26343
26344         if(this.isDocument){
26345             this.setThumbBoxSize();
26346             this.setThumbBoxPosition();
26347             this.setCanvasPosition();
26348         }
26349         
26350         this.draw();
26351         
26352         this.fireEvent('rotate', this, 'right');
26353     },
26354     
26355     onRotateFail : function()
26356     {
26357         this.errorEl.show(true);
26358         
26359         var _this = this;
26360         
26361         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26362     },
26363     
26364     draw : function()
26365     {
26366         this.previewEl.dom.innerHTML = '';
26367         
26368         var canvasEl = document.createElement("canvas");
26369         
26370         var contextEl = canvasEl.getContext("2d");
26371         
26372         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26373         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26374         var center = this.imageEl.OriginWidth / 2;
26375         
26376         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26377             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26378             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26379             center = this.imageEl.OriginHeight / 2;
26380         }
26381         
26382         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26383         
26384         contextEl.translate(center, center);
26385         contextEl.rotate(this.rotate * Math.PI / 180);
26386
26387         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26388         
26389         this.canvasEl = document.createElement("canvas");
26390         
26391         this.contextEl = this.canvasEl.getContext("2d");
26392         
26393         switch (this.rotate) {
26394             case 0 :
26395                 
26396                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26397                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26398                 
26399                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26400                 
26401                 break;
26402             case 90 : 
26403                 
26404                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26405                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26406                 
26407                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26408                     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);
26409                     break;
26410                 }
26411                 
26412                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26413                 
26414                 break;
26415             case 180 :
26416                 
26417                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26418                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26419                 
26420                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26421                     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);
26422                     break;
26423                 }
26424                 
26425                 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);
26426                 
26427                 break;
26428             case 270 :
26429                 
26430                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26431                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26432         
26433                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26434                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26435                     break;
26436                 }
26437                 
26438                 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);
26439                 
26440                 break;
26441             default : 
26442                 break;
26443         }
26444         
26445         this.previewEl.appendChild(this.canvasEl);
26446         
26447         this.setCanvasPosition();
26448     },
26449     
26450     crop : function()
26451     {
26452         if(!this.canvasLoaded){
26453             return;
26454         }
26455         
26456         var imageCanvas = document.createElement("canvas");
26457         
26458         var imageContext = imageCanvas.getContext("2d");
26459         
26460         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26461         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26462         
26463         var center = imageCanvas.width / 2;
26464         
26465         imageContext.translate(center, center);
26466         
26467         imageContext.rotate(this.rotate * Math.PI / 180);
26468         
26469         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26470         
26471         var canvas = document.createElement("canvas");
26472         
26473         var context = canvas.getContext("2d");
26474                 
26475         canvas.width = this.minWidth;
26476         canvas.height = this.minHeight;
26477
26478         switch (this.rotate) {
26479             case 0 :
26480                 
26481                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26482                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26483                 
26484                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26485                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26486                 
26487                 var targetWidth = this.minWidth - 2 * x;
26488                 var targetHeight = this.minHeight - 2 * y;
26489                 
26490                 var scale = 1;
26491                 
26492                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26493                     scale = targetWidth / width;
26494                 }
26495                 
26496                 if(x > 0 && y == 0){
26497                     scale = targetHeight / height;
26498                 }
26499                 
26500                 if(x > 0 && y > 0){
26501                     scale = targetWidth / width;
26502                     
26503                     if(width < height){
26504                         scale = targetHeight / height;
26505                     }
26506                 }
26507                 
26508                 context.scale(scale, scale);
26509                 
26510                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26511                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26512
26513                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26514                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26515
26516                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26517                 
26518                 break;
26519             case 90 : 
26520                 
26521                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26522                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26523                 
26524                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26525                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26526                 
26527                 var targetWidth = this.minWidth - 2 * x;
26528                 var targetHeight = this.minHeight - 2 * y;
26529                 
26530                 var scale = 1;
26531                 
26532                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26533                     scale = targetWidth / width;
26534                 }
26535                 
26536                 if(x > 0 && y == 0){
26537                     scale = targetHeight / height;
26538                 }
26539                 
26540                 if(x > 0 && y > 0){
26541                     scale = targetWidth / width;
26542                     
26543                     if(width < height){
26544                         scale = targetHeight / height;
26545                     }
26546                 }
26547                 
26548                 context.scale(scale, scale);
26549                 
26550                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26551                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26552
26553                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26554                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26555                 
26556                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26557                 
26558                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26559                 
26560                 break;
26561             case 180 :
26562                 
26563                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26564                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26565                 
26566                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26567                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26568                 
26569                 var targetWidth = this.minWidth - 2 * x;
26570                 var targetHeight = this.minHeight - 2 * y;
26571                 
26572                 var scale = 1;
26573                 
26574                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26575                     scale = targetWidth / width;
26576                 }
26577                 
26578                 if(x > 0 && y == 0){
26579                     scale = targetHeight / height;
26580                 }
26581                 
26582                 if(x > 0 && y > 0){
26583                     scale = targetWidth / width;
26584                     
26585                     if(width < height){
26586                         scale = targetHeight / height;
26587                     }
26588                 }
26589                 
26590                 context.scale(scale, scale);
26591                 
26592                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26593                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26594
26595                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26596                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26597
26598                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26599                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26600                 
26601                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26602                 
26603                 break;
26604             case 270 :
26605                 
26606                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26607                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26608                 
26609                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26610                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26611                 
26612                 var targetWidth = this.minWidth - 2 * x;
26613                 var targetHeight = this.minHeight - 2 * y;
26614                 
26615                 var scale = 1;
26616                 
26617                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26618                     scale = targetWidth / width;
26619                 }
26620                 
26621                 if(x > 0 && y == 0){
26622                     scale = targetHeight / height;
26623                 }
26624                 
26625                 if(x > 0 && y > 0){
26626                     scale = targetWidth / width;
26627                     
26628                     if(width < height){
26629                         scale = targetHeight / height;
26630                     }
26631                 }
26632                 
26633                 context.scale(scale, scale);
26634                 
26635                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26636                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26637
26638                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26639                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26640                 
26641                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26642                 
26643                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26644                 
26645                 break;
26646             default : 
26647                 break;
26648         }
26649         
26650         this.cropData = canvas.toDataURL(this.cropType);
26651         
26652         if(this.fireEvent('crop', this, this.cropData) !== false){
26653             this.process(this.file, this.cropData);
26654         }
26655         
26656         return;
26657         
26658     },
26659     
26660     setThumbBoxSize : function()
26661     {
26662         var width, height;
26663         
26664         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26665             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26666             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26667             
26668             this.minWidth = width;
26669             this.minHeight = height;
26670             
26671             if(this.rotate == 90 || this.rotate == 270){
26672                 this.minWidth = height;
26673                 this.minHeight = width;
26674             }
26675         }
26676         
26677         height = 300;
26678         width = Math.ceil(this.minWidth * height / this.minHeight);
26679         
26680         if(this.minWidth > this.minHeight){
26681             width = 300;
26682             height = Math.ceil(this.minHeight * width / this.minWidth);
26683         }
26684         
26685         this.thumbEl.setStyle({
26686             width : width + 'px',
26687             height : height + 'px'
26688         });
26689
26690         return;
26691             
26692     },
26693     
26694     setThumbBoxPosition : function()
26695     {
26696         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26697         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26698         
26699         this.thumbEl.setLeft(x);
26700         this.thumbEl.setTop(y);
26701         
26702     },
26703     
26704     baseRotateLevel : function()
26705     {
26706         this.baseRotate = 1;
26707         
26708         if(
26709                 typeof(this.exif) != 'undefined' &&
26710                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26711                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26712         ){
26713             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26714         }
26715         
26716         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26717         
26718     },
26719     
26720     baseScaleLevel : function()
26721     {
26722         var width, height;
26723         
26724         if(this.isDocument){
26725             
26726             if(this.baseRotate == 6 || this.baseRotate == 8){
26727             
26728                 height = this.thumbEl.getHeight();
26729                 this.baseScale = height / this.imageEl.OriginWidth;
26730
26731                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26732                     width = this.thumbEl.getWidth();
26733                     this.baseScale = width / this.imageEl.OriginHeight;
26734                 }
26735
26736                 return;
26737             }
26738
26739             height = this.thumbEl.getHeight();
26740             this.baseScale = height / this.imageEl.OriginHeight;
26741
26742             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26743                 width = this.thumbEl.getWidth();
26744                 this.baseScale = width / this.imageEl.OriginWidth;
26745             }
26746
26747             return;
26748         }
26749         
26750         if(this.baseRotate == 6 || this.baseRotate == 8){
26751             
26752             width = this.thumbEl.getHeight();
26753             this.baseScale = width / this.imageEl.OriginHeight;
26754             
26755             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26756                 height = this.thumbEl.getWidth();
26757                 this.baseScale = height / this.imageEl.OriginHeight;
26758             }
26759             
26760             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26761                 height = this.thumbEl.getWidth();
26762                 this.baseScale = height / this.imageEl.OriginHeight;
26763                 
26764                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26765                     width = this.thumbEl.getHeight();
26766                     this.baseScale = width / this.imageEl.OriginWidth;
26767                 }
26768             }
26769             
26770             return;
26771         }
26772         
26773         width = this.thumbEl.getWidth();
26774         this.baseScale = width / this.imageEl.OriginWidth;
26775         
26776         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26777             height = this.thumbEl.getHeight();
26778             this.baseScale = height / this.imageEl.OriginHeight;
26779         }
26780         
26781         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26782             
26783             height = this.thumbEl.getHeight();
26784             this.baseScale = height / this.imageEl.OriginHeight;
26785             
26786             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26787                 width = this.thumbEl.getWidth();
26788                 this.baseScale = width / this.imageEl.OriginWidth;
26789             }
26790             
26791         }
26792         
26793         return;
26794     },
26795     
26796     getScaleLevel : function()
26797     {
26798         return this.baseScale * Math.pow(1.1, this.scale);
26799     },
26800     
26801     onTouchStart : function(e)
26802     {
26803         if(!this.canvasLoaded){
26804             this.beforeSelectFile(e);
26805             return;
26806         }
26807         
26808         var touches = e.browserEvent.touches;
26809         
26810         if(!touches){
26811             return;
26812         }
26813         
26814         if(touches.length == 1){
26815             this.onMouseDown(e);
26816             return;
26817         }
26818         
26819         if(touches.length != 2){
26820             return;
26821         }
26822         
26823         var coords = [];
26824         
26825         for(var i = 0, finger; finger = touches[i]; i++){
26826             coords.push(finger.pageX, finger.pageY);
26827         }
26828         
26829         var x = Math.pow(coords[0] - coords[2], 2);
26830         var y = Math.pow(coords[1] - coords[3], 2);
26831         
26832         this.startDistance = Math.sqrt(x + y);
26833         
26834         this.startScale = this.scale;
26835         
26836         this.pinching = true;
26837         this.dragable = false;
26838         
26839     },
26840     
26841     onTouchMove : function(e)
26842     {
26843         if(!this.pinching && !this.dragable){
26844             return;
26845         }
26846         
26847         var touches = e.browserEvent.touches;
26848         
26849         if(!touches){
26850             return;
26851         }
26852         
26853         if(this.dragable){
26854             this.onMouseMove(e);
26855             return;
26856         }
26857         
26858         var coords = [];
26859         
26860         for(var i = 0, finger; finger = touches[i]; i++){
26861             coords.push(finger.pageX, finger.pageY);
26862         }
26863         
26864         var x = Math.pow(coords[0] - coords[2], 2);
26865         var y = Math.pow(coords[1] - coords[3], 2);
26866         
26867         this.endDistance = Math.sqrt(x + y);
26868         
26869         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26870         
26871         if(!this.zoomable()){
26872             this.scale = this.startScale;
26873             return;
26874         }
26875         
26876         this.draw();
26877         
26878     },
26879     
26880     onTouchEnd : function(e)
26881     {
26882         this.pinching = false;
26883         this.dragable = false;
26884         
26885     },
26886     
26887     process : function(file, crop)
26888     {
26889         if(this.loadMask){
26890             this.maskEl.mask(this.loadingText);
26891         }
26892         
26893         this.xhr = new XMLHttpRequest();
26894         
26895         file.xhr = this.xhr;
26896
26897         this.xhr.open(this.method, this.url, true);
26898         
26899         var headers = {
26900             "Accept": "application/json",
26901             "Cache-Control": "no-cache",
26902             "X-Requested-With": "XMLHttpRequest"
26903         };
26904         
26905         for (var headerName in headers) {
26906             var headerValue = headers[headerName];
26907             if (headerValue) {
26908                 this.xhr.setRequestHeader(headerName, headerValue);
26909             }
26910         }
26911         
26912         var _this = this;
26913         
26914         this.xhr.onload = function()
26915         {
26916             _this.xhrOnLoad(_this.xhr);
26917         }
26918         
26919         this.xhr.onerror = function()
26920         {
26921             _this.xhrOnError(_this.xhr);
26922         }
26923         
26924         var formData = new FormData();
26925
26926         formData.append('returnHTML', 'NO');
26927         
26928         if(crop){
26929             formData.append('crop', crop);
26930         }
26931         
26932         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26933             formData.append(this.paramName, file, file.name);
26934         }
26935         
26936         if(typeof(file.filename) != 'undefined'){
26937             formData.append('filename', file.filename);
26938         }
26939         
26940         if(typeof(file.mimetype) != 'undefined'){
26941             formData.append('mimetype', file.mimetype);
26942         }
26943         
26944         if(this.fireEvent('arrange', this, formData) != false){
26945             this.xhr.send(formData);
26946         };
26947     },
26948     
26949     xhrOnLoad : function(xhr)
26950     {
26951         if(this.loadMask){
26952             this.maskEl.unmask();
26953         }
26954         
26955         if (xhr.readyState !== 4) {
26956             this.fireEvent('exception', this, xhr);
26957             return;
26958         }
26959
26960         var response = Roo.decode(xhr.responseText);
26961         
26962         if(!response.success){
26963             this.fireEvent('exception', this, xhr);
26964             return;
26965         }
26966         
26967         var response = Roo.decode(xhr.responseText);
26968         
26969         this.fireEvent('upload', this, response);
26970         
26971     },
26972     
26973     xhrOnError : function()
26974     {
26975         if(this.loadMask){
26976             this.maskEl.unmask();
26977         }
26978         
26979         Roo.log('xhr on error');
26980         
26981         var response = Roo.decode(xhr.responseText);
26982           
26983         Roo.log(response);
26984         
26985     },
26986     
26987     prepare : function(file)
26988     {   
26989         if(this.loadMask){
26990             this.maskEl.mask(this.loadingText);
26991         }
26992         
26993         this.file = false;
26994         this.exif = {};
26995         
26996         if(typeof(file) === 'string'){
26997             this.loadCanvas(file);
26998             return;
26999         }
27000         
27001         if(!file || !this.urlAPI){
27002             return;
27003         }
27004         
27005         this.file = file;
27006         this.cropType = file.type;
27007         
27008         var _this = this;
27009         
27010         if(this.fireEvent('prepare', this, this.file) != false){
27011             
27012             var reader = new FileReader();
27013             
27014             reader.onload = function (e) {
27015                 if (e.target.error) {
27016                     Roo.log(e.target.error);
27017                     return;
27018                 }
27019                 
27020                 var buffer = e.target.result,
27021                     dataView = new DataView(buffer),
27022                     offset = 2,
27023                     maxOffset = dataView.byteLength - 4,
27024                     markerBytes,
27025                     markerLength;
27026                 
27027                 if (dataView.getUint16(0) === 0xffd8) {
27028                     while (offset < maxOffset) {
27029                         markerBytes = dataView.getUint16(offset);
27030                         
27031                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27032                             markerLength = dataView.getUint16(offset + 2) + 2;
27033                             if (offset + markerLength > dataView.byteLength) {
27034                                 Roo.log('Invalid meta data: Invalid segment size.');
27035                                 break;
27036                             }
27037                             
27038                             if(markerBytes == 0xffe1){
27039                                 _this.parseExifData(
27040                                     dataView,
27041                                     offset,
27042                                     markerLength
27043                                 );
27044                             }
27045                             
27046                             offset += markerLength;
27047                             
27048                             continue;
27049                         }
27050                         
27051                         break;
27052                     }
27053                     
27054                 }
27055                 
27056                 var url = _this.urlAPI.createObjectURL(_this.file);
27057                 
27058                 _this.loadCanvas(url);
27059                 
27060                 return;
27061             }
27062             
27063             reader.readAsArrayBuffer(this.file);
27064             
27065         }
27066         
27067     },
27068     
27069     parseExifData : function(dataView, offset, length)
27070     {
27071         var tiffOffset = offset + 10,
27072             littleEndian,
27073             dirOffset;
27074     
27075         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27076             // No Exif data, might be XMP data instead
27077             return;
27078         }
27079         
27080         // Check for the ASCII code for "Exif" (0x45786966):
27081         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27082             // No Exif data, might be XMP data instead
27083             return;
27084         }
27085         if (tiffOffset + 8 > dataView.byteLength) {
27086             Roo.log('Invalid Exif data: Invalid segment size.');
27087             return;
27088         }
27089         // Check for the two null bytes:
27090         if (dataView.getUint16(offset + 8) !== 0x0000) {
27091             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27092             return;
27093         }
27094         // Check the byte alignment:
27095         switch (dataView.getUint16(tiffOffset)) {
27096         case 0x4949:
27097             littleEndian = true;
27098             break;
27099         case 0x4D4D:
27100             littleEndian = false;
27101             break;
27102         default:
27103             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27104             return;
27105         }
27106         // Check for the TIFF tag marker (0x002A):
27107         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27108             Roo.log('Invalid Exif data: Missing TIFF marker.');
27109             return;
27110         }
27111         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27112         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27113         
27114         this.parseExifTags(
27115             dataView,
27116             tiffOffset,
27117             tiffOffset + dirOffset,
27118             littleEndian
27119         );
27120     },
27121     
27122     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27123     {
27124         var tagsNumber,
27125             dirEndOffset,
27126             i;
27127         if (dirOffset + 6 > dataView.byteLength) {
27128             Roo.log('Invalid Exif data: Invalid directory offset.');
27129             return;
27130         }
27131         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27132         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27133         if (dirEndOffset + 4 > dataView.byteLength) {
27134             Roo.log('Invalid Exif data: Invalid directory size.');
27135             return;
27136         }
27137         for (i = 0; i < tagsNumber; i += 1) {
27138             this.parseExifTag(
27139                 dataView,
27140                 tiffOffset,
27141                 dirOffset + 2 + 12 * i, // tag offset
27142                 littleEndian
27143             );
27144         }
27145         // Return the offset to the next directory:
27146         return dataView.getUint32(dirEndOffset, littleEndian);
27147     },
27148     
27149     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27150     {
27151         var tag = dataView.getUint16(offset, littleEndian);
27152         
27153         this.exif[tag] = this.getExifValue(
27154             dataView,
27155             tiffOffset,
27156             offset,
27157             dataView.getUint16(offset + 2, littleEndian), // tag type
27158             dataView.getUint32(offset + 4, littleEndian), // tag length
27159             littleEndian
27160         );
27161     },
27162     
27163     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27164     {
27165         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27166             tagSize,
27167             dataOffset,
27168             values,
27169             i,
27170             str,
27171             c;
27172     
27173         if (!tagType) {
27174             Roo.log('Invalid Exif data: Invalid tag type.');
27175             return;
27176         }
27177         
27178         tagSize = tagType.size * length;
27179         // Determine if the value is contained in the dataOffset bytes,
27180         // or if the value at the dataOffset is a pointer to the actual data:
27181         dataOffset = tagSize > 4 ?
27182                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27183         if (dataOffset + tagSize > dataView.byteLength) {
27184             Roo.log('Invalid Exif data: Invalid data offset.');
27185             return;
27186         }
27187         if (length === 1) {
27188             return tagType.getValue(dataView, dataOffset, littleEndian);
27189         }
27190         values = [];
27191         for (i = 0; i < length; i += 1) {
27192             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27193         }
27194         
27195         if (tagType.ascii) {
27196             str = '';
27197             // Concatenate the chars:
27198             for (i = 0; i < values.length; i += 1) {
27199                 c = values[i];
27200                 // Ignore the terminating NULL byte(s):
27201                 if (c === '\u0000') {
27202                     break;
27203                 }
27204                 str += c;
27205             }
27206             return str;
27207         }
27208         return values;
27209     }
27210     
27211 });
27212
27213 Roo.apply(Roo.bootstrap.UploadCropbox, {
27214     tags : {
27215         'Orientation': 0x0112
27216     },
27217     
27218     Orientation: {
27219             1: 0, //'top-left',
27220 //            2: 'top-right',
27221             3: 180, //'bottom-right',
27222 //            4: 'bottom-left',
27223 //            5: 'left-top',
27224             6: 90, //'right-top',
27225 //            7: 'right-bottom',
27226             8: 270 //'left-bottom'
27227     },
27228     
27229     exifTagTypes : {
27230         // byte, 8-bit unsigned int:
27231         1: {
27232             getValue: function (dataView, dataOffset) {
27233                 return dataView.getUint8(dataOffset);
27234             },
27235             size: 1
27236         },
27237         // ascii, 8-bit byte:
27238         2: {
27239             getValue: function (dataView, dataOffset) {
27240                 return String.fromCharCode(dataView.getUint8(dataOffset));
27241             },
27242             size: 1,
27243             ascii: true
27244         },
27245         // short, 16 bit int:
27246         3: {
27247             getValue: function (dataView, dataOffset, littleEndian) {
27248                 return dataView.getUint16(dataOffset, littleEndian);
27249             },
27250             size: 2
27251         },
27252         // long, 32 bit int:
27253         4: {
27254             getValue: function (dataView, dataOffset, littleEndian) {
27255                 return dataView.getUint32(dataOffset, littleEndian);
27256             },
27257             size: 4
27258         },
27259         // rational = two long values, first is numerator, second is denominator:
27260         5: {
27261             getValue: function (dataView, dataOffset, littleEndian) {
27262                 return dataView.getUint32(dataOffset, littleEndian) /
27263                     dataView.getUint32(dataOffset + 4, littleEndian);
27264             },
27265             size: 8
27266         },
27267         // slong, 32 bit signed int:
27268         9: {
27269             getValue: function (dataView, dataOffset, littleEndian) {
27270                 return dataView.getInt32(dataOffset, littleEndian);
27271             },
27272             size: 4
27273         },
27274         // srational, two slongs, first is numerator, second is denominator:
27275         10: {
27276             getValue: function (dataView, dataOffset, littleEndian) {
27277                 return dataView.getInt32(dataOffset, littleEndian) /
27278                     dataView.getInt32(dataOffset + 4, littleEndian);
27279             },
27280             size: 8
27281         }
27282     },
27283     
27284     footer : {
27285         STANDARD : [
27286             {
27287                 tag : 'div',
27288                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27289                 action : 'rotate-left',
27290                 cn : [
27291                     {
27292                         tag : 'button',
27293                         cls : 'btn btn-default',
27294                         html : '<i class="fa fa-undo"></i>'
27295                     }
27296                 ]
27297             },
27298             {
27299                 tag : 'div',
27300                 cls : 'btn-group roo-upload-cropbox-picture',
27301                 action : 'picture',
27302                 cn : [
27303                     {
27304                         tag : 'button',
27305                         cls : 'btn btn-default',
27306                         html : '<i class="fa fa-picture-o"></i>'
27307                     }
27308                 ]
27309             },
27310             {
27311                 tag : 'div',
27312                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27313                 action : 'rotate-right',
27314                 cn : [
27315                     {
27316                         tag : 'button',
27317                         cls : 'btn btn-default',
27318                         html : '<i class="fa fa-repeat"></i>'
27319                     }
27320                 ]
27321             }
27322         ],
27323         DOCUMENT : [
27324             {
27325                 tag : 'div',
27326                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27327                 action : 'rotate-left',
27328                 cn : [
27329                     {
27330                         tag : 'button',
27331                         cls : 'btn btn-default',
27332                         html : '<i class="fa fa-undo"></i>'
27333                     }
27334                 ]
27335             },
27336             {
27337                 tag : 'div',
27338                 cls : 'btn-group roo-upload-cropbox-download',
27339                 action : 'download',
27340                 cn : [
27341                     {
27342                         tag : 'button',
27343                         cls : 'btn btn-default',
27344                         html : '<i class="fa fa-download"></i>'
27345                     }
27346                 ]
27347             },
27348             {
27349                 tag : 'div',
27350                 cls : 'btn-group roo-upload-cropbox-crop',
27351                 action : 'crop',
27352                 cn : [
27353                     {
27354                         tag : 'button',
27355                         cls : 'btn btn-default',
27356                         html : '<i class="fa fa-crop"></i>'
27357                     }
27358                 ]
27359             },
27360             {
27361                 tag : 'div',
27362                 cls : 'btn-group roo-upload-cropbox-trash',
27363                 action : 'trash',
27364                 cn : [
27365                     {
27366                         tag : 'button',
27367                         cls : 'btn btn-default',
27368                         html : '<i class="fa fa-trash"></i>'
27369                     }
27370                 ]
27371             },
27372             {
27373                 tag : 'div',
27374                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27375                 action : 'rotate-right',
27376                 cn : [
27377                     {
27378                         tag : 'button',
27379                         cls : 'btn btn-default',
27380                         html : '<i class="fa fa-repeat"></i>'
27381                     }
27382                 ]
27383             }
27384         ],
27385         ROTATOR : [
27386             {
27387                 tag : 'div',
27388                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27389                 action : 'rotate-left',
27390                 cn : [
27391                     {
27392                         tag : 'button',
27393                         cls : 'btn btn-default',
27394                         html : '<i class="fa fa-undo"></i>'
27395                     }
27396                 ]
27397             },
27398             {
27399                 tag : 'div',
27400                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27401                 action : 'rotate-right',
27402                 cn : [
27403                     {
27404                         tag : 'button',
27405                         cls : 'btn btn-default',
27406                         html : '<i class="fa fa-repeat"></i>'
27407                     }
27408                 ]
27409             }
27410         ]
27411     }
27412 });
27413
27414 /*
27415 * Licence: LGPL
27416 */
27417
27418 /**
27419  * @class Roo.bootstrap.DocumentManager
27420  * @extends Roo.bootstrap.Component
27421  * Bootstrap DocumentManager class
27422  * @cfg {String} paramName default 'imageUpload'
27423  * @cfg {String} toolTipName default 'filename'
27424  * @cfg {String} method default POST
27425  * @cfg {String} url action url
27426  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27427  * @cfg {Boolean} multiple multiple upload default true
27428  * @cfg {Number} thumbSize default 300
27429  * @cfg {String} fieldLabel
27430  * @cfg {Number} labelWidth default 4
27431  * @cfg {String} labelAlign (left|top) default left
27432  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27433  * 
27434  * @constructor
27435  * Create a new DocumentManager
27436  * @param {Object} config The config object
27437  */
27438
27439 Roo.bootstrap.DocumentManager = function(config){
27440     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27441     
27442     this.files = [];
27443     this.delegates = [];
27444     
27445     this.addEvents({
27446         /**
27447          * @event initial
27448          * Fire when initial the DocumentManager
27449          * @param {Roo.bootstrap.DocumentManager} this
27450          */
27451         "initial" : true,
27452         /**
27453          * @event inspect
27454          * inspect selected file
27455          * @param {Roo.bootstrap.DocumentManager} this
27456          * @param {File} file
27457          */
27458         "inspect" : true,
27459         /**
27460          * @event exception
27461          * Fire when xhr load exception
27462          * @param {Roo.bootstrap.DocumentManager} this
27463          * @param {XMLHttpRequest} xhr
27464          */
27465         "exception" : true,
27466         /**
27467          * @event afterupload
27468          * Fire when xhr load exception
27469          * @param {Roo.bootstrap.DocumentManager} this
27470          * @param {XMLHttpRequest} xhr
27471          */
27472         "afterupload" : true,
27473         /**
27474          * @event prepare
27475          * prepare the form data
27476          * @param {Roo.bootstrap.DocumentManager} this
27477          * @param {Object} formData
27478          */
27479         "prepare" : true,
27480         /**
27481          * @event remove
27482          * Fire when remove the file
27483          * @param {Roo.bootstrap.DocumentManager} this
27484          * @param {Object} file
27485          */
27486         "remove" : true,
27487         /**
27488          * @event refresh
27489          * Fire after refresh the file
27490          * @param {Roo.bootstrap.DocumentManager} this
27491          */
27492         "refresh" : true,
27493         /**
27494          * @event click
27495          * Fire after click the image
27496          * @param {Roo.bootstrap.DocumentManager} this
27497          * @param {Object} file
27498          */
27499         "click" : true,
27500         /**
27501          * @event edit
27502          * Fire when upload a image and editable set to true
27503          * @param {Roo.bootstrap.DocumentManager} this
27504          * @param {Object} file
27505          */
27506         "edit" : true,
27507         /**
27508          * @event beforeselectfile
27509          * Fire before select file
27510          * @param {Roo.bootstrap.DocumentManager} this
27511          */
27512         "beforeselectfile" : true,
27513         /**
27514          * @event process
27515          * Fire before process file
27516          * @param {Roo.bootstrap.DocumentManager} this
27517          * @param {Object} file
27518          */
27519         "process" : true
27520         
27521     });
27522 };
27523
27524 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27525     
27526     boxes : 0,
27527     inputName : '',
27528     thumbSize : 300,
27529     multiple : true,
27530     files : false,
27531     method : 'POST',
27532     url : '',
27533     paramName : 'imageUpload',
27534     toolTipName : 'filename',
27535     fieldLabel : '',
27536     labelWidth : 4,
27537     labelAlign : 'left',
27538     editable : true,
27539     delegates : false,
27540     xhr : false, 
27541     
27542     getAutoCreate : function()
27543     {   
27544         var managerWidget = {
27545             tag : 'div',
27546             cls : 'roo-document-manager',
27547             cn : [
27548                 {
27549                     tag : 'input',
27550                     cls : 'roo-document-manager-selector',
27551                     type : 'file'
27552                 },
27553                 {
27554                     tag : 'div',
27555                     cls : 'roo-document-manager-uploader',
27556                     cn : [
27557                         {
27558                             tag : 'div',
27559                             cls : 'roo-document-manager-upload-btn',
27560                             html : '<i class="fa fa-plus"></i>'
27561                         }
27562                     ]
27563                     
27564                 }
27565             ]
27566         };
27567         
27568         var content = [
27569             {
27570                 tag : 'div',
27571                 cls : 'column col-md-12',
27572                 cn : managerWidget
27573             }
27574         ];
27575         
27576         if(this.fieldLabel.length){
27577             
27578             content = [
27579                 {
27580                     tag : 'div',
27581                     cls : 'column col-md-12',
27582                     html : this.fieldLabel
27583                 },
27584                 {
27585                     tag : 'div',
27586                     cls : 'column col-md-12',
27587                     cn : managerWidget
27588                 }
27589             ];
27590
27591             if(this.labelAlign == 'left'){
27592                 content = [
27593                     {
27594                         tag : 'div',
27595                         cls : 'column col-md-' + this.labelWidth,
27596                         html : this.fieldLabel
27597                     },
27598                     {
27599                         tag : 'div',
27600                         cls : 'column col-md-' + (12 - this.labelWidth),
27601                         cn : managerWidget
27602                     }
27603                 ];
27604                 
27605             }
27606         }
27607         
27608         var cfg = {
27609             tag : 'div',
27610             cls : 'row clearfix',
27611             cn : content
27612         };
27613         
27614         return cfg;
27615         
27616     },
27617     
27618     initEvents : function()
27619     {
27620         this.managerEl = this.el.select('.roo-document-manager', true).first();
27621         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27622         
27623         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27624         this.selectorEl.hide();
27625         
27626         if(this.multiple){
27627             this.selectorEl.attr('multiple', 'multiple');
27628         }
27629         
27630         this.selectorEl.on('change', this.onFileSelected, this);
27631         
27632         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27633         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27634         
27635         this.uploader.on('click', this.onUploaderClick, this);
27636         
27637         this.renderProgressDialog();
27638         
27639         var _this = this;
27640         
27641         window.addEventListener("resize", function() { _this.refresh(); } );
27642         
27643         this.fireEvent('initial', this);
27644     },
27645     
27646     renderProgressDialog : function()
27647     {
27648         var _this = this;
27649         
27650         this.progressDialog = new Roo.bootstrap.Modal({
27651             cls : 'roo-document-manager-progress-dialog',
27652             allow_close : false,
27653             title : '',
27654             buttons : [
27655                 {
27656                     name  :'cancel',
27657                     weight : 'danger',
27658                     html : 'Cancel'
27659                 }
27660             ], 
27661             listeners : { 
27662                 btnclick : function() {
27663                     _this.uploadCancel();
27664                     this.hide();
27665                 }
27666             }
27667         });
27668          
27669         this.progressDialog.render(Roo.get(document.body));
27670          
27671         this.progress = new Roo.bootstrap.Progress({
27672             cls : 'roo-document-manager-progress',
27673             active : true,
27674             striped : true
27675         });
27676         
27677         this.progress.render(this.progressDialog.getChildContainer());
27678         
27679         this.progressBar = new Roo.bootstrap.ProgressBar({
27680             cls : 'roo-document-manager-progress-bar',
27681             aria_valuenow : 0,
27682             aria_valuemin : 0,
27683             aria_valuemax : 12,
27684             panel : 'success'
27685         });
27686         
27687         this.progressBar.render(this.progress.getChildContainer());
27688     },
27689     
27690     onUploaderClick : function(e)
27691     {
27692         e.preventDefault();
27693      
27694         if(this.fireEvent('beforeselectfile', this) != false){
27695             this.selectorEl.dom.click();
27696         }
27697         
27698     },
27699     
27700     onFileSelected : function(e)
27701     {
27702         e.preventDefault();
27703         
27704         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27705             return;
27706         }
27707         
27708         Roo.each(this.selectorEl.dom.files, function(file){
27709             if(this.fireEvent('inspect', this, file) != false){
27710                 this.files.push(file);
27711             }
27712         }, this);
27713         
27714         this.queue();
27715         
27716     },
27717     
27718     queue : function()
27719     {
27720         this.selectorEl.dom.value = '';
27721         
27722         if(!this.files.length){
27723             return;
27724         }
27725         
27726         if(this.boxes > 0 && this.files.length > this.boxes){
27727             this.files = this.files.slice(0, this.boxes);
27728         }
27729         
27730         this.uploader.show();
27731         
27732         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27733             this.uploader.hide();
27734         }
27735         
27736         var _this = this;
27737         
27738         var files = [];
27739         
27740         var docs = [];
27741         
27742         Roo.each(this.files, function(file){
27743             
27744             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27745                 var f = this.renderPreview(file);
27746                 files.push(f);
27747                 return;
27748             }
27749             
27750             if(file.type.indexOf('image') != -1){
27751                 this.delegates.push(
27752                     (function(){
27753                         _this.process(file);
27754                     }).createDelegate(this)
27755                 );
27756         
27757                 return;
27758             }
27759             
27760             docs.push(
27761                 (function(){
27762                     _this.process(file);
27763                 }).createDelegate(this)
27764             );
27765             
27766         }, this);
27767         
27768         this.files = files;
27769         
27770         this.delegates = this.delegates.concat(docs);
27771         
27772         if(!this.delegates.length){
27773             this.refresh();
27774             return;
27775         }
27776         
27777         this.progressBar.aria_valuemax = this.delegates.length;
27778         
27779         this.arrange();
27780         
27781         return;
27782     },
27783     
27784     arrange : function()
27785     {
27786         if(!this.delegates.length){
27787             this.progressDialog.hide();
27788             this.refresh();
27789             return;
27790         }
27791         
27792         var delegate = this.delegates.shift();
27793         
27794         this.progressDialog.show();
27795         
27796         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27797         
27798         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27799         
27800         delegate();
27801     },
27802     
27803     refresh : function()
27804     {
27805         this.uploader.show();
27806         
27807         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27808             this.uploader.hide();
27809         }
27810         
27811         Roo.isTouch ? this.closable(false) : this.closable(true);
27812         
27813         this.fireEvent('refresh', this);
27814     },
27815     
27816     onRemove : function(e, el, o)
27817     {
27818         e.preventDefault();
27819         
27820         this.fireEvent('remove', this, o);
27821         
27822     },
27823     
27824     remove : function(o)
27825     {
27826         var files = [];
27827         
27828         Roo.each(this.files, function(file){
27829             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27830                 files.push(file);
27831                 return;
27832             }
27833
27834             o.target.remove();
27835
27836         }, this);
27837         
27838         this.files = files;
27839         
27840         this.refresh();
27841     },
27842     
27843     clear : function()
27844     {
27845         Roo.each(this.files, function(file){
27846             if(!file.target){
27847                 return;
27848             }
27849             
27850             file.target.remove();
27851
27852         }, this);
27853         
27854         this.files = [];
27855         
27856         this.refresh();
27857     },
27858     
27859     onClick : function(e, el, o)
27860     {
27861         e.preventDefault();
27862         
27863         this.fireEvent('click', this, o);
27864         
27865     },
27866     
27867     closable : function(closable)
27868     {
27869         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27870             
27871             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27872             
27873             if(closable){
27874                 el.show();
27875                 return;
27876             }
27877             
27878             el.hide();
27879             
27880         }, this);
27881     },
27882     
27883     xhrOnLoad : function(xhr)
27884     {
27885         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27886             el.remove();
27887         }, this);
27888         
27889         if (xhr.readyState !== 4) {
27890             this.arrange();
27891             this.fireEvent('exception', this, xhr);
27892             return;
27893         }
27894
27895         var response = Roo.decode(xhr.responseText);
27896         
27897         if(!response.success){
27898             this.arrange();
27899             this.fireEvent('exception', this, xhr);
27900             return;
27901         }
27902         
27903         var file = this.renderPreview(response.data);
27904         
27905         this.files.push(file);
27906         
27907         this.arrange();
27908         
27909         this.fireEvent('afterupload', this, xhr);
27910         
27911     },
27912     
27913     xhrOnError : function(xhr)
27914     {
27915         Roo.log('xhr on error');
27916         
27917         var response = Roo.decode(xhr.responseText);
27918           
27919         Roo.log(response);
27920         
27921         this.arrange();
27922     },
27923     
27924     process : function(file)
27925     {
27926         if(this.fireEvent('process', this, file) !== false){
27927             if(this.editable && file.type.indexOf('image') != -1){
27928                 this.fireEvent('edit', this, file);
27929                 return;
27930             }
27931
27932             this.uploadStart(file, false);
27933
27934             return;
27935         }
27936         
27937     },
27938     
27939     uploadStart : function(file, crop)
27940     {
27941         this.xhr = new XMLHttpRequest();
27942         
27943         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27944             this.arrange();
27945             return;
27946         }
27947         
27948         file.xhr = this.xhr;
27949             
27950         this.managerEl.createChild({
27951             tag : 'div',
27952             cls : 'roo-document-manager-loading',
27953             cn : [
27954                 {
27955                     tag : 'div',
27956                     tooltip : file.name,
27957                     cls : 'roo-document-manager-thumb',
27958                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27959                 }
27960             ]
27961
27962         });
27963
27964         this.xhr.open(this.method, this.url, true);
27965         
27966         var headers = {
27967             "Accept": "application/json",
27968             "Cache-Control": "no-cache",
27969             "X-Requested-With": "XMLHttpRequest"
27970         };
27971         
27972         for (var headerName in headers) {
27973             var headerValue = headers[headerName];
27974             if (headerValue) {
27975                 this.xhr.setRequestHeader(headerName, headerValue);
27976             }
27977         }
27978         
27979         var _this = this;
27980         
27981         this.xhr.onload = function()
27982         {
27983             _this.xhrOnLoad(_this.xhr);
27984         }
27985         
27986         this.xhr.onerror = function()
27987         {
27988             _this.xhrOnError(_this.xhr);
27989         }
27990         
27991         var formData = new FormData();
27992
27993         formData.append('returnHTML', 'NO');
27994         
27995         if(crop){
27996             formData.append('crop', crop);
27997         }
27998         
27999         formData.append(this.paramName, file, file.name);
28000         
28001         var options = {
28002             file : file, 
28003             manually : false
28004         };
28005         
28006         if(this.fireEvent('prepare', this, formData, options) != false){
28007             
28008             if(options.manually){
28009                 return;
28010             }
28011             
28012             this.xhr.send(formData);
28013             return;
28014         };
28015         
28016         this.uploadCancel();
28017     },
28018     
28019     uploadCancel : function()
28020     {
28021         if (this.xhr) {
28022             this.xhr.abort();
28023         }
28024         
28025         this.delegates = [];
28026         
28027         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28028             el.remove();
28029         }, this);
28030         
28031         this.arrange();
28032     },
28033     
28034     renderPreview : function(file)
28035     {
28036         if(typeof(file.target) != 'undefined' && file.target){
28037             return file;
28038         }
28039         
28040         var previewEl = this.managerEl.createChild({
28041             tag : 'div',
28042             cls : 'roo-document-manager-preview',
28043             cn : [
28044                 {
28045                     tag : 'div',
28046                     tooltip : file[this.toolTipName],
28047                     cls : 'roo-document-manager-thumb',
28048                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28049                 },
28050                 {
28051                     tag : 'button',
28052                     cls : 'close',
28053                     html : '<i class="fa fa-times-circle"></i>'
28054                 }
28055             ]
28056         });
28057
28058         var close = previewEl.select('button.close', true).first();
28059
28060         close.on('click', this.onRemove, this, file);
28061
28062         file.target = previewEl;
28063
28064         var image = previewEl.select('img', true).first();
28065         
28066         var _this = this;
28067         
28068         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28069         
28070         image.on('click', this.onClick, this, file);
28071         
28072         return file;
28073         
28074     },
28075     
28076     onPreviewLoad : function(file, image)
28077     {
28078         if(typeof(file.target) == 'undefined' || !file.target){
28079             return;
28080         }
28081         
28082         var width = image.dom.naturalWidth || image.dom.width;
28083         var height = image.dom.naturalHeight || image.dom.height;
28084         
28085         if(width > height){
28086             file.target.addClass('wide');
28087             return;
28088         }
28089         
28090         file.target.addClass('tall');
28091         return;
28092         
28093     },
28094     
28095     uploadFromSource : function(file, crop)
28096     {
28097         this.xhr = new XMLHttpRequest();
28098         
28099         this.managerEl.createChild({
28100             tag : 'div',
28101             cls : 'roo-document-manager-loading',
28102             cn : [
28103                 {
28104                     tag : 'div',
28105                     tooltip : file.name,
28106                     cls : 'roo-document-manager-thumb',
28107                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28108                 }
28109             ]
28110
28111         });
28112
28113         this.xhr.open(this.method, this.url, true);
28114         
28115         var headers = {
28116             "Accept": "application/json",
28117             "Cache-Control": "no-cache",
28118             "X-Requested-With": "XMLHttpRequest"
28119         };
28120         
28121         for (var headerName in headers) {
28122             var headerValue = headers[headerName];
28123             if (headerValue) {
28124                 this.xhr.setRequestHeader(headerName, headerValue);
28125             }
28126         }
28127         
28128         var _this = this;
28129         
28130         this.xhr.onload = function()
28131         {
28132             _this.xhrOnLoad(_this.xhr);
28133         }
28134         
28135         this.xhr.onerror = function()
28136         {
28137             _this.xhrOnError(_this.xhr);
28138         }
28139         
28140         var formData = new FormData();
28141
28142         formData.append('returnHTML', 'NO');
28143         
28144         formData.append('crop', crop);
28145         
28146         if(typeof(file.filename) != 'undefined'){
28147             formData.append('filename', file.filename);
28148         }
28149         
28150         if(typeof(file.mimetype) != 'undefined'){
28151             formData.append('mimetype', file.mimetype);
28152         }
28153         
28154         if(this.fireEvent('prepare', this, formData) != false){
28155             this.xhr.send(formData);
28156         };
28157     }
28158 });
28159
28160 /*
28161 * Licence: LGPL
28162 */
28163
28164 /**
28165  * @class Roo.bootstrap.DocumentViewer
28166  * @extends Roo.bootstrap.Component
28167  * Bootstrap DocumentViewer class
28168  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28169  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28170  * 
28171  * @constructor
28172  * Create a new DocumentViewer
28173  * @param {Object} config The config object
28174  */
28175
28176 Roo.bootstrap.DocumentViewer = function(config){
28177     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28178     
28179     this.addEvents({
28180         /**
28181          * @event initial
28182          * Fire after initEvent
28183          * @param {Roo.bootstrap.DocumentViewer} this
28184          */
28185         "initial" : true,
28186         /**
28187          * @event click
28188          * Fire after click
28189          * @param {Roo.bootstrap.DocumentViewer} this
28190          */
28191         "click" : true,
28192         /**
28193          * @event download
28194          * Fire after download button
28195          * @param {Roo.bootstrap.DocumentViewer} this
28196          */
28197         "download" : true,
28198         /**
28199          * @event trash
28200          * Fire after trash button
28201          * @param {Roo.bootstrap.DocumentViewer} this
28202          */
28203         "trash" : true
28204         
28205     });
28206 };
28207
28208 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28209     
28210     showDownload : true,
28211     
28212     showTrash : true,
28213     
28214     getAutoCreate : function()
28215     {
28216         var cfg = {
28217             tag : 'div',
28218             cls : 'roo-document-viewer',
28219             cn : [
28220                 {
28221                     tag : 'div',
28222                     cls : 'roo-document-viewer-body',
28223                     cn : [
28224                         {
28225                             tag : 'div',
28226                             cls : 'roo-document-viewer-thumb',
28227                             cn : [
28228                                 {
28229                                     tag : 'img',
28230                                     cls : 'roo-document-viewer-image'
28231                                 }
28232                             ]
28233                         }
28234                     ]
28235                 },
28236                 {
28237                     tag : 'div',
28238                     cls : 'roo-document-viewer-footer',
28239                     cn : {
28240                         tag : 'div',
28241                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28242                         cn : [
28243                             {
28244                                 tag : 'div',
28245                                 cls : 'btn-group roo-document-viewer-download',
28246                                 cn : [
28247                                     {
28248                                         tag : 'button',
28249                                         cls : 'btn btn-default',
28250                                         html : '<i class="fa fa-download"></i>'
28251                                     }
28252                                 ]
28253                             },
28254                             {
28255                                 tag : 'div',
28256                                 cls : 'btn-group roo-document-viewer-trash',
28257                                 cn : [
28258                                     {
28259                                         tag : 'button',
28260                                         cls : 'btn btn-default',
28261                                         html : '<i class="fa fa-trash"></i>'
28262                                     }
28263                                 ]
28264                             }
28265                         ]
28266                     }
28267                 }
28268             ]
28269         };
28270         
28271         return cfg;
28272     },
28273     
28274     initEvents : function()
28275     {
28276         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28277         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28278         
28279         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28280         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28281         
28282         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28283         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28284         
28285         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28286         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28287         
28288         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28289         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28290         
28291         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28292         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28293         
28294         this.bodyEl.on('click', this.onClick, this);
28295         this.downloadBtn.on('click', this.onDownload, this);
28296         this.trashBtn.on('click', this.onTrash, this);
28297         
28298         this.downloadBtn.hide();
28299         this.trashBtn.hide();
28300         
28301         if(this.showDownload){
28302             this.downloadBtn.show();
28303         }
28304         
28305         if(this.showTrash){
28306             this.trashBtn.show();
28307         }
28308         
28309         if(!this.showDownload && !this.showTrash) {
28310             this.footerEl.hide();
28311         }
28312         
28313     },
28314     
28315     initial : function()
28316     {
28317         this.fireEvent('initial', this);
28318         
28319     },
28320     
28321     onClick : function(e)
28322     {
28323         e.preventDefault();
28324         
28325         this.fireEvent('click', this);
28326     },
28327     
28328     onDownload : function(e)
28329     {
28330         e.preventDefault();
28331         
28332         this.fireEvent('download', this);
28333     },
28334     
28335     onTrash : function(e)
28336     {
28337         e.preventDefault();
28338         
28339         this.fireEvent('trash', this);
28340     }
28341     
28342 });
28343 /*
28344  * - LGPL
28345  *
28346  * nav progress bar
28347  * 
28348  */
28349
28350 /**
28351  * @class Roo.bootstrap.NavProgressBar
28352  * @extends Roo.bootstrap.Component
28353  * Bootstrap NavProgressBar class
28354  * 
28355  * @constructor
28356  * Create a new nav progress bar
28357  * @param {Object} config The config object
28358  */
28359
28360 Roo.bootstrap.NavProgressBar = function(config){
28361     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28362
28363     this.bullets = this.bullets || [];
28364    
28365 //    Roo.bootstrap.NavProgressBar.register(this);
28366      this.addEvents({
28367         /**
28368              * @event changed
28369              * Fires when the active item changes
28370              * @param {Roo.bootstrap.NavProgressBar} this
28371              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28372              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28373          */
28374         'changed': true
28375      });
28376     
28377 };
28378
28379 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28380     
28381     bullets : [],
28382     barItems : [],
28383     
28384     getAutoCreate : function()
28385     {
28386         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28387         
28388         cfg = {
28389             tag : 'div',
28390             cls : 'roo-navigation-bar-group',
28391             cn : [
28392                 {
28393                     tag : 'div',
28394                     cls : 'roo-navigation-top-bar'
28395                 },
28396                 {
28397                     tag : 'div',
28398                     cls : 'roo-navigation-bullets-bar',
28399                     cn : [
28400                         {
28401                             tag : 'ul',
28402                             cls : 'roo-navigation-bar'
28403                         }
28404                     ]
28405                 },
28406                 
28407                 {
28408                     tag : 'div',
28409                     cls : 'roo-navigation-bottom-bar'
28410                 }
28411             ]
28412             
28413         };
28414         
28415         return cfg;
28416         
28417     },
28418     
28419     initEvents: function() 
28420     {
28421         
28422     },
28423     
28424     onRender : function(ct, position) 
28425     {
28426         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28427         
28428         if(this.bullets.length){
28429             Roo.each(this.bullets, function(b){
28430                this.addItem(b);
28431             }, this);
28432         }
28433         
28434         this.format();
28435         
28436     },
28437     
28438     addItem : function(cfg)
28439     {
28440         var item = new Roo.bootstrap.NavProgressItem(cfg);
28441         
28442         item.parentId = this.id;
28443         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28444         
28445         if(cfg.html){
28446             var top = new Roo.bootstrap.Element({
28447                 tag : 'div',
28448                 cls : 'roo-navigation-bar-text'
28449             });
28450             
28451             var bottom = new Roo.bootstrap.Element({
28452                 tag : 'div',
28453                 cls : 'roo-navigation-bar-text'
28454             });
28455             
28456             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28457             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28458             
28459             var topText = new Roo.bootstrap.Element({
28460                 tag : 'span',
28461                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28462             });
28463             
28464             var bottomText = new Roo.bootstrap.Element({
28465                 tag : 'span',
28466                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28467             });
28468             
28469             topText.onRender(top.el, null);
28470             bottomText.onRender(bottom.el, null);
28471             
28472             item.topEl = top;
28473             item.bottomEl = bottom;
28474         }
28475         
28476         this.barItems.push(item);
28477         
28478         return item;
28479     },
28480     
28481     getActive : function()
28482     {
28483         var active = false;
28484         
28485         Roo.each(this.barItems, function(v){
28486             
28487             if (!v.isActive()) {
28488                 return;
28489             }
28490             
28491             active = v;
28492             return false;
28493             
28494         });
28495         
28496         return active;
28497     },
28498     
28499     setActiveItem : function(item)
28500     {
28501         var prev = false;
28502         
28503         Roo.each(this.barItems, function(v){
28504             if (v.rid == item.rid) {
28505                 return ;
28506             }
28507             
28508             if (v.isActive()) {
28509                 v.setActive(false);
28510                 prev = v;
28511             }
28512         });
28513
28514         item.setActive(true);
28515         
28516         this.fireEvent('changed', this, item, prev);
28517     },
28518     
28519     getBarItem: function(rid)
28520     {
28521         var ret = false;
28522         
28523         Roo.each(this.barItems, function(e) {
28524             if (e.rid != rid) {
28525                 return;
28526             }
28527             
28528             ret =  e;
28529             return false;
28530         });
28531         
28532         return ret;
28533     },
28534     
28535     indexOfItem : function(item)
28536     {
28537         var index = false;
28538         
28539         Roo.each(this.barItems, function(v, i){
28540             
28541             if (v.rid != item.rid) {
28542                 return;
28543             }
28544             
28545             index = i;
28546             return false
28547         });
28548         
28549         return index;
28550     },
28551     
28552     setActiveNext : function()
28553     {
28554         var i = this.indexOfItem(this.getActive());
28555         
28556         if (i > this.barItems.length) {
28557             return;
28558         }
28559         
28560         this.setActiveItem(this.barItems[i+1]);
28561     },
28562     
28563     setActivePrev : function()
28564     {
28565         var i = this.indexOfItem(this.getActive());
28566         
28567         if (i  < 1) {
28568             return;
28569         }
28570         
28571         this.setActiveItem(this.barItems[i-1]);
28572     },
28573     
28574     format : function()
28575     {
28576         if(!this.barItems.length){
28577             return;
28578         }
28579      
28580         var width = 100 / this.barItems.length;
28581         
28582         Roo.each(this.barItems, function(i){
28583             i.el.setStyle('width', width + '%');
28584             i.topEl.el.setStyle('width', width + '%');
28585             i.bottomEl.el.setStyle('width', width + '%');
28586         }, this);
28587         
28588     }
28589     
28590 });
28591 /*
28592  * - LGPL
28593  *
28594  * Nav Progress Item
28595  * 
28596  */
28597
28598 /**
28599  * @class Roo.bootstrap.NavProgressItem
28600  * @extends Roo.bootstrap.Component
28601  * Bootstrap NavProgressItem class
28602  * @cfg {String} rid the reference id
28603  * @cfg {Boolean} active (true|false) Is item active default false
28604  * @cfg {Boolean} disabled (true|false) Is item active default false
28605  * @cfg {String} html
28606  * @cfg {String} position (top|bottom) text position default bottom
28607  * @cfg {String} icon show icon instead of number
28608  * 
28609  * @constructor
28610  * Create a new NavProgressItem
28611  * @param {Object} config The config object
28612  */
28613 Roo.bootstrap.NavProgressItem = function(config){
28614     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28615     this.addEvents({
28616         // raw events
28617         /**
28618          * @event click
28619          * The raw click event for the entire grid.
28620          * @param {Roo.bootstrap.NavProgressItem} this
28621          * @param {Roo.EventObject} e
28622          */
28623         "click" : true
28624     });
28625    
28626 };
28627
28628 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28629     
28630     rid : '',
28631     active : false,
28632     disabled : false,
28633     html : '',
28634     position : 'bottom',
28635     icon : false,
28636     
28637     getAutoCreate : function()
28638     {
28639         var iconCls = 'roo-navigation-bar-item-icon';
28640         
28641         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28642         
28643         var cfg = {
28644             tag: 'li',
28645             cls: 'roo-navigation-bar-item',
28646             cn : [
28647                 {
28648                     tag : 'i',
28649                     cls : iconCls
28650                 }
28651             ]
28652         };
28653         
28654         if(this.active){
28655             cfg.cls += ' active';
28656         }
28657         if(this.disabled){
28658             cfg.cls += ' disabled';
28659         }
28660         
28661         return cfg;
28662     },
28663     
28664     disable : function()
28665     {
28666         this.setDisabled(true);
28667     },
28668     
28669     enable : function()
28670     {
28671         this.setDisabled(false);
28672     },
28673     
28674     initEvents: function() 
28675     {
28676         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28677         
28678         this.iconEl.on('click', this.onClick, this);
28679     },
28680     
28681     onClick : function(e)
28682     {
28683         e.preventDefault();
28684         
28685         if(this.disabled){
28686             return;
28687         }
28688         
28689         if(this.fireEvent('click', this, e) === false){
28690             return;
28691         };
28692         
28693         this.parent().setActiveItem(this);
28694     },
28695     
28696     isActive: function () 
28697     {
28698         return this.active;
28699     },
28700     
28701     setActive : function(state)
28702     {
28703         if(this.active == state){
28704             return;
28705         }
28706         
28707         this.active = state;
28708         
28709         if (state) {
28710             this.el.addClass('active');
28711             return;
28712         }
28713         
28714         this.el.removeClass('active');
28715         
28716         return;
28717     },
28718     
28719     setDisabled : function(state)
28720     {
28721         if(this.disabled == state){
28722             return;
28723         }
28724         
28725         this.disabled = state;
28726         
28727         if (state) {
28728             this.el.addClass('disabled');
28729             return;
28730         }
28731         
28732         this.el.removeClass('disabled');
28733     },
28734     
28735     tooltipEl : function()
28736     {
28737         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28738     }
28739 });
28740  
28741
28742  /*
28743  * - LGPL
28744  *
28745  * FieldLabel
28746  * 
28747  */
28748
28749 /**
28750  * @class Roo.bootstrap.FieldLabel
28751  * @extends Roo.bootstrap.Component
28752  * Bootstrap FieldLabel class
28753  * @cfg {String} html contents of the element
28754  * @cfg {String} tag tag of the element default label
28755  * @cfg {String} cls class of the element
28756  * @cfg {String} target label target 
28757  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28758  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28759  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28760  * @cfg {String} iconTooltip default "This field is required"
28761  * 
28762  * @constructor
28763  * Create a new FieldLabel
28764  * @param {Object} config The config object
28765  */
28766
28767 Roo.bootstrap.FieldLabel = function(config){
28768     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28769     
28770     this.addEvents({
28771             /**
28772              * @event invalid
28773              * Fires after the field has been marked as invalid.
28774              * @param {Roo.form.FieldLabel} this
28775              * @param {String} msg The validation message
28776              */
28777             invalid : true,
28778             /**
28779              * @event valid
28780              * Fires after the field has been validated with no errors.
28781              * @param {Roo.form.FieldLabel} this
28782              */
28783             valid : true
28784         });
28785 };
28786
28787 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28788     
28789     tag: 'label',
28790     cls: '',
28791     html: '',
28792     target: '',
28793     allowBlank : true,
28794     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28795     validClass : 'text-success fa fa-lg fa-check',
28796     iconTooltip : 'This field is required',
28797     
28798     getAutoCreate : function(){
28799         
28800         var cfg = {
28801             tag : this.tag,
28802             cls : 'roo-bootstrap-field-label ' + this.cls,
28803             for : this.target,
28804             cn : [
28805                 {
28806                     tag : 'i',
28807                     cls : '',
28808                     tooltip : this.iconTooltip
28809                 },
28810                 {
28811                     tag : 'span',
28812                     html : this.html
28813                 }
28814             ] 
28815         };
28816         
28817         return cfg;
28818     },
28819     
28820     initEvents: function() 
28821     {
28822         Roo.bootstrap.Element.superclass.initEvents.call(this);
28823         
28824         this.iconEl = this.el.select('i', true).first();
28825         
28826         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28827         
28828         Roo.bootstrap.FieldLabel.register(this);
28829     },
28830     
28831     /**
28832      * Mark this field as valid
28833      */
28834     markValid : function()
28835     {
28836         this.iconEl.show();
28837         
28838         this.iconEl.removeClass(this.invalidClass);
28839         
28840         this.iconEl.addClass(this.validClass);
28841         
28842         this.fireEvent('valid', this);
28843     },
28844     
28845     /**
28846      * Mark this field as invalid
28847      * @param {String} msg The validation message
28848      */
28849     markInvalid : function(msg)
28850     {
28851         this.iconEl.show();
28852         
28853         this.iconEl.removeClass(this.validClass);
28854         
28855         this.iconEl.addClass(this.invalidClass);
28856         
28857         this.fireEvent('invalid', this, msg);
28858     }
28859     
28860    
28861 });
28862
28863 Roo.apply(Roo.bootstrap.FieldLabel, {
28864     
28865     groups: {},
28866     
28867      /**
28868     * register a FieldLabel Group
28869     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28870     */
28871     register : function(label)
28872     {
28873         if(this.groups.hasOwnProperty(label.target)){
28874             return;
28875         }
28876      
28877         this.groups[label.target] = label;
28878         
28879     },
28880     /**
28881     * fetch a FieldLabel Group based on the target
28882     * @param {string} target
28883     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28884     */
28885     get: function(target) {
28886         if (typeof(this.groups[target]) == 'undefined') {
28887             return false;
28888         }
28889         
28890         return this.groups[target] ;
28891     }
28892 });
28893
28894  
28895
28896  /*
28897  * - LGPL
28898  *
28899  * page DateSplitField.
28900  * 
28901  */
28902
28903
28904 /**
28905  * @class Roo.bootstrap.DateSplitField
28906  * @extends Roo.bootstrap.Component
28907  * Bootstrap DateSplitField class
28908  * @cfg {string} fieldLabel - the label associated
28909  * @cfg {Number} labelWidth set the width of label (0-12)
28910  * @cfg {String} labelAlign (top|left)
28911  * @cfg {Boolean} dayAllowBlank (true|false) default false
28912  * @cfg {Boolean} monthAllowBlank (true|false) default false
28913  * @cfg {Boolean} yearAllowBlank (true|false) default false
28914  * @cfg {string} dayPlaceholder 
28915  * @cfg {string} monthPlaceholder
28916  * @cfg {string} yearPlaceholder
28917  * @cfg {string} dayFormat default 'd'
28918  * @cfg {string} monthFormat default 'm'
28919  * @cfg {string} yearFormat default 'Y'
28920
28921  *     
28922  * @constructor
28923  * Create a new DateSplitField
28924  * @param {Object} config The config object
28925  */
28926
28927 Roo.bootstrap.DateSplitField = function(config){
28928     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28929     
28930     this.addEvents({
28931         // raw events
28932          /**
28933          * @event years
28934          * getting the data of years
28935          * @param {Roo.bootstrap.DateSplitField} this
28936          * @param {Object} years
28937          */
28938         "years" : true,
28939         /**
28940          * @event days
28941          * getting the data of days
28942          * @param {Roo.bootstrap.DateSplitField} this
28943          * @param {Object} days
28944          */
28945         "days" : true,
28946         /**
28947          * @event invalid
28948          * Fires after the field has been marked as invalid.
28949          * @param {Roo.form.Field} this
28950          * @param {String} msg The validation message
28951          */
28952         invalid : true,
28953        /**
28954          * @event valid
28955          * Fires after the field has been validated with no errors.
28956          * @param {Roo.form.Field} this
28957          */
28958         valid : true
28959     });
28960 };
28961
28962 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28963     
28964     fieldLabel : '',
28965     labelAlign : 'top',
28966     labelWidth : 3,
28967     dayAllowBlank : false,
28968     monthAllowBlank : false,
28969     yearAllowBlank : false,
28970     dayPlaceholder : '',
28971     monthPlaceholder : '',
28972     yearPlaceholder : '',
28973     dayFormat : 'd',
28974     monthFormat : 'm',
28975     yearFormat : 'Y',
28976     isFormField : true,
28977     
28978     getAutoCreate : function()
28979     {
28980         var cfg = {
28981             tag : 'div',
28982             cls : 'row roo-date-split-field-group',
28983             cn : [
28984                 {
28985                     tag : 'input',
28986                     type : 'hidden',
28987                     cls : 'form-hidden-field roo-date-split-field-group-value',
28988                     name : this.name
28989                 }
28990             ]
28991         };
28992         
28993         if(this.fieldLabel){
28994             cfg.cn.push({
28995                 tag : 'div',
28996                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28997                 cn : [
28998                     {
28999                         tag : 'label',
29000                         html : this.fieldLabel
29001                     }
29002                 ]
29003             });
29004         }
29005         
29006         Roo.each(['day', 'month', 'year'], function(t){
29007             cfg.cn.push({
29008                 tag : 'div',
29009                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29010             });
29011         }, this);
29012         
29013         return cfg;
29014     },
29015     
29016     inputEl: function ()
29017     {
29018         return this.el.select('.roo-date-split-field-group-value', true).first();
29019     },
29020     
29021     onRender : function(ct, position) 
29022     {
29023         var _this = this;
29024         
29025         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29026         
29027         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29028         
29029         this.dayField = new Roo.bootstrap.ComboBox({
29030             allowBlank : this.dayAllowBlank,
29031             alwaysQuery : true,
29032             displayField : 'value',
29033             editable : false,
29034             fieldLabel : '',
29035             forceSelection : true,
29036             mode : 'local',
29037             placeholder : this.dayPlaceholder,
29038             selectOnFocus : true,
29039             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29040             triggerAction : 'all',
29041             typeAhead : true,
29042             valueField : 'value',
29043             store : new Roo.data.SimpleStore({
29044                 data : (function() {    
29045                     var days = [];
29046                     _this.fireEvent('days', _this, days);
29047                     return days;
29048                 })(),
29049                 fields : [ 'value' ]
29050             }),
29051             listeners : {
29052                 select : function (_self, record, index)
29053                 {
29054                     _this.setValue(_this.getValue());
29055                 }
29056             }
29057         });
29058
29059         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29060         
29061         this.monthField = new Roo.bootstrap.MonthField({
29062             after : '<i class=\"fa fa-calendar\"></i>',
29063             allowBlank : this.monthAllowBlank,
29064             placeholder : this.monthPlaceholder,
29065             readOnly : true,
29066             listeners : {
29067                 render : function (_self)
29068                 {
29069                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29070                         e.preventDefault();
29071                         _self.focus();
29072                     });
29073                 },
29074                 select : function (_self, oldvalue, newvalue)
29075                 {
29076                     _this.setValue(_this.getValue());
29077                 }
29078             }
29079         });
29080         
29081         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29082         
29083         this.yearField = new Roo.bootstrap.ComboBox({
29084             allowBlank : this.yearAllowBlank,
29085             alwaysQuery : true,
29086             displayField : 'value',
29087             editable : false,
29088             fieldLabel : '',
29089             forceSelection : true,
29090             mode : 'local',
29091             placeholder : this.yearPlaceholder,
29092             selectOnFocus : true,
29093             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29094             triggerAction : 'all',
29095             typeAhead : true,
29096             valueField : 'value',
29097             store : new Roo.data.SimpleStore({
29098                 data : (function() {
29099                     var years = [];
29100                     _this.fireEvent('years', _this, years);
29101                     return years;
29102                 })(),
29103                 fields : [ 'value' ]
29104             }),
29105             listeners : {
29106                 select : function (_self, record, index)
29107                 {
29108                     _this.setValue(_this.getValue());
29109                 }
29110             }
29111         });
29112
29113         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29114     },
29115     
29116     setValue : function(v, format)
29117     {
29118         this.inputEl.dom.value = v;
29119         
29120         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29121         
29122         var d = Date.parseDate(v, f);
29123         
29124         if(!d){
29125             this.validate();
29126             return;
29127         }
29128         
29129         this.setDay(d.format(this.dayFormat));
29130         this.setMonth(d.format(this.monthFormat));
29131         this.setYear(d.format(this.yearFormat));
29132         
29133         this.validate();
29134         
29135         return;
29136     },
29137     
29138     setDay : function(v)
29139     {
29140         this.dayField.setValue(v);
29141         this.inputEl.dom.value = this.getValue();
29142         this.validate();
29143         return;
29144     },
29145     
29146     setMonth : function(v)
29147     {
29148         this.monthField.setValue(v, true);
29149         this.inputEl.dom.value = this.getValue();
29150         this.validate();
29151         return;
29152     },
29153     
29154     setYear : function(v)
29155     {
29156         this.yearField.setValue(v);
29157         this.inputEl.dom.value = this.getValue();
29158         this.validate();
29159         return;
29160     },
29161     
29162     getDay : function()
29163     {
29164         return this.dayField.getValue();
29165     },
29166     
29167     getMonth : function()
29168     {
29169         return this.monthField.getValue();
29170     },
29171     
29172     getYear : function()
29173     {
29174         return this.yearField.getValue();
29175     },
29176     
29177     getValue : function()
29178     {
29179         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29180         
29181         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29182         
29183         return date;
29184     },
29185     
29186     reset : function()
29187     {
29188         this.setDay('');
29189         this.setMonth('');
29190         this.setYear('');
29191         this.inputEl.dom.value = '';
29192         this.validate();
29193         return;
29194     },
29195     
29196     validate : function()
29197     {
29198         var d = this.dayField.validate();
29199         var m = this.monthField.validate();
29200         var y = this.yearField.validate();
29201         
29202         var valid = true;
29203         
29204         if(
29205                 (!this.dayAllowBlank && !d) ||
29206                 (!this.monthAllowBlank && !m) ||
29207                 (!this.yearAllowBlank && !y)
29208         ){
29209             valid = false;
29210         }
29211         
29212         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29213             return valid;
29214         }
29215         
29216         if(valid){
29217             this.markValid();
29218             return valid;
29219         }
29220         
29221         this.markInvalid();
29222         
29223         return valid;
29224     },
29225     
29226     markValid : function()
29227     {
29228         
29229         var label = this.el.select('label', true).first();
29230         var icon = this.el.select('i.fa-star', true).first();
29231
29232         if(label && icon){
29233             icon.remove();
29234         }
29235         
29236         this.fireEvent('valid', this);
29237     },
29238     
29239      /**
29240      * Mark this field as invalid
29241      * @param {String} msg The validation message
29242      */
29243     markInvalid : function(msg)
29244     {
29245         
29246         var label = this.el.select('label', true).first();
29247         var icon = this.el.select('i.fa-star', true).first();
29248
29249         if(label && !icon){
29250             this.el.select('.roo-date-split-field-label', true).createChild({
29251                 tag : 'i',
29252                 cls : 'text-danger fa fa-lg fa-star',
29253                 tooltip : 'This field is required',
29254                 style : 'margin-right:5px;'
29255             }, label, true);
29256         }
29257         
29258         this.fireEvent('invalid', this, msg);
29259     },
29260     
29261     clearInvalid : function()
29262     {
29263         var label = this.el.select('label', true).first();
29264         var icon = this.el.select('i.fa-star', true).first();
29265
29266         if(label && icon){
29267             icon.remove();
29268         }
29269         
29270         this.fireEvent('valid', this);
29271     },
29272     
29273     getName: function()
29274     {
29275         return this.name;
29276     }
29277     
29278 });
29279
29280  /**
29281  *
29282  * This is based on 
29283  * http://masonry.desandro.com
29284  *
29285  * The idea is to render all the bricks based on vertical width...
29286  *
29287  * The original code extends 'outlayer' - we might need to use that....
29288  * 
29289  */
29290
29291
29292 /**
29293  * @class Roo.bootstrap.LayoutMasonry
29294  * @extends Roo.bootstrap.Component
29295  * Bootstrap Layout Masonry class
29296  * 
29297  * @constructor
29298  * Create a new Element
29299  * @param {Object} config The config object
29300  */
29301
29302 Roo.bootstrap.LayoutMasonry = function(config){
29303     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29304     
29305     this.bricks = [];
29306     
29307 };
29308
29309 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29310     
29311     /**
29312      * @cfg {Boolean} isLayoutInstant = no animation?
29313      */   
29314     isLayoutInstant : false, // needed?
29315    
29316     /**
29317      * @cfg {Number} boxWidth  width of the columns
29318      */   
29319     boxWidth : 450,
29320     
29321       /**
29322      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29323      */   
29324     boxHeight : 0,
29325     
29326     /**
29327      * @cfg {Number} padWidth padding below box..
29328      */   
29329     padWidth : 10, 
29330     
29331     /**
29332      * @cfg {Number} gutter gutter width..
29333      */   
29334     gutter : 10,
29335     
29336      /**
29337      * @cfg {Number} maxCols maximum number of columns
29338      */   
29339     
29340     maxCols: 0,
29341     
29342     /**
29343      * @cfg {Boolean} isAutoInitial defalut true
29344      */   
29345     isAutoInitial : true, 
29346     
29347     containerWidth: 0,
29348     
29349     /**
29350      * @cfg {Boolean} isHorizontal defalut false
29351      */   
29352     isHorizontal : false, 
29353
29354     currentSize : null,
29355     
29356     tag: 'div',
29357     
29358     cls: '',
29359     
29360     bricks: null, //CompositeElement
29361     
29362     cols : 1,
29363     
29364     _isLayoutInited : false,
29365     
29366 //    isAlternative : false, // only use for vertical layout...
29367     
29368     /**
29369      * @cfg {Number} alternativePadWidth padding below box..
29370      */   
29371     alternativePadWidth : 50, 
29372     
29373     getAutoCreate : function(){
29374         
29375         var cfg = {
29376             tag: this.tag,
29377             cls: 'blog-masonary-wrapper ' + this.cls,
29378             cn : {
29379                 cls : 'mas-boxes masonary'
29380             }
29381         };
29382         
29383         return cfg;
29384     },
29385     
29386     getChildContainer: function( )
29387     {
29388         if (this.boxesEl) {
29389             return this.boxesEl;
29390         }
29391         
29392         this.boxesEl = this.el.select('.mas-boxes').first();
29393         
29394         return this.boxesEl;
29395     },
29396     
29397     
29398     initEvents : function()
29399     {
29400         var _this = this;
29401         
29402         if(this.isAutoInitial){
29403             Roo.log('hook children rendered');
29404             this.on('childrenrendered', function() {
29405                 Roo.log('children rendered');
29406                 _this.initial();
29407             } ,this);
29408         }
29409     },
29410     
29411     initial : function()
29412     {
29413         this.currentSize = this.el.getBox(true);
29414         
29415         Roo.EventManager.onWindowResize(this.resize, this); 
29416
29417         if(!this.isAutoInitial){
29418             this.layout();
29419             return;
29420         }
29421         
29422         this.layout();
29423         
29424         return;
29425         //this.layout.defer(500,this);
29426         
29427     },
29428     
29429     resize : function()
29430     {
29431         Roo.log('resize');
29432         
29433         var cs = this.el.getBox(true);
29434         
29435         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29436             Roo.log("no change in with or X");
29437             return;
29438         }
29439         
29440         this.currentSize = cs;
29441         
29442         this.layout();
29443         
29444     },
29445     
29446     layout : function()
29447     {   
29448         this._resetLayout();
29449         
29450         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29451         
29452         this.layoutItems( isInstant );
29453       
29454         this._isLayoutInited = true;
29455         
29456     },
29457     
29458     _resetLayout : function()
29459     {
29460         if(this.isHorizontal){
29461             this.horizontalMeasureColumns();
29462             return;
29463         }
29464         
29465         this.verticalMeasureColumns();
29466         
29467     },
29468     
29469     verticalMeasureColumns : function()
29470     {
29471         this.getContainerWidth();
29472         
29473 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29474 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29475 //            return;
29476 //        }
29477         
29478         var boxWidth = this.boxWidth + this.padWidth;
29479         
29480         if(this.containerWidth < this.boxWidth){
29481             boxWidth = this.containerWidth
29482         }
29483         
29484         var containerWidth = this.containerWidth;
29485         
29486         var cols = Math.floor(containerWidth / boxWidth);
29487         
29488         this.cols = Math.max( cols, 1 );
29489         
29490         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29491         
29492         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29493         
29494         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29495         
29496         this.colWidth = boxWidth + avail - this.padWidth;
29497         
29498         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29499         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29500     },
29501     
29502     horizontalMeasureColumns : function()
29503     {
29504         this.getContainerWidth();
29505         
29506         var boxWidth = this.boxWidth;
29507         
29508         if(this.containerWidth < boxWidth){
29509             boxWidth = this.containerWidth;
29510         }
29511         
29512         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29513         
29514         this.el.setHeight(boxWidth);
29515         
29516     },
29517     
29518     getContainerWidth : function()
29519     {
29520         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29521     },
29522     
29523     layoutItems : function( isInstant )
29524     {
29525         var items = Roo.apply([], this.bricks);
29526         
29527         if(this.isHorizontal){
29528             this._horizontalLayoutItems( items , isInstant );
29529             return;
29530         }
29531         
29532 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29533 //            this._verticalAlternativeLayoutItems( items , isInstant );
29534 //            return;
29535 //        }
29536         
29537         this._verticalLayoutItems( items , isInstant );
29538         
29539     },
29540     
29541     _verticalLayoutItems : function ( items , isInstant)
29542     {
29543         if ( !items || !items.length ) {
29544             return;
29545         }
29546         
29547         var standard = [
29548             ['xs', 'xs', 'xs', 'tall'],
29549             ['xs', 'xs', 'tall'],
29550             ['xs', 'xs', 'sm'],
29551             ['xs', 'xs', 'xs'],
29552             ['xs', 'tall'],
29553             ['xs', 'sm'],
29554             ['xs', 'xs'],
29555             ['xs'],
29556             
29557             ['sm', 'xs', 'xs'],
29558             ['sm', 'xs'],
29559             ['sm'],
29560             
29561             ['tall', 'xs', 'xs', 'xs'],
29562             ['tall', 'xs', 'xs'],
29563             ['tall', 'xs'],
29564             ['tall']
29565             
29566         ];
29567         
29568         var queue = [];
29569         
29570         var boxes = [];
29571         
29572         var box = [];
29573         
29574         Roo.each(items, function(item, k){
29575             
29576             switch (item.size) {
29577                 // these layouts take up a full box,
29578                 case 'md' :
29579                 case 'md-left' :
29580                 case 'md-right' :
29581                 case 'wide' :
29582                     
29583                     if(box.length){
29584                         boxes.push(box);
29585                         box = [];
29586                     }
29587                     
29588                     boxes.push([item]);
29589                     
29590                     break;
29591                     
29592                 case 'xs' :
29593                 case 'sm' :
29594                 case 'tall' :
29595                     
29596                     box.push(item);
29597                     
29598                     break;
29599                 default :
29600                     break;
29601                     
29602             }
29603             
29604         }, this);
29605         
29606         if(box.length){
29607             boxes.push(box);
29608             box = [];
29609         }
29610         
29611         var filterPattern = function(box, length)
29612         {
29613             if(!box.length){
29614                 return;
29615             }
29616             
29617             var match = false;
29618             
29619             var pattern = box.slice(0, length);
29620             
29621             var format = [];
29622             
29623             Roo.each(pattern, function(i){
29624                 format.push(i.size);
29625             }, this);
29626             
29627             Roo.each(standard, function(s){
29628                 
29629                 if(String(s) != String(format)){
29630                     return;
29631                 }
29632                 
29633                 match = true;
29634                 return false;
29635                 
29636             }, this);
29637             
29638             if(!match && length == 1){
29639                 return;
29640             }
29641             
29642             if(!match){
29643                 filterPattern(box, length - 1);
29644                 return;
29645             }
29646                 
29647             queue.push(pattern);
29648
29649             box = box.slice(length, box.length);
29650
29651             filterPattern(box, 4);
29652
29653             return;
29654             
29655         }
29656         
29657         Roo.each(boxes, function(box, k){
29658             
29659             if(!box.length){
29660                 return;
29661             }
29662             
29663             if(box.length == 1){
29664                 queue.push(box);
29665                 return;
29666             }
29667             
29668             filterPattern(box, 4);
29669             
29670         }, this);
29671         
29672         this._processVerticalLayoutQueue( queue, isInstant );
29673         
29674     },
29675     
29676 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29677 //    {
29678 //        if ( !items || !items.length ) {
29679 //            return;
29680 //        }
29681 //
29682 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29683 //        
29684 //    },
29685     
29686     _horizontalLayoutItems : function ( items , isInstant)
29687     {
29688         if ( !items || !items.length || items.length < 3) {
29689             return;
29690         }
29691         
29692         items.reverse();
29693         
29694         var eItems = items.slice(0, 3);
29695         
29696         items = items.slice(3, items.length);
29697         
29698         var standard = [
29699             ['xs', 'xs', 'xs', 'wide'],
29700             ['xs', 'xs', 'wide'],
29701             ['xs', 'xs', 'sm'],
29702             ['xs', 'xs', 'xs'],
29703             ['xs', 'wide'],
29704             ['xs', 'sm'],
29705             ['xs', 'xs'],
29706             ['xs'],
29707             
29708             ['sm', 'xs', 'xs'],
29709             ['sm', 'xs'],
29710             ['sm'],
29711             
29712             ['wide', 'xs', 'xs', 'xs'],
29713             ['wide', 'xs', 'xs'],
29714             ['wide', 'xs'],
29715             ['wide'],
29716             
29717             ['wide-thin']
29718         ];
29719         
29720         var queue = [];
29721         
29722         var boxes = [];
29723         
29724         var box = [];
29725         
29726         Roo.each(items, function(item, k){
29727             
29728             switch (item.size) {
29729                 case 'md' :
29730                 case 'md-left' :
29731                 case 'md-right' :
29732                 case 'tall' :
29733                     
29734                     if(box.length){
29735                         boxes.push(box);
29736                         box = [];
29737                     }
29738                     
29739                     boxes.push([item]);
29740                     
29741                     break;
29742                     
29743                 case 'xs' :
29744                 case 'sm' :
29745                 case 'wide' :
29746                 case 'wide-thin' :
29747                     
29748                     box.push(item);
29749                     
29750                     break;
29751                 default :
29752                     break;
29753                     
29754             }
29755             
29756         }, this);
29757         
29758         if(box.length){
29759             boxes.push(box);
29760             box = [];
29761         }
29762         
29763         var filterPattern = function(box, length)
29764         {
29765             if(!box.length){
29766                 return;
29767             }
29768             
29769             var match = false;
29770             
29771             var pattern = box.slice(0, length);
29772             
29773             var format = [];
29774             
29775             Roo.each(pattern, function(i){
29776                 format.push(i.size);
29777             }, this);
29778             
29779             Roo.each(standard, function(s){
29780                 
29781                 if(String(s) != String(format)){
29782                     return;
29783                 }
29784                 
29785                 match = true;
29786                 return false;
29787                 
29788             }, this);
29789             
29790             if(!match && length == 1){
29791                 return;
29792             }
29793             
29794             if(!match){
29795                 filterPattern(box, length - 1);
29796                 return;
29797             }
29798                 
29799             queue.push(pattern);
29800
29801             box = box.slice(length, box.length);
29802
29803             filterPattern(box, 4);
29804
29805             return;
29806             
29807         }
29808         
29809         Roo.each(boxes, function(box, k){
29810             
29811             if(!box.length){
29812                 return;
29813             }
29814             
29815             if(box.length == 1){
29816                 queue.push(box);
29817                 return;
29818             }
29819             
29820             filterPattern(box, 4);
29821             
29822         }, this);
29823         
29824         
29825         var prune = [];
29826         
29827         var pos = this.el.getBox(true);
29828         
29829         var minX = pos.x;
29830         
29831         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29832         
29833         var hit_end = false;
29834         
29835         Roo.each(queue, function(box){
29836             
29837             if(hit_end){
29838                 
29839                 Roo.each(box, function(b){
29840                 
29841                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29842                     b.el.hide();
29843
29844                 }, this);
29845
29846                 return;
29847             }
29848             
29849             var mx = 0;
29850             
29851             Roo.each(box, function(b){
29852                 
29853                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29854                 b.el.show();
29855
29856                 mx = Math.max(mx, b.x);
29857                 
29858             }, this);
29859             
29860             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29861             
29862             if(maxX < minX){
29863                 
29864                 Roo.each(box, function(b){
29865                 
29866                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29867                     b.el.hide();
29868                     
29869                 }, this);
29870                 
29871                 hit_end = true;
29872                 
29873                 return;
29874             }
29875             
29876             prune.push(box);
29877             
29878         }, this);
29879         
29880         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29881     },
29882     
29883     /** Sets position of item in DOM
29884     * @param {Element} item
29885     * @param {Number} x - horizontal position
29886     * @param {Number} y - vertical position
29887     * @param {Boolean} isInstant - disables transitions
29888     */
29889     _processVerticalLayoutQueue : function( queue, isInstant )
29890     {
29891         var pos = this.el.getBox(true);
29892         var x = pos.x;
29893         var y = pos.y;
29894         var maxY = [];
29895         
29896         for (var i = 0; i < this.cols; i++){
29897             maxY[i] = pos.y;
29898         }
29899         
29900         Roo.each(queue, function(box, k){
29901             
29902             var col = k % this.cols;
29903             
29904             Roo.each(box, function(b,kk){
29905                 
29906                 b.el.position('absolute');
29907                 
29908                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29909                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29910                 
29911                 if(b.size == 'md-left' || b.size == 'md-right'){
29912                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29913                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29914                 }
29915                 
29916                 b.el.setWidth(width);
29917                 b.el.setHeight(height);
29918                 // iframe?
29919                 b.el.select('iframe',true).setSize(width,height);
29920                 
29921             }, this);
29922             
29923             for (var i = 0; i < this.cols; i++){
29924                 
29925                 if(maxY[i] < maxY[col]){
29926                     col = i;
29927                     continue;
29928                 }
29929                 
29930                 col = Math.min(col, i);
29931                 
29932             }
29933             
29934             x = pos.x + col * (this.colWidth + this.padWidth);
29935             
29936             y = maxY[col];
29937             
29938             var positions = [];
29939             
29940             switch (box.length){
29941                 case 1 :
29942                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29943                     break;
29944                 case 2 :
29945                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29946                     break;
29947                 case 3 :
29948                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29949                     break;
29950                 case 4 :
29951                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29952                     break;
29953                 default :
29954                     break;
29955             }
29956             
29957             Roo.each(box, function(b,kk){
29958                 
29959                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29960                 
29961                 var sz = b.el.getSize();
29962                 
29963                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29964                 
29965             }, this);
29966             
29967         }, this);
29968         
29969         var mY = 0;
29970         
29971         for (var i = 0; i < this.cols; i++){
29972             mY = Math.max(mY, maxY[i]);
29973         }
29974         
29975         this.el.setHeight(mY - pos.y);
29976         
29977     },
29978     
29979 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29980 //    {
29981 //        var pos = this.el.getBox(true);
29982 //        var x = pos.x;
29983 //        var y = pos.y;
29984 //        var maxX = pos.right;
29985 //        
29986 //        var maxHeight = 0;
29987 //        
29988 //        Roo.each(items, function(item, k){
29989 //            
29990 //            var c = k % 2;
29991 //            
29992 //            item.el.position('absolute');
29993 //                
29994 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29995 //
29996 //            item.el.setWidth(width);
29997 //
29998 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29999 //
30000 //            item.el.setHeight(height);
30001 //            
30002 //            if(c == 0){
30003 //                item.el.setXY([x, y], isInstant ? false : true);
30004 //            } else {
30005 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30006 //            }
30007 //            
30008 //            y = y + height + this.alternativePadWidth;
30009 //            
30010 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30011 //            
30012 //        }, this);
30013 //        
30014 //        this.el.setHeight(maxHeight);
30015 //        
30016 //    },
30017     
30018     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30019     {
30020         var pos = this.el.getBox(true);
30021         
30022         var minX = pos.x;
30023         var minY = pos.y;
30024         
30025         var maxX = pos.right;
30026         
30027         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30028         
30029         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30030         
30031         Roo.each(queue, function(box, k){
30032             
30033             Roo.each(box, function(b, kk){
30034                 
30035                 b.el.position('absolute');
30036                 
30037                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30038                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30039                 
30040                 if(b.size == 'md-left' || b.size == 'md-right'){
30041                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30042                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30043                 }
30044                 
30045                 b.el.setWidth(width);
30046                 b.el.setHeight(height);
30047                 
30048             }, this);
30049             
30050             if(!box.length){
30051                 return;
30052             }
30053             
30054             var positions = [];
30055             
30056             switch (box.length){
30057                 case 1 :
30058                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30059                     break;
30060                 case 2 :
30061                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30062                     break;
30063                 case 3 :
30064                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30065                     break;
30066                 case 4 :
30067                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30068                     break;
30069                 default :
30070                     break;
30071             }
30072             
30073             Roo.each(box, function(b,kk){
30074                 
30075                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30076                 
30077                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30078                 
30079             }, this);
30080             
30081         }, this);
30082         
30083     },
30084     
30085     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30086     {
30087         Roo.each(eItems, function(b,k){
30088             
30089             b.size = (k == 0) ? 'sm' : 'xs';
30090             b.x = (k == 0) ? 2 : 1;
30091             b.y = (k == 0) ? 2 : 1;
30092             
30093             b.el.position('absolute');
30094             
30095             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30096                 
30097             b.el.setWidth(width);
30098             
30099             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30100             
30101             b.el.setHeight(height);
30102             
30103         }, this);
30104
30105         var positions = [];
30106         
30107         positions.push({
30108             x : maxX - this.unitWidth * 2 - this.gutter,
30109             y : minY
30110         });
30111         
30112         positions.push({
30113             x : maxX - this.unitWidth,
30114             y : minY + (this.unitWidth + this.gutter) * 2
30115         });
30116         
30117         positions.push({
30118             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30119             y : minY
30120         });
30121         
30122         Roo.each(eItems, function(b,k){
30123             
30124             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30125
30126         }, this);
30127         
30128     },
30129     
30130     getVerticalOneBoxColPositions : function(x, y, box)
30131     {
30132         var pos = [];
30133         
30134         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30135         
30136         if(box[0].size == 'md-left'){
30137             rand = 0;
30138         }
30139         
30140         if(box[0].size == 'md-right'){
30141             rand = 1;
30142         }
30143         
30144         pos.push({
30145             x : x + (this.unitWidth + this.gutter) * rand,
30146             y : y
30147         });
30148         
30149         return pos;
30150     },
30151     
30152     getVerticalTwoBoxColPositions : function(x, y, box)
30153     {
30154         var pos = [];
30155         
30156         if(box[0].size == 'xs'){
30157             
30158             pos.push({
30159                 x : x,
30160                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30161             });
30162
30163             pos.push({
30164                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30165                 y : y
30166             });
30167             
30168             return pos;
30169             
30170         }
30171         
30172         pos.push({
30173             x : x,
30174             y : y
30175         });
30176
30177         pos.push({
30178             x : x + (this.unitWidth + this.gutter) * 2,
30179             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30180         });
30181         
30182         return pos;
30183         
30184     },
30185     
30186     getVerticalThreeBoxColPositions : function(x, y, box)
30187     {
30188         var pos = [];
30189         
30190         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30191             
30192             pos.push({
30193                 x : x,
30194                 y : y
30195             });
30196
30197             pos.push({
30198                 x : x + (this.unitWidth + this.gutter) * 1,
30199                 y : y
30200             });
30201             
30202             pos.push({
30203                 x : x + (this.unitWidth + this.gutter) * 2,
30204                 y : y
30205             });
30206             
30207             return pos;
30208             
30209         }
30210         
30211         if(box[0].size == 'xs' && box[1].size == 'xs'){
30212             
30213             pos.push({
30214                 x : x,
30215                 y : y
30216             });
30217
30218             pos.push({
30219                 x : x,
30220                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30221             });
30222             
30223             pos.push({
30224                 x : x + (this.unitWidth + this.gutter) * 1,
30225                 y : y
30226             });
30227             
30228             return pos;
30229             
30230         }
30231         
30232         pos.push({
30233             x : x,
30234             y : y
30235         });
30236
30237         pos.push({
30238             x : x + (this.unitWidth + this.gutter) * 2,
30239             y : y
30240         });
30241
30242         pos.push({
30243             x : x + (this.unitWidth + this.gutter) * 2,
30244             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30245         });
30246             
30247         return pos;
30248         
30249     },
30250     
30251     getVerticalFourBoxColPositions : function(x, y, box)
30252     {
30253         var pos = [];
30254         
30255         if(box[0].size == 'xs'){
30256             
30257             pos.push({
30258                 x : x,
30259                 y : y
30260             });
30261
30262             pos.push({
30263                 x : x,
30264                 y : y + (this.unitHeight + this.gutter) * 1
30265             });
30266             
30267             pos.push({
30268                 x : x,
30269                 y : y + (this.unitHeight + this.gutter) * 2
30270             });
30271             
30272             pos.push({
30273                 x : x + (this.unitWidth + this.gutter) * 1,
30274                 y : y
30275             });
30276             
30277             return pos;
30278             
30279         }
30280         
30281         pos.push({
30282             x : x,
30283             y : y
30284         });
30285
30286         pos.push({
30287             x : x + (this.unitWidth + this.gutter) * 2,
30288             y : y
30289         });
30290
30291         pos.push({
30292             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30293             y : y + (this.unitHeight + this.gutter) * 1
30294         });
30295
30296         pos.push({
30297             x : x + (this.unitWidth + this.gutter) * 2,
30298             y : y + (this.unitWidth + this.gutter) * 2
30299         });
30300
30301         return pos;
30302         
30303     },
30304     
30305     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30306     {
30307         var pos = [];
30308         
30309         if(box[0].size == 'md-left'){
30310             pos.push({
30311                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30312                 y : minY
30313             });
30314             
30315             return pos;
30316         }
30317         
30318         if(box[0].size == 'md-right'){
30319             pos.push({
30320                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30321                 y : minY + (this.unitWidth + this.gutter) * 1
30322             });
30323             
30324             return pos;
30325         }
30326         
30327         var rand = Math.floor(Math.random() * (4 - box[0].y));
30328         
30329         pos.push({
30330             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30331             y : minY + (this.unitWidth + this.gutter) * rand
30332         });
30333         
30334         return pos;
30335         
30336     },
30337     
30338     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30339     {
30340         var pos = [];
30341         
30342         if(box[0].size == 'xs'){
30343             
30344             pos.push({
30345                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30346                 y : minY
30347             });
30348
30349             pos.push({
30350                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30351                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30352             });
30353             
30354             return pos;
30355             
30356         }
30357         
30358         pos.push({
30359             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30360             y : minY
30361         });
30362
30363         pos.push({
30364             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30365             y : minY + (this.unitWidth + this.gutter) * 2
30366         });
30367         
30368         return pos;
30369         
30370     },
30371     
30372     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30373     {
30374         var pos = [];
30375         
30376         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30377             
30378             pos.push({
30379                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30380                 y : minY
30381             });
30382
30383             pos.push({
30384                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30385                 y : minY + (this.unitWidth + this.gutter) * 1
30386             });
30387             
30388             pos.push({
30389                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30390                 y : minY + (this.unitWidth + this.gutter) * 2
30391             });
30392             
30393             return pos;
30394             
30395         }
30396         
30397         if(box[0].size == 'xs' && box[1].size == 'xs'){
30398             
30399             pos.push({
30400                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30401                 y : minY
30402             });
30403
30404             pos.push({
30405                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30406                 y : minY
30407             });
30408             
30409             pos.push({
30410                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30411                 y : minY + (this.unitWidth + this.gutter) * 1
30412             });
30413             
30414             return pos;
30415             
30416         }
30417         
30418         pos.push({
30419             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30420             y : minY
30421         });
30422
30423         pos.push({
30424             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30425             y : minY + (this.unitWidth + this.gutter) * 2
30426         });
30427
30428         pos.push({
30429             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30430             y : minY + (this.unitWidth + this.gutter) * 2
30431         });
30432             
30433         return pos;
30434         
30435     },
30436     
30437     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30438     {
30439         var pos = [];
30440         
30441         if(box[0].size == 'xs'){
30442             
30443             pos.push({
30444                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30445                 y : minY
30446             });
30447
30448             pos.push({
30449                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30450                 y : minY
30451             });
30452             
30453             pos.push({
30454                 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),
30455                 y : minY
30456             });
30457             
30458             pos.push({
30459                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30460                 y : minY + (this.unitWidth + this.gutter) * 1
30461             });
30462             
30463             return pos;
30464             
30465         }
30466         
30467         pos.push({
30468             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30469             y : minY
30470         });
30471         
30472         pos.push({
30473             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30474             y : minY + (this.unitWidth + this.gutter) * 2
30475         });
30476         
30477         pos.push({
30478             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30479             y : minY + (this.unitWidth + this.gutter) * 2
30480         });
30481         
30482         pos.push({
30483             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),
30484             y : minY + (this.unitWidth + this.gutter) * 2
30485         });
30486
30487         return pos;
30488         
30489     }
30490     
30491 });
30492
30493  
30494
30495  /**
30496  *
30497  * This is based on 
30498  * http://masonry.desandro.com
30499  *
30500  * The idea is to render all the bricks based on vertical width...
30501  *
30502  * The original code extends 'outlayer' - we might need to use that....
30503  * 
30504  */
30505
30506
30507 /**
30508  * @class Roo.bootstrap.LayoutMasonryAuto
30509  * @extends Roo.bootstrap.Component
30510  * Bootstrap Layout Masonry class
30511  * 
30512  * @constructor
30513  * Create a new Element
30514  * @param {Object} config The config object
30515  */
30516
30517 Roo.bootstrap.LayoutMasonryAuto = function(config){
30518     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30519 };
30520
30521 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30522     
30523       /**
30524      * @cfg {Boolean} isFitWidth  - resize the width..
30525      */   
30526     isFitWidth : false,  // options..
30527     /**
30528      * @cfg {Boolean} isOriginLeft = left align?
30529      */   
30530     isOriginLeft : true,
30531     /**
30532      * @cfg {Boolean} isOriginTop = top align?
30533      */   
30534     isOriginTop : false,
30535     /**
30536      * @cfg {Boolean} isLayoutInstant = no animation?
30537      */   
30538     isLayoutInstant : false, // needed?
30539     /**
30540      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30541      */   
30542     isResizingContainer : true,
30543     /**
30544      * @cfg {Number} columnWidth  width of the columns 
30545      */   
30546     
30547     columnWidth : 0,
30548     
30549     /**
30550      * @cfg {Number} maxCols maximum number of columns
30551      */   
30552     
30553     maxCols: 0,
30554     /**
30555      * @cfg {Number} padHeight padding below box..
30556      */   
30557     
30558     padHeight : 10, 
30559     
30560     /**
30561      * @cfg {Boolean} isAutoInitial defalut true
30562      */   
30563     
30564     isAutoInitial : true, 
30565     
30566     // private?
30567     gutter : 0,
30568     
30569     containerWidth: 0,
30570     initialColumnWidth : 0,
30571     currentSize : null,
30572     
30573     colYs : null, // array.
30574     maxY : 0,
30575     padWidth: 10,
30576     
30577     
30578     tag: 'div',
30579     cls: '',
30580     bricks: null, //CompositeElement
30581     cols : 0, // array?
30582     // element : null, // wrapped now this.el
30583     _isLayoutInited : null, 
30584     
30585     
30586     getAutoCreate : function(){
30587         
30588         var cfg = {
30589             tag: this.tag,
30590             cls: 'blog-masonary-wrapper ' + this.cls,
30591             cn : {
30592                 cls : 'mas-boxes masonary'
30593             }
30594         };
30595         
30596         return cfg;
30597     },
30598     
30599     getChildContainer: function( )
30600     {
30601         if (this.boxesEl) {
30602             return this.boxesEl;
30603         }
30604         
30605         this.boxesEl = this.el.select('.mas-boxes').first();
30606         
30607         return this.boxesEl;
30608     },
30609     
30610     
30611     initEvents : function()
30612     {
30613         var _this = this;
30614         
30615         if(this.isAutoInitial){
30616             Roo.log('hook children rendered');
30617             this.on('childrenrendered', function() {
30618                 Roo.log('children rendered');
30619                 _this.initial();
30620             } ,this);
30621         }
30622         
30623     },
30624     
30625     initial : function()
30626     {
30627         this.reloadItems();
30628
30629         this.currentSize = this.el.getBox(true);
30630
30631         /// was window resize... - let's see if this works..
30632         Roo.EventManager.onWindowResize(this.resize, this); 
30633
30634         if(!this.isAutoInitial){
30635             this.layout();
30636             return;
30637         }
30638         
30639         this.layout.defer(500,this);
30640     },
30641     
30642     reloadItems: function()
30643     {
30644         this.bricks = this.el.select('.masonry-brick', true);
30645         
30646         this.bricks.each(function(b) {
30647             //Roo.log(b.getSize());
30648             if (!b.attr('originalwidth')) {
30649                 b.attr('originalwidth',  b.getSize().width);
30650             }
30651             
30652         });
30653         
30654         Roo.log(this.bricks.elements.length);
30655     },
30656     
30657     resize : function()
30658     {
30659         Roo.log('resize');
30660         var cs = this.el.getBox(true);
30661         
30662         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30663             Roo.log("no change in with or X");
30664             return;
30665         }
30666         this.currentSize = cs;
30667         this.layout();
30668     },
30669     
30670     layout : function()
30671     {
30672          Roo.log('layout');
30673         this._resetLayout();
30674         //this._manageStamps();
30675       
30676         // don't animate first layout
30677         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30678         this.layoutItems( isInstant );
30679       
30680         // flag for initalized
30681         this._isLayoutInited = true;
30682     },
30683     
30684     layoutItems : function( isInstant )
30685     {
30686         //var items = this._getItemsForLayout( this.items );
30687         // original code supports filtering layout items.. we just ignore it..
30688         
30689         this._layoutItems( this.bricks , isInstant );
30690       
30691         this._postLayout();
30692     },
30693     _layoutItems : function ( items , isInstant)
30694     {
30695        //this.fireEvent( 'layout', this, items );
30696     
30697
30698         if ( !items || !items.elements.length ) {
30699           // no items, emit event with empty array
30700             return;
30701         }
30702
30703         var queue = [];
30704         items.each(function(item) {
30705             Roo.log("layout item");
30706             Roo.log(item);
30707             // get x/y object from method
30708             var position = this._getItemLayoutPosition( item );
30709             // enqueue
30710             position.item = item;
30711             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30712             queue.push( position );
30713         }, this);
30714       
30715         this._processLayoutQueue( queue );
30716     },
30717     /** Sets position of item in DOM
30718     * @param {Element} item
30719     * @param {Number} x - horizontal position
30720     * @param {Number} y - vertical position
30721     * @param {Boolean} isInstant - disables transitions
30722     */
30723     _processLayoutQueue : function( queue )
30724     {
30725         for ( var i=0, len = queue.length; i < len; i++ ) {
30726             var obj = queue[i];
30727             obj.item.position('absolute');
30728             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30729         }
30730     },
30731       
30732     
30733     /**
30734     * Any logic you want to do after each layout,
30735     * i.e. size the container
30736     */
30737     _postLayout : function()
30738     {
30739         this.resizeContainer();
30740     },
30741     
30742     resizeContainer : function()
30743     {
30744         if ( !this.isResizingContainer ) {
30745             return;
30746         }
30747         var size = this._getContainerSize();
30748         if ( size ) {
30749             this.el.setSize(size.width,size.height);
30750             this.boxesEl.setSize(size.width,size.height);
30751         }
30752     },
30753     
30754     
30755     
30756     _resetLayout : function()
30757     {
30758         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30759         this.colWidth = this.el.getWidth();
30760         //this.gutter = this.el.getWidth(); 
30761         
30762         this.measureColumns();
30763
30764         // reset column Y
30765         var i = this.cols;
30766         this.colYs = [];
30767         while (i--) {
30768             this.colYs.push( 0 );
30769         }
30770     
30771         this.maxY = 0;
30772     },
30773
30774     measureColumns : function()
30775     {
30776         this.getContainerWidth();
30777       // if columnWidth is 0, default to outerWidth of first item
30778         if ( !this.columnWidth ) {
30779             var firstItem = this.bricks.first();
30780             Roo.log(firstItem);
30781             this.columnWidth  = this.containerWidth;
30782             if (firstItem && firstItem.attr('originalwidth') ) {
30783                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30784             }
30785             // columnWidth fall back to item of first element
30786             Roo.log("set column width?");
30787                         this.initialColumnWidth = this.columnWidth  ;
30788
30789             // if first elem has no width, default to size of container
30790             
30791         }
30792         
30793         
30794         if (this.initialColumnWidth) {
30795             this.columnWidth = this.initialColumnWidth;
30796         }
30797         
30798         
30799             
30800         // column width is fixed at the top - however if container width get's smaller we should
30801         // reduce it...
30802         
30803         // this bit calcs how man columns..
30804             
30805         var columnWidth = this.columnWidth += this.gutter;
30806       
30807         // calculate columns
30808         var containerWidth = this.containerWidth + this.gutter;
30809         
30810         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30811         // fix rounding errors, typically with gutters
30812         var excess = columnWidth - containerWidth % columnWidth;
30813         
30814         
30815         // if overshoot is less than a pixel, round up, otherwise floor it
30816         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30817         cols = Math[ mathMethod ]( cols );
30818         this.cols = Math.max( cols, 1 );
30819         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30820         
30821          // padding positioning..
30822         var totalColWidth = this.cols * this.columnWidth;
30823         var padavail = this.containerWidth - totalColWidth;
30824         // so for 2 columns - we need 3 'pads'
30825         
30826         var padNeeded = (1+this.cols) * this.padWidth;
30827         
30828         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30829         
30830         this.columnWidth += padExtra
30831         //this.padWidth = Math.floor(padavail /  ( this.cols));
30832         
30833         // adjust colum width so that padding is fixed??
30834         
30835         // we have 3 columns ... total = width * 3
30836         // we have X left over... that should be used by 
30837         
30838         //if (this.expandC) {
30839             
30840         //}
30841         
30842         
30843         
30844     },
30845     
30846     getContainerWidth : function()
30847     {
30848        /* // container is parent if fit width
30849         var container = this.isFitWidth ? this.element.parentNode : this.element;
30850         // check that this.size and size are there
30851         // IE8 triggers resize on body size change, so they might not be
30852         
30853         var size = getSize( container );  //FIXME
30854         this.containerWidth = size && size.innerWidth; //FIXME
30855         */
30856          
30857         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30858         
30859     },
30860     
30861     _getItemLayoutPosition : function( item )  // what is item?
30862     {
30863         // we resize the item to our columnWidth..
30864       
30865         item.setWidth(this.columnWidth);
30866         item.autoBoxAdjust  = false;
30867         
30868         var sz = item.getSize();
30869  
30870         // how many columns does this brick span
30871         var remainder = this.containerWidth % this.columnWidth;
30872         
30873         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30874         // round if off by 1 pixel, otherwise use ceil
30875         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30876         colSpan = Math.min( colSpan, this.cols );
30877         
30878         // normally this should be '1' as we dont' currently allow multi width columns..
30879         
30880         var colGroup = this._getColGroup( colSpan );
30881         // get the minimum Y value from the columns
30882         var minimumY = Math.min.apply( Math, colGroup );
30883         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30884         
30885         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30886          
30887         // position the brick
30888         var position = {
30889             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30890             y: this.currentSize.y + minimumY + this.padHeight
30891         };
30892         
30893         Roo.log(position);
30894         // apply setHeight to necessary columns
30895         var setHeight = minimumY + sz.height + this.padHeight;
30896         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30897         
30898         var setSpan = this.cols + 1 - colGroup.length;
30899         for ( var i = 0; i < setSpan; i++ ) {
30900           this.colYs[ shortColIndex + i ] = setHeight ;
30901         }
30902       
30903         return position;
30904     },
30905     
30906     /**
30907      * @param {Number} colSpan - number of columns the element spans
30908      * @returns {Array} colGroup
30909      */
30910     _getColGroup : function( colSpan )
30911     {
30912         if ( colSpan < 2 ) {
30913           // if brick spans only one column, use all the column Ys
30914           return this.colYs;
30915         }
30916       
30917         var colGroup = [];
30918         // how many different places could this brick fit horizontally
30919         var groupCount = this.cols + 1 - colSpan;
30920         // for each group potential horizontal position
30921         for ( var i = 0; i < groupCount; i++ ) {
30922           // make an array of colY values for that one group
30923           var groupColYs = this.colYs.slice( i, i + colSpan );
30924           // and get the max value of the array
30925           colGroup[i] = Math.max.apply( Math, groupColYs );
30926         }
30927         return colGroup;
30928     },
30929     /*
30930     _manageStamp : function( stamp )
30931     {
30932         var stampSize =  stamp.getSize();
30933         var offset = stamp.getBox();
30934         // get the columns that this stamp affects
30935         var firstX = this.isOriginLeft ? offset.x : offset.right;
30936         var lastX = firstX + stampSize.width;
30937         var firstCol = Math.floor( firstX / this.columnWidth );
30938         firstCol = Math.max( 0, firstCol );
30939         
30940         var lastCol = Math.floor( lastX / this.columnWidth );
30941         // lastCol should not go over if multiple of columnWidth #425
30942         lastCol -= lastX % this.columnWidth ? 0 : 1;
30943         lastCol = Math.min( this.cols - 1, lastCol );
30944         
30945         // set colYs to bottom of the stamp
30946         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30947             stampSize.height;
30948             
30949         for ( var i = firstCol; i <= lastCol; i++ ) {
30950           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30951         }
30952     },
30953     */
30954     
30955     _getContainerSize : function()
30956     {
30957         this.maxY = Math.max.apply( Math, this.colYs );
30958         var size = {
30959             height: this.maxY
30960         };
30961       
30962         if ( this.isFitWidth ) {
30963             size.width = this._getContainerFitWidth();
30964         }
30965       
30966         return size;
30967     },
30968     
30969     _getContainerFitWidth : function()
30970     {
30971         var unusedCols = 0;
30972         // count unused columns
30973         var i = this.cols;
30974         while ( --i ) {
30975           if ( this.colYs[i] !== 0 ) {
30976             break;
30977           }
30978           unusedCols++;
30979         }
30980         // fit container to columns that have been used
30981         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30982     },
30983     
30984     needsResizeLayout : function()
30985     {
30986         var previousWidth = this.containerWidth;
30987         this.getContainerWidth();
30988         return previousWidth !== this.containerWidth;
30989     }
30990  
30991 });
30992
30993  
30994
30995  /*
30996  * - LGPL
30997  *
30998  * element
30999  * 
31000  */
31001
31002 /**
31003  * @class Roo.bootstrap.MasonryBrick
31004  * @extends Roo.bootstrap.Component
31005  * Bootstrap MasonryBrick class
31006  * 
31007  * @constructor
31008  * Create a new MasonryBrick
31009  * @param {Object} config The config object
31010  */
31011
31012 Roo.bootstrap.MasonryBrick = function(config){
31013     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31014     
31015     this.addEvents({
31016         // raw events
31017         /**
31018          * @event click
31019          * When a MasonryBrick is clcik
31020          * @param {Roo.bootstrap.MasonryBrick} this
31021          * @param {Roo.EventObject} e
31022          */
31023         "click" : true
31024     });
31025 };
31026
31027 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31028     
31029     /**
31030      * @cfg {String} title
31031      */   
31032     title : '',
31033     /**
31034      * @cfg {String} html
31035      */   
31036     html : '',
31037     /**
31038      * @cfg {String} bgimage
31039      */   
31040     bgimage : '',
31041     /**
31042      * @cfg {String} videourl
31043      */   
31044     videourl : '',
31045     /**
31046      * @cfg {String} cls
31047      */   
31048     cls : '',
31049     /**
31050      * @cfg {String} href
31051      */   
31052     href : '',
31053     /**
31054      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31055      */   
31056     size : 'xs',
31057     
31058     /**
31059      * @cfg {String} (center|bottom) placetitle
31060      */   
31061     placetitle : '',
31062     
31063     /**
31064      * @cfg {Boolean} isFitContainer defalut true
31065      */   
31066     isFitContainer : true, 
31067     
31068     /**
31069      * @cfg {Boolean} preventDefault defalut false
31070      */   
31071     preventDefault : false, 
31072     
31073     getAutoCreate : function()
31074     {
31075         if(!this.isFitContainer){
31076             return this.getSplitAutoCreate();
31077         }
31078         
31079         var cls = 'masonry-brick masonry-brick-full';
31080         
31081         if(this.href.length){
31082             cls += ' masonry-brick-link';
31083         }
31084         
31085         if(this.bgimage.length){
31086             cls += ' masonry-brick-image';
31087         }
31088         
31089         if(!this.html.length){
31090             cls += ' enable-mask';
31091         }
31092         
31093         if(this.size){
31094             cls += ' masonry-' + this.size + '-brick';
31095         }
31096         
31097         if(this.placetitle.length){
31098             
31099             switch (this.placetitle) {
31100                 case 'center' :
31101                     cls += ' masonry-center-title';
31102                     break;
31103                 case 'bottom' :
31104                     cls += ' masonry-bottom-title';
31105                     break;
31106                 default:
31107                     break;
31108             }
31109             
31110         } else {
31111             if(!this.html.length && !this.bgimage.length){
31112                 cls += ' masonry-center-title';
31113             }
31114
31115             if(!this.html.length && this.bgimage.length){
31116                 cls += ' masonry-bottom-title';
31117             }
31118         }
31119         
31120         if(this.cls){
31121             cls += ' ' + this.cls;
31122         }
31123         
31124         var cfg = {
31125             tag: (this.href.length) ? 'a' : 'div',
31126             cls: cls,
31127             cn: [
31128                 {
31129                     tag: 'div',
31130                     cls: 'masonry-brick-paragraph',
31131                     cn: []
31132                 }
31133             ]
31134         };
31135         
31136         if(this.href.length){
31137             cfg.href = this.href;
31138         }
31139         
31140         var cn = cfg.cn[0].cn;
31141         
31142         if(this.title.length){
31143             cn.push({
31144                 tag: 'h4',
31145                 cls: 'masonry-brick-title',
31146                 html: this.title
31147             });
31148         }
31149         
31150         if(this.html.length){
31151             cn.push({
31152                 tag: 'p',
31153                 cls: 'masonry-brick-text',
31154                 html: this.html
31155             });
31156         }  
31157         if (!this.title.length && !this.html.length) {
31158             cfg.cn[0].cls += ' hide';
31159         }
31160         
31161         if(this.bgimage.length){
31162             cfg.cn.push({
31163                 tag: 'img',
31164                 cls: 'masonry-brick-image-view',
31165                 src: this.bgimage
31166             });
31167         }
31168         
31169         if(this.videourl.length){
31170             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31171             // youtube support only?
31172             cfg.cn.push({
31173                 tag: 'iframe',
31174                 cls: 'masonry-brick-image-view',
31175                 src: vurl,
31176                 frameborder : 0,
31177                 allowfullscreen : true
31178             });
31179             
31180             
31181         }
31182         
31183         cfg.cn.push({
31184             tag: 'div',
31185             cls: 'masonry-brick-mask'
31186         });
31187         
31188         return cfg;
31189         
31190     },
31191     
31192     getSplitAutoCreate : function()
31193     {
31194         var cls = 'masonry-brick masonry-brick-split';
31195         
31196         if(this.href.length){
31197             cls += ' masonry-brick-link';
31198         }
31199         
31200         if(this.bgimage.length){
31201             cls += ' masonry-brick-image';
31202         }
31203         
31204         if(this.size){
31205             cls += ' masonry-' + this.size + '-brick';
31206         }
31207         
31208         switch (this.placetitle) {
31209             case 'center' :
31210                 cls += ' masonry-center-title';
31211                 break;
31212             case 'bottom' :
31213                 cls += ' masonry-bottom-title';
31214                 break;
31215             default:
31216                 if(!this.bgimage.length){
31217                     cls += ' masonry-center-title';
31218                 }
31219
31220                 if(this.bgimage.length){
31221                     cls += ' masonry-bottom-title';
31222                 }
31223                 break;
31224         }
31225         
31226         if(this.cls){
31227             cls += ' ' + this.cls;
31228         }
31229         
31230         var cfg = {
31231             tag: (this.href.length) ? 'a' : 'div',
31232             cls: cls,
31233             cn: [
31234                 {
31235                     tag: 'div',
31236                     cls: 'masonry-brick-split-head',
31237                     cn: [
31238                         {
31239                             tag: 'div',
31240                             cls: 'masonry-brick-paragraph',
31241                             cn: []
31242                         }
31243                     ]
31244                 },
31245                 {
31246                     tag: 'div',
31247                     cls: 'masonry-brick-split-body',
31248                     cn: []
31249                 }
31250             ]
31251         };
31252         
31253         if(this.href.length){
31254             cfg.href = this.href;
31255         }
31256         
31257         if(this.title.length){
31258             cfg.cn[0].cn[0].cn.push({
31259                 tag: 'h4',
31260                 cls: 'masonry-brick-title',
31261                 html: this.title
31262             });
31263         }
31264         
31265         if(this.html.length){
31266             cfg.cn[1].cn.push({
31267                 tag: 'p',
31268                 cls: 'masonry-brick-text',
31269                 html: this.html
31270             });
31271         }
31272
31273         if(this.bgimage.length){
31274             cfg.cn[0].cn.push({
31275                 tag: 'img',
31276                 cls: 'masonry-brick-image-view',
31277                 src: this.bgimage
31278             });
31279         }
31280         
31281         if(this.videourl.length){
31282             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31283             // youtube support only?
31284             cfg.cn[0].cn.cn.push({
31285                 tag: 'iframe',
31286                 cls: 'masonry-brick-image-view',
31287                 src: vurl,
31288                 frameborder : 0,
31289                 allowfullscreen : true
31290             });
31291         }
31292         
31293         return cfg;
31294     },
31295     
31296     initEvents: function() 
31297     {
31298         switch (this.size) {
31299             case 'xs' :
31300                 this.x = 1;
31301                 this.y = 1;
31302                 break;
31303             case 'sm' :
31304                 this.x = 2;
31305                 this.y = 2;
31306                 break;
31307             case 'md' :
31308             case 'md-left' :
31309             case 'md-right' :
31310                 this.x = 3;
31311                 this.y = 3;
31312                 break;
31313             case 'tall' :
31314                 this.x = 2;
31315                 this.y = 3;
31316                 break;
31317             case 'wide' :
31318                 this.x = 3;
31319                 this.y = 2;
31320                 break;
31321             case 'wide-thin' :
31322                 this.x = 3;
31323                 this.y = 1;
31324                 break;
31325                         
31326             default :
31327                 break;
31328         }
31329         
31330         if(Roo.isTouch){
31331             this.el.on('touchstart', this.onTouchStart, this);
31332             this.el.on('touchmove', this.onTouchMove, this);
31333             this.el.on('touchend', this.onTouchEnd, this);
31334             this.el.on('contextmenu', this.onContextMenu, this);
31335         } else {
31336             this.el.on('mouseenter'  ,this.enter, this);
31337             this.el.on('mouseleave', this.leave, this);
31338             this.el.on('click', this.onClick, this);
31339         }
31340         
31341         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31342             this.parent().bricks.push(this);   
31343         }
31344         
31345     },
31346     
31347     onClick: function(e, el)
31348     {
31349         var time = this.endTimer - this.startTimer;
31350         
31351         if(Roo.isTouch){
31352             if(time > 1000){
31353                 e.preventDefault();
31354                 return;
31355             }
31356         }
31357         
31358         if(!this.preventDefault){
31359             return;
31360         }
31361         
31362         e.preventDefault();
31363         this.fireEvent('click', this);
31364     },
31365     
31366     enter: function(e, el)
31367     {
31368         e.preventDefault();
31369         
31370         if(!this.isFitContainer){
31371             return;
31372         }
31373         
31374         if(this.bgimage.length && this.html.length){
31375             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31376         }
31377     },
31378     
31379     leave: function(e, el)
31380     {
31381         e.preventDefault();
31382         
31383         if(!this.isFitContainer){
31384             return;
31385         }
31386         
31387         if(this.bgimage.length && this.html.length){
31388             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31389         }
31390     },
31391     
31392     onTouchStart: function(e, el)
31393     {
31394 //        e.preventDefault();
31395         
31396         this.touchmoved = false;
31397         
31398         if(!this.isFitContainer){
31399             return;
31400         }
31401         
31402         if(!this.bgimage.length || !this.html.length){
31403             return;
31404         }
31405         
31406         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31407         
31408         this.timer = new Date().getTime();
31409         
31410     },
31411     
31412     onTouchMove: function(e, el)
31413     {
31414         this.touchmoved = true;
31415     },
31416     
31417     onContextMenu : function(e,el)
31418     {
31419         e.preventDefault();
31420         e.stopPropagation();
31421         return false;
31422     },
31423     
31424     onTouchEnd: function(e, el)
31425     {
31426 //        e.preventDefault();
31427         
31428         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31429         
31430             this.leave(e,el);
31431             
31432             return;
31433         }
31434         
31435         if(!this.bgimage.length || !this.html.length){
31436             
31437             if(this.href.length){
31438                 window.location.href = this.href;
31439             }
31440             
31441             return;
31442         }
31443         
31444         if(!this.isFitContainer){
31445             return;
31446         }
31447         
31448         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31449         
31450         window.location.href = this.href;
31451     }
31452     
31453 });
31454
31455  
31456
31457  /*
31458  * - LGPL
31459  *
31460  * element
31461  * 
31462  */
31463
31464 /**
31465  * @class Roo.bootstrap.Brick
31466  * @extends Roo.bootstrap.Component
31467  * Bootstrap Brick class
31468  * 
31469  * @constructor
31470  * Create a new Brick
31471  * @param {Object} config The config object
31472  */
31473
31474 Roo.bootstrap.Brick = function(config){
31475     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31476     
31477     this.addEvents({
31478         // raw events
31479         /**
31480          * @event click
31481          * When a Brick is click
31482          * @param {Roo.bootstrap.Brick} this
31483          * @param {Roo.EventObject} e
31484          */
31485         "click" : true
31486     });
31487 };
31488
31489 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31490     
31491     /**
31492      * @cfg {String} title
31493      */   
31494     title : '',
31495     /**
31496      * @cfg {String} html
31497      */   
31498     html : '',
31499     /**
31500      * @cfg {String} bgimage
31501      */   
31502     bgimage : '',
31503     /**
31504      * @cfg {String} cls
31505      */   
31506     cls : '',
31507     /**
31508      * @cfg {String} href
31509      */   
31510     href : '',
31511     /**
31512      * @cfg {String} video
31513      */   
31514     video : '',
31515     /**
31516      * @cfg {Boolean} square
31517      */   
31518     square : true,
31519     
31520     getAutoCreate : function()
31521     {
31522         var cls = 'roo-brick';
31523         
31524         if(this.href.length){
31525             cls += ' roo-brick-link';
31526         }
31527         
31528         if(this.bgimage.length){
31529             cls += ' roo-brick-image';
31530         }
31531         
31532         if(!this.html.length && !this.bgimage.length){
31533             cls += ' roo-brick-center-title';
31534         }
31535         
31536         if(!this.html.length && this.bgimage.length){
31537             cls += ' roo-brick-bottom-title';
31538         }
31539         
31540         if(this.cls){
31541             cls += ' ' + this.cls;
31542         }
31543         
31544         var cfg = {
31545             tag: (this.href.length) ? 'a' : 'div',
31546             cls: cls,
31547             cn: [
31548                 {
31549                     tag: 'div',
31550                     cls: 'roo-brick-paragraph',
31551                     cn: []
31552                 }
31553             ]
31554         };
31555         
31556         if(this.href.length){
31557             cfg.href = this.href;
31558         }
31559         
31560         var cn = cfg.cn[0].cn;
31561         
31562         if(this.title.length){
31563             cn.push({
31564                 tag: 'h4',
31565                 cls: 'roo-brick-title',
31566                 html: this.title
31567             });
31568         }
31569         
31570         if(this.html.length){
31571             cn.push({
31572                 tag: 'p',
31573                 cls: 'roo-brick-text',
31574                 html: this.html
31575             });
31576         } else {
31577             cn.cls += ' hide';
31578         }
31579         
31580         if(this.bgimage.length){
31581             cfg.cn.push({
31582                 tag: 'img',
31583                 cls: 'roo-brick-image-view',
31584                 src: this.bgimage
31585             });
31586         }
31587         
31588         return cfg;
31589     },
31590     
31591     initEvents: function() 
31592     {
31593         if(this.title.length || this.html.length){
31594             this.el.on('mouseenter'  ,this.enter, this);
31595             this.el.on('mouseleave', this.leave, this);
31596         }
31597         
31598         
31599         Roo.EventManager.onWindowResize(this.resize, this); 
31600         
31601         this.resize();
31602     },
31603     
31604     resize : function()
31605     {
31606         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31607         
31608         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31609         
31610         if(this.bgimage.length){
31611             var image = this.el.select('.roo-brick-image-view', true).first();
31612             image.setWidth(paragraph.getWidth());
31613             image.setHeight(paragraph.getWidth());
31614             
31615             this.el.setHeight(paragraph.getWidth());
31616             
31617         }
31618         
31619     },
31620     
31621     enter: function(e, el)
31622     {
31623         e.preventDefault();
31624         
31625         if(this.bgimage.length){
31626             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31627             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31628         }
31629     },
31630     
31631     leave: function(e, el)
31632     {
31633         e.preventDefault();
31634         
31635         if(this.bgimage.length){
31636             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31637             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31638         }
31639     }
31640     
31641 });
31642
31643  
31644
31645  /*
31646  * - LGPL
31647  *
31648  * Input
31649  * 
31650  */
31651
31652 /**
31653  * @class Roo.bootstrap.NumberField
31654  * @extends Roo.bootstrap.Input
31655  * Bootstrap NumberField class
31656  * 
31657  * 
31658  * 
31659  * 
31660  * @constructor
31661  * Create a new NumberField
31662  * @param {Object} config The config object
31663  */
31664
31665 Roo.bootstrap.NumberField = function(config){
31666     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31667 };
31668
31669 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31670     
31671     /**
31672      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31673      */
31674     allowDecimals : true,
31675     /**
31676      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31677      */
31678     decimalSeparator : ".",
31679     /**
31680      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31681      */
31682     decimalPrecision : 2,
31683     /**
31684      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31685      */
31686     allowNegative : true,
31687     /**
31688      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31689      */
31690     minValue : Number.NEGATIVE_INFINITY,
31691     /**
31692      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31693      */
31694     maxValue : Number.MAX_VALUE,
31695     /**
31696      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31697      */
31698     minText : "The minimum value for this field is {0}",
31699     /**
31700      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31701      */
31702     maxText : "The maximum value for this field is {0}",
31703     /**
31704      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
31705      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31706      */
31707     nanText : "{0} is not a valid number",
31708     /**
31709      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31710      */
31711     castInt : true,
31712
31713     // private
31714     initEvents : function()
31715     {   
31716         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31717         
31718         var allowed = "0123456789";
31719         
31720         if(this.allowDecimals){
31721             allowed += this.decimalSeparator;
31722         }
31723         
31724         if(this.allowNegative){
31725             allowed += "-";
31726         }
31727         
31728         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31729         
31730         var keyPress = function(e){
31731             
31732             var k = e.getKey();
31733             
31734             var c = e.getCharCode();
31735             
31736             if(
31737                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31738                     allowed.indexOf(String.fromCharCode(c)) === -1
31739             ){
31740                 e.stopEvent();
31741                 return;
31742             }
31743             
31744             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31745                 return;
31746             }
31747             
31748             if(allowed.indexOf(String.fromCharCode(c)) === -1){
31749                 e.stopEvent();
31750             }
31751         };
31752         
31753         this.el.on("keypress", keyPress, this);
31754     },
31755     
31756     validateValue : function(value)
31757     {
31758         
31759         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31760             return false;
31761         }
31762         
31763         var num = this.parseValue(value);
31764         
31765         if(isNaN(num)){
31766             this.markInvalid(String.format(this.nanText, value));
31767             return false;
31768         }
31769         
31770         if(num < this.minValue){
31771             this.markInvalid(String.format(this.minText, this.minValue));
31772             return false;
31773         }
31774         
31775         if(num > this.maxValue){
31776             this.markInvalid(String.format(this.maxText, this.maxValue));
31777             return false;
31778         }
31779         
31780         return true;
31781     },
31782
31783     getValue : function()
31784     {
31785         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31786     },
31787
31788     parseValue : function(value)
31789     {
31790         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31791         return isNaN(value) ? '' : value;
31792     },
31793
31794     fixPrecision : function(value)
31795     {
31796         var nan = isNaN(value);
31797         
31798         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31799             return nan ? '' : value;
31800         }
31801         return parseFloat(value).toFixed(this.decimalPrecision);
31802     },
31803
31804     setValue : function(v)
31805     {
31806         v = this.fixPrecision(v);
31807         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31808     },
31809
31810     decimalPrecisionFcn : function(v)
31811     {
31812         return Math.floor(v);
31813     },
31814
31815     beforeBlur : function()
31816     {
31817         if(!this.castInt){
31818             return;
31819         }
31820         
31821         var v = this.parseValue(this.getRawValue());
31822         if(v){
31823             this.setValue(v);
31824         }
31825     }
31826     
31827 });
31828
31829  
31830
31831 /*
31832 * Licence: LGPL
31833 */
31834
31835 /**
31836  * @class Roo.bootstrap.DocumentSlider
31837  * @extends Roo.bootstrap.Component
31838  * Bootstrap DocumentSlider class
31839  * 
31840  * @constructor
31841  * Create a new DocumentViewer
31842  * @param {Object} config The config object
31843  */
31844
31845 Roo.bootstrap.DocumentSlider = function(config){
31846     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31847     
31848     this.files = [];
31849     
31850     this.addEvents({
31851         /**
31852          * @event initial
31853          * Fire after initEvent
31854          * @param {Roo.bootstrap.DocumentSlider} this
31855          */
31856         "initial" : true,
31857         /**
31858          * @event update
31859          * Fire after update
31860          * @param {Roo.bootstrap.DocumentSlider} this
31861          */
31862         "update" : true,
31863         /**
31864          * @event click
31865          * Fire after click
31866          * @param {Roo.bootstrap.DocumentSlider} this
31867          */
31868         "click" : true
31869     });
31870 };
31871
31872 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
31873     
31874     files : false,
31875     
31876     indicator : 0,
31877     
31878     getAutoCreate : function()
31879     {
31880         var cfg = {
31881             tag : 'div',
31882             cls : 'roo-document-slider',
31883             cn : [
31884                 {
31885                     tag : 'div',
31886                     cls : 'roo-document-slider-header',
31887                     cn : [
31888                         {
31889                             tag : 'div',
31890                             cls : 'roo-document-slider-header-title'
31891                         }
31892                     ]
31893                 },
31894                 {
31895                     tag : 'div',
31896                     cls : 'roo-document-slider-body',
31897                     cn : [
31898                         {
31899                             tag : 'div',
31900                             cls : 'roo-document-slider-prev',
31901                             cn : [
31902                                 {
31903                                     tag : 'i',
31904                                     cls : 'fa fa-chevron-left'
31905                                 }
31906                             ]
31907                         },
31908                         {
31909                             tag : 'div',
31910                             cls : 'roo-document-slider-thumb',
31911                             cn : [
31912                                 {
31913                                     tag : 'img',
31914                                     cls : 'roo-document-slider-image'
31915                                 }
31916                             ]
31917                         },
31918                         {
31919                             tag : 'div',
31920                             cls : 'roo-document-slider-next',
31921                             cn : [
31922                                 {
31923                                     tag : 'i',
31924                                     cls : 'fa fa-chevron-right'
31925                                 }
31926                             ]
31927                         }
31928                     ]
31929                 }
31930             ]
31931         };
31932         
31933         return cfg;
31934     },
31935     
31936     initEvents : function()
31937     {
31938         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31939         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31940         
31941         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31942         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31943         
31944         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31945         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31946         
31947         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31948         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31949         
31950         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31951         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31952         
31953         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31954         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31955         
31956         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31957         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31958         
31959         this.thumbEl.on('click', this.onClick, this);
31960         
31961         this.prevIndicator.on('click', this.prev, this);
31962         
31963         this.nextIndicator.on('click', this.next, this);
31964         
31965     },
31966     
31967     initial : function()
31968     {
31969         if(this.files.length){
31970             this.indicator = 1;
31971             this.update()
31972         }
31973         
31974         this.fireEvent('initial', this);
31975     },
31976     
31977     update : function()
31978     {
31979         this.imageEl.attr('src', this.files[this.indicator - 1]);
31980         
31981         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
31982         
31983         this.prevIndicator.show();
31984         
31985         if(this.indicator == 1){
31986             this.prevIndicator.hide();
31987         }
31988         
31989         this.nextIndicator.show();
31990         
31991         if(this.indicator == this.files.length){
31992             this.nextIndicator.hide();
31993         }
31994         
31995         this.thumbEl.scrollTo('top');
31996         
31997         this.fireEvent('update', this);
31998     },
31999     
32000     onClick : function(e)
32001     {
32002         e.preventDefault();
32003         
32004         this.fireEvent('click', this);
32005     },
32006     
32007     prev : function(e)
32008     {
32009         e.preventDefault();
32010         
32011         this.indicator = Math.max(1, this.indicator - 1);
32012         
32013         this.update();
32014     },
32015     
32016     next : function(e)
32017     {
32018         e.preventDefault();
32019         
32020         this.indicator = Math.min(this.files.length, this.indicator + 1);
32021         
32022         this.update();
32023     }
32024 });
32025 /*
32026  * Based on:
32027  * Ext JS Library 1.1.1
32028  * Copyright(c) 2006-2007, Ext JS, LLC.
32029  *
32030  * Originally Released Under LGPL - original licence link has changed is not relivant.
32031  *
32032  * Fork - LGPL
32033  * <script type="text/javascript">
32034  */
32035
32036
32037 /**
32038  * @class Roo.bootstrap.SplitBar
32039  * @extends Roo.util.Observable
32040  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32041  * <br><br>
32042  * Usage:
32043  * <pre><code>
32044 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32045                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32046 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32047 split.minSize = 100;
32048 split.maxSize = 600;
32049 split.animate = true;
32050 split.on('moved', splitterMoved);
32051 </code></pre>
32052  * @constructor
32053  * Create a new SplitBar
32054  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32055  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32056  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32057  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32058                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32059                         position of the SplitBar).
32060  */
32061 Roo.bootstrap.SplitBar = function(cfg){
32062     
32063     /** @private */
32064     
32065     //{
32066     //  dragElement : elm
32067     //  resizingElement: el,
32068         // optional..
32069     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32070     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32071         // existingProxy ???
32072     //}
32073     
32074     this.el = Roo.get(cfg.dragElement, true);
32075     this.el.dom.unselectable = "on";
32076     /** @private */
32077     this.resizingEl = Roo.get(cfg.resizingElement, true);
32078
32079     /**
32080      * @private
32081      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32082      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32083      * @type Number
32084      */
32085     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32086     
32087     /**
32088      * The minimum size of the resizing element. (Defaults to 0)
32089      * @type Number
32090      */
32091     this.minSize = 0;
32092     
32093     /**
32094      * The maximum size of the resizing element. (Defaults to 2000)
32095      * @type Number
32096      */
32097     this.maxSize = 2000;
32098     
32099     /**
32100      * Whether to animate the transition to the new size
32101      * @type Boolean
32102      */
32103     this.animate = false;
32104     
32105     /**
32106      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32107      * @type Boolean
32108      */
32109     this.useShim = false;
32110     
32111     /** @private */
32112     this.shim = null;
32113     
32114     if(!cfg.existingProxy){
32115         /** @private */
32116         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32117     }else{
32118         this.proxy = Roo.get(cfg.existingProxy).dom;
32119     }
32120     /** @private */
32121     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32122     
32123     /** @private */
32124     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32125     
32126     /** @private */
32127     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32128     
32129     /** @private */
32130     this.dragSpecs = {};
32131     
32132     /**
32133      * @private The adapter to use to positon and resize elements
32134      */
32135     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32136     this.adapter.init(this);
32137     
32138     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32139         /** @private */
32140         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32141         this.el.addClass("roo-splitbar-h");
32142     }else{
32143         /** @private */
32144         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32145         this.el.addClass("roo-splitbar-v");
32146     }
32147     
32148     this.addEvents({
32149         /**
32150          * @event resize
32151          * Fires when the splitter is moved (alias for {@link #event-moved})
32152          * @param {Roo.bootstrap.SplitBar} this
32153          * @param {Number} newSize the new width or height
32154          */
32155         "resize" : true,
32156         /**
32157          * @event moved
32158          * Fires when the splitter is moved
32159          * @param {Roo.bootstrap.SplitBar} this
32160          * @param {Number} newSize the new width or height
32161          */
32162         "moved" : true,
32163         /**
32164          * @event beforeresize
32165          * Fires before the splitter is dragged
32166          * @param {Roo.bootstrap.SplitBar} this
32167          */
32168         "beforeresize" : true,
32169
32170         "beforeapply" : true
32171     });
32172
32173     Roo.util.Observable.call(this);
32174 };
32175
32176 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32177     onStartProxyDrag : function(x, y){
32178         this.fireEvent("beforeresize", this);
32179         if(!this.overlay){
32180             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32181             o.unselectable();
32182             o.enableDisplayMode("block");
32183             // all splitbars share the same overlay
32184             Roo.bootstrap.SplitBar.prototype.overlay = o;
32185         }
32186         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32187         this.overlay.show();
32188         Roo.get(this.proxy).setDisplayed("block");
32189         var size = this.adapter.getElementSize(this);
32190         this.activeMinSize = this.getMinimumSize();;
32191         this.activeMaxSize = this.getMaximumSize();;
32192         var c1 = size - this.activeMinSize;
32193         var c2 = Math.max(this.activeMaxSize - size, 0);
32194         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32195             this.dd.resetConstraints();
32196             this.dd.setXConstraint(
32197                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32198                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32199             );
32200             this.dd.setYConstraint(0, 0);
32201         }else{
32202             this.dd.resetConstraints();
32203             this.dd.setXConstraint(0, 0);
32204             this.dd.setYConstraint(
32205                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32206                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32207             );
32208          }
32209         this.dragSpecs.startSize = size;
32210         this.dragSpecs.startPoint = [x, y];
32211         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32212     },
32213     
32214     /** 
32215      * @private Called after the drag operation by the DDProxy
32216      */
32217     onEndProxyDrag : function(e){
32218         Roo.get(this.proxy).setDisplayed(false);
32219         var endPoint = Roo.lib.Event.getXY(e);
32220         if(this.overlay){
32221             this.overlay.hide();
32222         }
32223         var newSize;
32224         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32225             newSize = this.dragSpecs.startSize + 
32226                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32227                     endPoint[0] - this.dragSpecs.startPoint[0] :
32228                     this.dragSpecs.startPoint[0] - endPoint[0]
32229                 );
32230         }else{
32231             newSize = this.dragSpecs.startSize + 
32232                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32233                     endPoint[1] - this.dragSpecs.startPoint[1] :
32234                     this.dragSpecs.startPoint[1] - endPoint[1]
32235                 );
32236         }
32237         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32238         if(newSize != this.dragSpecs.startSize){
32239             if(this.fireEvent('beforeapply', this, newSize) !== false){
32240                 this.adapter.setElementSize(this, newSize);
32241                 this.fireEvent("moved", this, newSize);
32242                 this.fireEvent("resize", this, newSize);
32243             }
32244         }
32245     },
32246     
32247     /**
32248      * Get the adapter this SplitBar uses
32249      * @return The adapter object
32250      */
32251     getAdapter : function(){
32252         return this.adapter;
32253     },
32254     
32255     /**
32256      * Set the adapter this SplitBar uses
32257      * @param {Object} adapter A SplitBar adapter object
32258      */
32259     setAdapter : function(adapter){
32260         this.adapter = adapter;
32261         this.adapter.init(this);
32262     },
32263     
32264     /**
32265      * Gets the minimum size for the resizing element
32266      * @return {Number} The minimum size
32267      */
32268     getMinimumSize : function(){
32269         return this.minSize;
32270     },
32271     
32272     /**
32273      * Sets the minimum size for the resizing element
32274      * @param {Number} minSize The minimum size
32275      */
32276     setMinimumSize : function(minSize){
32277         this.minSize = minSize;
32278     },
32279     
32280     /**
32281      * Gets the maximum size for the resizing element
32282      * @return {Number} The maximum size
32283      */
32284     getMaximumSize : function(){
32285         return this.maxSize;
32286     },
32287     
32288     /**
32289      * Sets the maximum size for the resizing element
32290      * @param {Number} maxSize The maximum size
32291      */
32292     setMaximumSize : function(maxSize){
32293         this.maxSize = maxSize;
32294     },
32295     
32296     /**
32297      * Sets the initialize size for the resizing element
32298      * @param {Number} size The initial size
32299      */
32300     setCurrentSize : function(size){
32301         var oldAnimate = this.animate;
32302         this.animate = false;
32303         this.adapter.setElementSize(this, size);
32304         this.animate = oldAnimate;
32305     },
32306     
32307     /**
32308      * Destroy this splitbar. 
32309      * @param {Boolean} removeEl True to remove the element
32310      */
32311     destroy : function(removeEl){
32312         if(this.shim){
32313             this.shim.remove();
32314         }
32315         this.dd.unreg();
32316         this.proxy.parentNode.removeChild(this.proxy);
32317         if(removeEl){
32318             this.el.remove();
32319         }
32320     }
32321 });
32322
32323 /**
32324  * @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.
32325  */
32326 Roo.bootstrap.SplitBar.createProxy = function(dir){
32327     var proxy = new Roo.Element(document.createElement("div"));
32328     proxy.unselectable();
32329     var cls = 'roo-splitbar-proxy';
32330     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32331     document.body.appendChild(proxy.dom);
32332     return proxy.dom;
32333 };
32334
32335 /** 
32336  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32337  * Default Adapter. It assumes the splitter and resizing element are not positioned
32338  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32339  */
32340 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32341 };
32342
32343 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32344     // do nothing for now
32345     init : function(s){
32346     
32347     },
32348     /**
32349      * Called before drag operations to get the current size of the resizing element. 
32350      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32351      */
32352      getElementSize : function(s){
32353         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32354             return s.resizingEl.getWidth();
32355         }else{
32356             return s.resizingEl.getHeight();
32357         }
32358     },
32359     
32360     /**
32361      * Called after drag operations to set the size of the resizing element.
32362      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32363      * @param {Number} newSize The new size to set
32364      * @param {Function} onComplete A function to be invoked when resizing is complete
32365      */
32366     setElementSize : function(s, newSize, onComplete){
32367         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32368             if(!s.animate){
32369                 s.resizingEl.setWidth(newSize);
32370                 if(onComplete){
32371                     onComplete(s, newSize);
32372                 }
32373             }else{
32374                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32375             }
32376         }else{
32377             
32378             if(!s.animate){
32379                 s.resizingEl.setHeight(newSize);
32380                 if(onComplete){
32381                     onComplete(s, newSize);
32382                 }
32383             }else{
32384                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32385             }
32386         }
32387     }
32388 };
32389
32390 /** 
32391  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32392  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32393  * Adapter that  moves the splitter element to align with the resized sizing element. 
32394  * Used with an absolute positioned SplitBar.
32395  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32396  * document.body, make sure you assign an id to the body element.
32397  */
32398 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32399     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32400     this.container = Roo.get(container);
32401 };
32402
32403 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32404     init : function(s){
32405         this.basic.init(s);
32406     },
32407     
32408     getElementSize : function(s){
32409         return this.basic.getElementSize(s);
32410     },
32411     
32412     setElementSize : function(s, newSize, onComplete){
32413         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32414     },
32415     
32416     moveSplitter : function(s){
32417         var yes = Roo.bootstrap.SplitBar;
32418         switch(s.placement){
32419             case yes.LEFT:
32420                 s.el.setX(s.resizingEl.getRight());
32421                 break;
32422             case yes.RIGHT:
32423                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32424                 break;
32425             case yes.TOP:
32426                 s.el.setY(s.resizingEl.getBottom());
32427                 break;
32428             case yes.BOTTOM:
32429                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32430                 break;
32431         }
32432     }
32433 };
32434
32435 /**
32436  * Orientation constant - Create a vertical SplitBar
32437  * @static
32438  * @type Number
32439  */
32440 Roo.bootstrap.SplitBar.VERTICAL = 1;
32441
32442 /**
32443  * Orientation constant - Create a horizontal SplitBar
32444  * @static
32445  * @type Number
32446  */
32447 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32448
32449 /**
32450  * Placement constant - The resizing element is to the left of the splitter element
32451  * @static
32452  * @type Number
32453  */
32454 Roo.bootstrap.SplitBar.LEFT = 1;
32455
32456 /**
32457  * Placement constant - The resizing element is to the right of the splitter element
32458  * @static
32459  * @type Number
32460  */
32461 Roo.bootstrap.SplitBar.RIGHT = 2;
32462
32463 /**
32464  * Placement constant - The resizing element is positioned above the splitter element
32465  * @static
32466  * @type Number
32467  */
32468 Roo.bootstrap.SplitBar.TOP = 3;
32469
32470 /**
32471  * Placement constant - The resizing element is positioned under splitter element
32472  * @static
32473  * @type Number
32474  */
32475 Roo.bootstrap.SplitBar.BOTTOM = 4;
32476 Roo.namespace("Roo.bootstrap.layout");/*
32477  * Based on:
32478  * Ext JS Library 1.1.1
32479  * Copyright(c) 2006-2007, Ext JS, LLC.
32480  *
32481  * Originally Released Under LGPL - original licence link has changed is not relivant.
32482  *
32483  * Fork - LGPL
32484  * <script type="text/javascript">
32485  */
32486
32487 /**
32488  * @class Roo.bootstrap.layout.Manager
32489  * @extends Roo.bootstrap.Component
32490  * Base class for layout managers.
32491  */
32492 Roo.bootstrap.layout.Manager = function(config)
32493 {
32494     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32495
32496
32497
32498
32499
32500     /** false to disable window resize monitoring @type Boolean */
32501     this.monitorWindowResize = true;
32502     this.regions = {};
32503     this.addEvents({
32504         /**
32505          * @event layout
32506          * Fires when a layout is performed.
32507          * @param {Roo.LayoutManager} this
32508          */
32509         "layout" : true,
32510         /**
32511          * @event regionresized
32512          * Fires when the user resizes a region.
32513          * @param {Roo.LayoutRegion} region The resized region
32514          * @param {Number} newSize The new size (width for east/west, height for north/south)
32515          */
32516         "regionresized" : true,
32517         /**
32518          * @event regioncollapsed
32519          * Fires when a region is collapsed.
32520          * @param {Roo.LayoutRegion} region The collapsed region
32521          */
32522         "regioncollapsed" : true,
32523         /**
32524          * @event regionexpanded
32525          * Fires when a region is expanded.
32526          * @param {Roo.LayoutRegion} region The expanded region
32527          */
32528         "regionexpanded" : true
32529     });
32530     this.updating = false;
32531
32532     if (config.el) {
32533         this.el = Roo.get(config.el);
32534         this.initEvents();
32535     }
32536
32537 };
32538
32539 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32540
32541
32542     regions : null,
32543
32544     monitorWindowResize : true,
32545
32546
32547     updating : false,
32548
32549
32550     onRender : function(ct, position)
32551     {
32552         if(!this.el){
32553             this.el = Roo.get(ct);
32554             this.initEvents();
32555         }
32556         //this.fireEvent('render',this);
32557     },
32558
32559
32560     initEvents: function()
32561     {
32562
32563
32564         // ie scrollbar fix
32565         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32566             document.body.scroll = "no";
32567         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32568             this.el.position('relative');
32569         }
32570         this.id = this.el.id;
32571         this.el.addClass("roo-layout-container");
32572         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32573         if(this.el.dom != document.body ) {
32574             this.el.on('resize', this.layout,this);
32575             this.el.on('show', this.layout,this);
32576         }
32577
32578     },
32579
32580     /**
32581      * Returns true if this layout is currently being updated
32582      * @return {Boolean}
32583      */
32584     isUpdating : function(){
32585         return this.updating;
32586     },
32587
32588     /**
32589      * Suspend the LayoutManager from doing auto-layouts while
32590      * making multiple add or remove calls
32591      */
32592     beginUpdate : function(){
32593         this.updating = true;
32594     },
32595
32596     /**
32597      * Restore auto-layouts and optionally disable the manager from performing a layout
32598      * @param {Boolean} noLayout true to disable a layout update
32599      */
32600     endUpdate : function(noLayout){
32601         this.updating = false;
32602         if(!noLayout){
32603             this.layout();
32604         }
32605     },
32606
32607     layout: function(){
32608         // abstract...
32609     },
32610
32611     onRegionResized : function(region, newSize){
32612         this.fireEvent("regionresized", region, newSize);
32613         this.layout();
32614     },
32615
32616     onRegionCollapsed : function(region){
32617         this.fireEvent("regioncollapsed", region);
32618     },
32619
32620     onRegionExpanded : function(region){
32621         this.fireEvent("regionexpanded", region);
32622     },
32623
32624     /**
32625      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32626      * performs box-model adjustments.
32627      * @return {Object} The size as an object {width: (the width), height: (the height)}
32628      */
32629     getViewSize : function()
32630     {
32631         var size;
32632         if(this.el.dom != document.body){
32633             size = this.el.getSize();
32634         }else{
32635             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32636         }
32637         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32638         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32639         return size;
32640     },
32641
32642     /**
32643      * Returns the Element this layout is bound to.
32644      * @return {Roo.Element}
32645      */
32646     getEl : function(){
32647         return this.el;
32648     },
32649
32650     /**
32651      * Returns the specified region.
32652      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32653      * @return {Roo.LayoutRegion}
32654      */
32655     getRegion : function(target){
32656         return this.regions[target.toLowerCase()];
32657     },
32658
32659     onWindowResize : function(){
32660         if(this.monitorWindowResize){
32661             this.layout();
32662         }
32663     }
32664 });
32665 /*
32666  * Based on:
32667  * Ext JS Library 1.1.1
32668  * Copyright(c) 2006-2007, Ext JS, LLC.
32669  *
32670  * Originally Released Under LGPL - original licence link has changed is not relivant.
32671  *
32672  * Fork - LGPL
32673  * <script type="text/javascript">
32674  */
32675 /**
32676  * @class Roo.bootstrap.layout.Border
32677  * @extends Roo.bootstrap.layout.Manager
32678  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32679  * please see: examples/bootstrap/nested.html<br><br>
32680  
32681 <b>The container the layout is rendered into can be either the body element or any other element.
32682 If it is not the body element, the container needs to either be an absolute positioned element,
32683 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32684 the container size if it is not the body element.</b>
32685
32686 * @constructor
32687 * Create a new Border
32688 * @param {Object} config Configuration options
32689  */
32690 Roo.bootstrap.layout.Border = function(config){
32691     config = config || {};
32692     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32693     
32694     
32695     
32696     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32697         if(config[region]){
32698             config[region].region = region;
32699             this.addRegion(config[region]);
32700         }
32701     },this);
32702     
32703 };
32704
32705 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32706
32707 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32708     /**
32709      * Creates and adds a new region if it doesn't already exist.
32710      * @param {String} target The target region key (north, south, east, west or center).
32711      * @param {Object} config The regions config object
32712      * @return {BorderLayoutRegion} The new region
32713      */
32714     addRegion : function(config)
32715     {
32716         if(!this.regions[config.region]){
32717             var r = this.factory(config);
32718             this.bindRegion(r);
32719         }
32720         return this.regions[config.region];
32721     },
32722
32723     // private (kinda)
32724     bindRegion : function(r){
32725         this.regions[r.config.region] = r;
32726         
32727         r.on("visibilitychange",    this.layout, this);
32728         r.on("paneladded",          this.layout, this);
32729         r.on("panelremoved",        this.layout, this);
32730         r.on("invalidated",         this.layout, this);
32731         r.on("resized",             this.onRegionResized, this);
32732         r.on("collapsed",           this.onRegionCollapsed, this);
32733         r.on("expanded",            this.onRegionExpanded, this);
32734     },
32735
32736     /**
32737      * Performs a layout update.
32738      */
32739     layout : function()
32740     {
32741         if(this.updating) {
32742             return;
32743         }
32744         
32745         // render all the rebions if they have not been done alreayd?
32746         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32747             if(this.regions[region] && !this.regions[region].bodyEl){
32748                 this.regions[region].onRender(this.el)
32749             }
32750         },this);
32751         
32752         var size = this.getViewSize();
32753         var w = size.width;
32754         var h = size.height;
32755         var centerW = w;
32756         var centerH = h;
32757         var centerY = 0;
32758         var centerX = 0;
32759         //var x = 0, y = 0;
32760
32761         var rs = this.regions;
32762         var north = rs["north"];
32763         var south = rs["south"]; 
32764         var west = rs["west"];
32765         var east = rs["east"];
32766         var center = rs["center"];
32767         //if(this.hideOnLayout){ // not supported anymore
32768             //c.el.setStyle("display", "none");
32769         //}
32770         if(north && north.isVisible()){
32771             var b = north.getBox();
32772             var m = north.getMargins();
32773             b.width = w - (m.left+m.right);
32774             b.x = m.left;
32775             b.y = m.top;
32776             centerY = b.height + b.y + m.bottom;
32777             centerH -= centerY;
32778             north.updateBox(this.safeBox(b));
32779         }
32780         if(south && south.isVisible()){
32781             var b = south.getBox();
32782             var m = south.getMargins();
32783             b.width = w - (m.left+m.right);
32784             b.x = m.left;
32785             var totalHeight = (b.height + m.top + m.bottom);
32786             b.y = h - totalHeight + m.top;
32787             centerH -= totalHeight;
32788             south.updateBox(this.safeBox(b));
32789         }
32790         if(west && west.isVisible()){
32791             var b = west.getBox();
32792             var m = west.getMargins();
32793             b.height = centerH - (m.top+m.bottom);
32794             b.x = m.left;
32795             b.y = centerY + m.top;
32796             var totalWidth = (b.width + m.left + m.right);
32797             centerX += totalWidth;
32798             centerW -= totalWidth;
32799             west.updateBox(this.safeBox(b));
32800         }
32801         if(east && east.isVisible()){
32802             var b = east.getBox();
32803             var m = east.getMargins();
32804             b.height = centerH - (m.top+m.bottom);
32805             var totalWidth = (b.width + m.left + m.right);
32806             b.x = w - totalWidth + m.left;
32807             b.y = centerY + m.top;
32808             centerW -= totalWidth;
32809             east.updateBox(this.safeBox(b));
32810         }
32811         if(center){
32812             var m = center.getMargins();
32813             var centerBox = {
32814                 x: centerX + m.left,
32815                 y: centerY + m.top,
32816                 width: centerW - (m.left+m.right),
32817                 height: centerH - (m.top+m.bottom)
32818             };
32819             //if(this.hideOnLayout){
32820                 //center.el.setStyle("display", "block");
32821             //}
32822             center.updateBox(this.safeBox(centerBox));
32823         }
32824         this.el.repaint();
32825         this.fireEvent("layout", this);
32826     },
32827
32828     // private
32829     safeBox : function(box){
32830         box.width = Math.max(0, box.width);
32831         box.height = Math.max(0, box.height);
32832         return box;
32833     },
32834
32835     /**
32836      * Adds a ContentPanel (or subclass) to this layout.
32837      * @param {String} target The target region key (north, south, east, west or center).
32838      * @param {Roo.ContentPanel} panel The panel to add
32839      * @return {Roo.ContentPanel} The added panel
32840      */
32841     add : function(target, panel){
32842          
32843         target = target.toLowerCase();
32844         return this.regions[target].add(panel);
32845     },
32846
32847     /**
32848      * Remove a ContentPanel (or subclass) to this layout.
32849      * @param {String} target The target region key (north, south, east, west or center).
32850      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32851      * @return {Roo.ContentPanel} The removed panel
32852      */
32853     remove : function(target, panel){
32854         target = target.toLowerCase();
32855         return this.regions[target].remove(panel);
32856     },
32857
32858     /**
32859      * Searches all regions for a panel with the specified id
32860      * @param {String} panelId
32861      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32862      */
32863     findPanel : function(panelId){
32864         var rs = this.regions;
32865         for(var target in rs){
32866             if(typeof rs[target] != "function"){
32867                 var p = rs[target].getPanel(panelId);
32868                 if(p){
32869                     return p;
32870                 }
32871             }
32872         }
32873         return null;
32874     },
32875
32876     /**
32877      * Searches all regions for a panel with the specified id and activates (shows) it.
32878      * @param {String/ContentPanel} panelId The panels id or the panel itself
32879      * @return {Roo.ContentPanel} The shown panel or null
32880      */
32881     showPanel : function(panelId) {
32882       var rs = this.regions;
32883       for(var target in rs){
32884          var r = rs[target];
32885          if(typeof r != "function"){
32886             if(r.hasPanel(panelId)){
32887                return r.showPanel(panelId);
32888             }
32889          }
32890       }
32891       return null;
32892    },
32893
32894    /**
32895      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32896      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32897      */
32898    /*
32899     restoreState : function(provider){
32900         if(!provider){
32901             provider = Roo.state.Manager;
32902         }
32903         var sm = new Roo.LayoutStateManager();
32904         sm.init(this, provider);
32905     },
32906 */
32907  
32908  
32909     /**
32910      * Adds a xtype elements to the layout.
32911      * <pre><code>
32912
32913 layout.addxtype({
32914        xtype : 'ContentPanel',
32915        region: 'west',
32916        items: [ .... ]
32917    }
32918 );
32919
32920 layout.addxtype({
32921         xtype : 'NestedLayoutPanel',
32922         region: 'west',
32923         layout: {
32924            center: { },
32925            west: { }   
32926         },
32927         items : [ ... list of content panels or nested layout panels.. ]
32928    }
32929 );
32930 </code></pre>
32931      * @param {Object} cfg Xtype definition of item to add.
32932      */
32933     addxtype : function(cfg)
32934     {
32935         // basically accepts a pannel...
32936         // can accept a layout region..!?!?
32937         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32938         
32939         
32940         // theory?  children can only be panels??
32941         
32942         //if (!cfg.xtype.match(/Panel$/)) {
32943         //    return false;
32944         //}
32945         var ret = false;
32946         
32947         if (typeof(cfg.region) == 'undefined') {
32948             Roo.log("Failed to add Panel, region was not set");
32949             Roo.log(cfg);
32950             return false;
32951         }
32952         var region = cfg.region;
32953         delete cfg.region;
32954         
32955           
32956         var xitems = [];
32957         if (cfg.items) {
32958             xitems = cfg.items;
32959             delete cfg.items;
32960         }
32961         var nb = false;
32962         
32963         switch(cfg.xtype) 
32964         {
32965             case 'Content':  // ContentPanel (el, cfg)
32966             case 'Scroll':  // ContentPanel (el, cfg)
32967             case 'View': 
32968                 cfg.autoCreate = true;
32969                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32970                 //} else {
32971                 //    var el = this.el.createChild();
32972                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32973                 //}
32974                 
32975                 this.add(region, ret);
32976                 break;
32977             
32978             /*
32979             case 'TreePanel': // our new panel!
32980                 cfg.el = this.el.createChild();
32981                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32982                 this.add(region, ret);
32983                 break;
32984             */
32985             
32986             case 'Nest': 
32987                 // create a new Layout (which is  a Border Layout...
32988                 
32989                 var clayout = cfg.layout;
32990                 clayout.el  = this.el.createChild();
32991                 clayout.items   = clayout.items  || [];
32992                 
32993                 delete cfg.layout;
32994                 
32995                 // replace this exitems with the clayout ones..
32996                 xitems = clayout.items;
32997                  
32998                 // force background off if it's in center...
32999                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33000                     cfg.background = false;
33001                 }
33002                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33003                 
33004                 
33005                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33006                 //console.log('adding nested layout panel '  + cfg.toSource());
33007                 this.add(region, ret);
33008                 nb = {}; /// find first...
33009                 break;
33010             
33011             case 'Grid':
33012                 
33013                 // needs grid and region
33014                 
33015                 //var el = this.getRegion(region).el.createChild();
33016                 /*
33017                  *var el = this.el.createChild();
33018                 // create the grid first...
33019                 cfg.grid.container = el;
33020                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33021                 */
33022                 
33023                 if (region == 'center' && this.active ) {
33024                     cfg.background = false;
33025                 }
33026                 
33027                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33028                 
33029                 this.add(region, ret);
33030                 /*
33031                 if (cfg.background) {
33032                     // render grid on panel activation (if panel background)
33033                     ret.on('activate', function(gp) {
33034                         if (!gp.grid.rendered) {
33035                     //        gp.grid.render(el);
33036                         }
33037                     });
33038                 } else {
33039                   //  cfg.grid.render(el);
33040                 }
33041                 */
33042                 break;
33043            
33044            
33045             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33046                 // it was the old xcomponent building that caused this before.
33047                 // espeically if border is the top element in the tree.
33048                 ret = this;
33049                 break; 
33050                 
33051                     
33052                 
33053                 
33054                 
33055             default:
33056                 /*
33057                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33058                     
33059                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33060                     this.add(region, ret);
33061                 } else {
33062                 */
33063                     Roo.log(cfg);
33064                     throw "Can not add '" + cfg.xtype + "' to Border";
33065                     return null;
33066              
33067                                 
33068              
33069         }
33070         this.beginUpdate();
33071         // add children..
33072         var region = '';
33073         var abn = {};
33074         Roo.each(xitems, function(i)  {
33075             region = nb && i.region ? i.region : false;
33076             
33077             var add = ret.addxtype(i);
33078            
33079             if (region) {
33080                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33081                 if (!i.background) {
33082                     abn[region] = nb[region] ;
33083                 }
33084             }
33085             
33086         });
33087         this.endUpdate();
33088
33089         // make the last non-background panel active..
33090         //if (nb) { Roo.log(abn); }
33091         if (nb) {
33092             
33093             for(var r in abn) {
33094                 region = this.getRegion(r);
33095                 if (region) {
33096                     // tried using nb[r], but it does not work..
33097                      
33098                     region.showPanel(abn[r]);
33099                    
33100                 }
33101             }
33102         }
33103         return ret;
33104         
33105     },
33106     
33107     
33108 // private
33109     factory : function(cfg)
33110     {
33111         
33112         var validRegions = Roo.bootstrap.layout.Border.regions;
33113
33114         var target = cfg.region;
33115         cfg.mgr = this;
33116         
33117         var r = Roo.bootstrap.layout;
33118         Roo.log(target);
33119         switch(target){
33120             case "north":
33121                 return new r.North(cfg);
33122             case "south":
33123                 return new r.South(cfg);
33124             case "east":
33125                 return new r.East(cfg);
33126             case "west":
33127                 return new r.West(cfg);
33128             case "center":
33129                 return new r.Center(cfg);
33130         }
33131         throw 'Layout region "'+target+'" not supported.';
33132     }
33133     
33134     
33135 });
33136  /*
33137  * Based on:
33138  * Ext JS Library 1.1.1
33139  * Copyright(c) 2006-2007, Ext JS, LLC.
33140  *
33141  * Originally Released Under LGPL - original licence link has changed is not relivant.
33142  *
33143  * Fork - LGPL
33144  * <script type="text/javascript">
33145  */
33146  
33147 /**
33148  * @class Roo.bootstrap.layout.Basic
33149  * @extends Roo.util.Observable
33150  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33151  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33152  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33153  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33154  * @cfg {string}   region  the region that it inhabits..
33155  * @cfg {bool}   skipConfig skip config?
33156  * 
33157
33158  */
33159 Roo.bootstrap.layout.Basic = function(config){
33160     
33161     this.mgr = config.mgr;
33162     
33163     this.position = config.region;
33164     
33165     var skipConfig = config.skipConfig;
33166     
33167     this.events = {
33168         /**
33169          * @scope Roo.BasicLayoutRegion
33170          */
33171         
33172         /**
33173          * @event beforeremove
33174          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33175          * @param {Roo.LayoutRegion} this
33176          * @param {Roo.ContentPanel} panel The panel
33177          * @param {Object} e The cancel event object
33178          */
33179         "beforeremove" : true,
33180         /**
33181          * @event invalidated
33182          * Fires when the layout for this region is changed.
33183          * @param {Roo.LayoutRegion} this
33184          */
33185         "invalidated" : true,
33186         /**
33187          * @event visibilitychange
33188          * Fires when this region is shown or hidden 
33189          * @param {Roo.LayoutRegion} this
33190          * @param {Boolean} visibility true or false
33191          */
33192         "visibilitychange" : true,
33193         /**
33194          * @event paneladded
33195          * Fires when a panel is added. 
33196          * @param {Roo.LayoutRegion} this
33197          * @param {Roo.ContentPanel} panel The panel
33198          */
33199         "paneladded" : true,
33200         /**
33201          * @event panelremoved
33202          * Fires when a panel is removed. 
33203          * @param {Roo.LayoutRegion} this
33204          * @param {Roo.ContentPanel} panel The panel
33205          */
33206         "panelremoved" : true,
33207         /**
33208          * @event beforecollapse
33209          * Fires when this region before collapse.
33210          * @param {Roo.LayoutRegion} this
33211          */
33212         "beforecollapse" : true,
33213         /**
33214          * @event collapsed
33215          * Fires when this region is collapsed.
33216          * @param {Roo.LayoutRegion} this
33217          */
33218         "collapsed" : true,
33219         /**
33220          * @event expanded
33221          * Fires when this region is expanded.
33222          * @param {Roo.LayoutRegion} this
33223          */
33224         "expanded" : true,
33225         /**
33226          * @event slideshow
33227          * Fires when this region is slid into view.
33228          * @param {Roo.LayoutRegion} this
33229          */
33230         "slideshow" : true,
33231         /**
33232          * @event slidehide
33233          * Fires when this region slides out of view. 
33234          * @param {Roo.LayoutRegion} this
33235          */
33236         "slidehide" : true,
33237         /**
33238          * @event panelactivated
33239          * Fires when a panel is activated. 
33240          * @param {Roo.LayoutRegion} this
33241          * @param {Roo.ContentPanel} panel The activated panel
33242          */
33243         "panelactivated" : true,
33244         /**
33245          * @event resized
33246          * Fires when the user resizes this region. 
33247          * @param {Roo.LayoutRegion} this
33248          * @param {Number} newSize The new size (width for east/west, height for north/south)
33249          */
33250         "resized" : true
33251     };
33252     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33253     this.panels = new Roo.util.MixedCollection();
33254     this.panels.getKey = this.getPanelId.createDelegate(this);
33255     this.box = null;
33256     this.activePanel = null;
33257     // ensure listeners are added...
33258     
33259     if (config.listeners || config.events) {
33260         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33261             listeners : config.listeners || {},
33262             events : config.events || {}
33263         });
33264     }
33265     
33266     if(skipConfig !== true){
33267         this.applyConfig(config);
33268     }
33269 };
33270
33271 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33272 {
33273     getPanelId : function(p){
33274         return p.getId();
33275     },
33276     
33277     applyConfig : function(config){
33278         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33279         this.config = config;
33280         
33281     },
33282     
33283     /**
33284      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33285      * the width, for horizontal (north, south) the height.
33286      * @param {Number} newSize The new width or height
33287      */
33288     resizeTo : function(newSize){
33289         var el = this.el ? this.el :
33290                  (this.activePanel ? this.activePanel.getEl() : null);
33291         if(el){
33292             switch(this.position){
33293                 case "east":
33294                 case "west":
33295                     el.setWidth(newSize);
33296                     this.fireEvent("resized", this, newSize);
33297                 break;
33298                 case "north":
33299                 case "south":
33300                     el.setHeight(newSize);
33301                     this.fireEvent("resized", this, newSize);
33302                 break;                
33303             }
33304         }
33305     },
33306     
33307     getBox : function(){
33308         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33309     },
33310     
33311     getMargins : function(){
33312         return this.margins;
33313     },
33314     
33315     updateBox : function(box){
33316         this.box = box;
33317         var el = this.activePanel.getEl();
33318         el.dom.style.left = box.x + "px";
33319         el.dom.style.top = box.y + "px";
33320         this.activePanel.setSize(box.width, box.height);
33321     },
33322     
33323     /**
33324      * Returns the container element for this region.
33325      * @return {Roo.Element}
33326      */
33327     getEl : function(){
33328         return this.activePanel;
33329     },
33330     
33331     /**
33332      * Returns true if this region is currently visible.
33333      * @return {Boolean}
33334      */
33335     isVisible : function(){
33336         return this.activePanel ? true : false;
33337     },
33338     
33339     setActivePanel : function(panel){
33340         panel = this.getPanel(panel);
33341         if(this.activePanel && this.activePanel != panel){
33342             this.activePanel.setActiveState(false);
33343             this.activePanel.getEl().setLeftTop(-10000,-10000);
33344         }
33345         this.activePanel = panel;
33346         panel.setActiveState(true);
33347         if(this.box){
33348             panel.setSize(this.box.width, this.box.height);
33349         }
33350         this.fireEvent("panelactivated", this, panel);
33351         this.fireEvent("invalidated");
33352     },
33353     
33354     /**
33355      * Show the specified panel.
33356      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33357      * @return {Roo.ContentPanel} The shown panel or null
33358      */
33359     showPanel : function(panel){
33360         panel = this.getPanel(panel);
33361         if(panel){
33362             this.setActivePanel(panel);
33363         }
33364         return panel;
33365     },
33366     
33367     /**
33368      * Get the active panel for this region.
33369      * @return {Roo.ContentPanel} The active panel or null
33370      */
33371     getActivePanel : function(){
33372         return this.activePanel;
33373     },
33374     
33375     /**
33376      * Add the passed ContentPanel(s)
33377      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33378      * @return {Roo.ContentPanel} The panel added (if only one was added)
33379      */
33380     add : function(panel){
33381         if(arguments.length > 1){
33382             for(var i = 0, len = arguments.length; i < len; i++) {
33383                 this.add(arguments[i]);
33384             }
33385             return null;
33386         }
33387         if(this.hasPanel(panel)){
33388             this.showPanel(panel);
33389             return panel;
33390         }
33391         var el = panel.getEl();
33392         if(el.dom.parentNode != this.mgr.el.dom){
33393             this.mgr.el.dom.appendChild(el.dom);
33394         }
33395         if(panel.setRegion){
33396             panel.setRegion(this);
33397         }
33398         this.panels.add(panel);
33399         el.setStyle("position", "absolute");
33400         if(!panel.background){
33401             this.setActivePanel(panel);
33402             if(this.config.initialSize && this.panels.getCount()==1){
33403                 this.resizeTo(this.config.initialSize);
33404             }
33405         }
33406         this.fireEvent("paneladded", this, panel);
33407         return panel;
33408     },
33409     
33410     /**
33411      * Returns true if the panel is in this region.
33412      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33413      * @return {Boolean}
33414      */
33415     hasPanel : function(panel){
33416         if(typeof panel == "object"){ // must be panel obj
33417             panel = panel.getId();
33418         }
33419         return this.getPanel(panel) ? true : false;
33420     },
33421     
33422     /**
33423      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33424      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33425      * @param {Boolean} preservePanel Overrides the config preservePanel option
33426      * @return {Roo.ContentPanel} The panel that was removed
33427      */
33428     remove : function(panel, preservePanel){
33429         panel = this.getPanel(panel);
33430         if(!panel){
33431             return null;
33432         }
33433         var e = {};
33434         this.fireEvent("beforeremove", this, panel, e);
33435         if(e.cancel === true){
33436             return null;
33437         }
33438         var panelId = panel.getId();
33439         this.panels.removeKey(panelId);
33440         return panel;
33441     },
33442     
33443     /**
33444      * Returns the panel specified or null if it's not in this region.
33445      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33446      * @return {Roo.ContentPanel}
33447      */
33448     getPanel : function(id){
33449         if(typeof id == "object"){ // must be panel obj
33450             return id;
33451         }
33452         return this.panels.get(id);
33453     },
33454     
33455     /**
33456      * Returns this regions position (north/south/east/west/center).
33457      * @return {String} 
33458      */
33459     getPosition: function(){
33460         return this.position;    
33461     }
33462 });/*
33463  * Based on:
33464  * Ext JS Library 1.1.1
33465  * Copyright(c) 2006-2007, Ext JS, LLC.
33466  *
33467  * Originally Released Under LGPL - original licence link has changed is not relivant.
33468  *
33469  * Fork - LGPL
33470  * <script type="text/javascript">
33471  */
33472  
33473 /**
33474  * @class Roo.bootstrap.layout.Region
33475  * @extends Roo.bootstrap.layout.Basic
33476  * This class represents a region in a layout manager.
33477  
33478  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33479  * @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})
33480  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33481  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33482  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33483  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33484  * @cfg {String}    title           The title for the region (overrides panel titles)
33485  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33486  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33487  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33488  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33489  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33490  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33491  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33492  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33493  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33494  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33495
33496  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33497  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33498  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33499  * @cfg {Number}    width           For East/West panels
33500  * @cfg {Number}    height          For North/South panels
33501  * @cfg {Boolean}   split           To show the splitter
33502  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33503  * 
33504  * @cfg {string}   cls             Extra CSS classes to add to region
33505  * 
33506  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33507  * @cfg {string}   region  the region that it inhabits..
33508  *
33509
33510  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33511  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33512
33513  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33514  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33515  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33516  */
33517 Roo.bootstrap.layout.Region = function(config)
33518 {
33519     this.applyConfig(config);
33520
33521     var mgr = config.mgr;
33522     var pos = config.region;
33523     config.skipConfig = true;
33524     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33525     
33526     if (mgr.el) {
33527         this.onRender(mgr.el);   
33528     }
33529      
33530     this.visible = true;
33531     this.collapsed = false;
33532     this.unrendered_panels = [];
33533 };
33534
33535 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33536
33537     position: '', // set by wrapper (eg. north/south etc..)
33538     unrendered_panels : null,  // unrendered panels.
33539     createBody : function(){
33540         /** This region's body element 
33541         * @type Roo.Element */
33542         this.bodyEl = this.el.createChild({
33543                 tag: "div",
33544                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33545         });
33546     },
33547
33548     onRender: function(ctr, pos)
33549     {
33550         var dh = Roo.DomHelper;
33551         /** This region's container element 
33552         * @type Roo.Element */
33553         this.el = dh.append(ctr.dom, {
33554                 tag: "div",
33555                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33556             }, true);
33557         /** This region's title element 
33558         * @type Roo.Element */
33559     
33560         this.titleEl = dh.append(this.el.dom,
33561             {
33562                     tag: "div",
33563                     unselectable: "on",
33564                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33565                     children:[
33566                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33567                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33568                     ]}, true);
33569         
33570         this.titleEl.enableDisplayMode();
33571         /** This region's title text element 
33572         * @type HTMLElement */
33573         this.titleTextEl = this.titleEl.dom.firstChild;
33574         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33575         /*
33576         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33577         this.closeBtn.enableDisplayMode();
33578         this.closeBtn.on("click", this.closeClicked, this);
33579         this.closeBtn.hide();
33580     */
33581         this.createBody(this.config);
33582         if(this.config.hideWhenEmpty){
33583             this.hide();
33584             this.on("paneladded", this.validateVisibility, this);
33585             this.on("panelremoved", this.validateVisibility, this);
33586         }
33587         if(this.autoScroll){
33588             this.bodyEl.setStyle("overflow", "auto");
33589         }else{
33590             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33591         }
33592         //if(c.titlebar !== false){
33593             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33594                 this.titleEl.hide();
33595             }else{
33596                 this.titleEl.show();
33597                 if(this.config.title){
33598                     this.titleTextEl.innerHTML = this.config.title;
33599                 }
33600             }
33601         //}
33602         if(this.config.collapsed){
33603             this.collapse(true);
33604         }
33605         if(this.config.hidden){
33606             this.hide();
33607         }
33608         
33609         if (this.unrendered_panels && this.unrendered_panels.length) {
33610             for (var i =0;i< this.unrendered_panels.length; i++) {
33611                 this.add(this.unrendered_panels[i]);
33612             }
33613             this.unrendered_panels = null;
33614             
33615         }
33616         
33617     },
33618     
33619     applyConfig : function(c)
33620     {
33621         /*
33622          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33623             var dh = Roo.DomHelper;
33624             if(c.titlebar !== false){
33625                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33626                 this.collapseBtn.on("click", this.collapse, this);
33627                 this.collapseBtn.enableDisplayMode();
33628                 /*
33629                 if(c.showPin === true || this.showPin){
33630                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33631                     this.stickBtn.enableDisplayMode();
33632                     this.stickBtn.on("click", this.expand, this);
33633                     this.stickBtn.hide();
33634                 }
33635                 
33636             }
33637             */
33638             /** This region's collapsed element
33639             * @type Roo.Element */
33640             /*
33641              *
33642             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33643                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33644             ]}, true);
33645             
33646             if(c.floatable !== false){
33647                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33648                this.collapsedEl.on("click", this.collapseClick, this);
33649             }
33650
33651             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33652                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33653                    id: "message", unselectable: "on", style:{"float":"left"}});
33654                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33655              }
33656             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33657             this.expandBtn.on("click", this.expand, this);
33658             
33659         }
33660         
33661         if(this.collapseBtn){
33662             this.collapseBtn.setVisible(c.collapsible == true);
33663         }
33664         
33665         this.cmargins = c.cmargins || this.cmargins ||
33666                          (this.position == "west" || this.position == "east" ?
33667                              {top: 0, left: 2, right:2, bottom: 0} :
33668                              {top: 2, left: 0, right:0, bottom: 2});
33669         */
33670         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33671         
33672         
33673         this.bottomTabs = c.tabPosition != "top";
33674         
33675         this.autoScroll = c.autoScroll || false;
33676         
33677         
33678        
33679         
33680         this.duration = c.duration || .30;
33681         this.slideDuration = c.slideDuration || .45;
33682         this.config = c;
33683        
33684     },
33685     /**
33686      * Returns true if this region is currently visible.
33687      * @return {Boolean}
33688      */
33689     isVisible : function(){
33690         return this.visible;
33691     },
33692
33693     /**
33694      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33695      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33696      */
33697     //setCollapsedTitle : function(title){
33698     //    title = title || "&#160;";
33699      //   if(this.collapsedTitleTextEl){
33700       //      this.collapsedTitleTextEl.innerHTML = title;
33701        // }
33702     //},
33703
33704     getBox : function(){
33705         var b;
33706       //  if(!this.collapsed){
33707             b = this.el.getBox(false, true);
33708        // }else{
33709           //  b = this.collapsedEl.getBox(false, true);
33710         //}
33711         return b;
33712     },
33713
33714     getMargins : function(){
33715         return this.margins;
33716         //return this.collapsed ? this.cmargins : this.margins;
33717     },
33718 /*
33719     highlight : function(){
33720         this.el.addClass("x-layout-panel-dragover");
33721     },
33722
33723     unhighlight : function(){
33724         this.el.removeClass("x-layout-panel-dragover");
33725     },
33726 */
33727     updateBox : function(box)
33728     {
33729         if (!this.bodyEl) {
33730             return; // not rendered yet..
33731         }
33732         
33733         this.box = box;
33734         if(!this.collapsed){
33735             this.el.dom.style.left = box.x + "px";
33736             this.el.dom.style.top = box.y + "px";
33737             this.updateBody(box.width, box.height);
33738         }else{
33739             this.collapsedEl.dom.style.left = box.x + "px";
33740             this.collapsedEl.dom.style.top = box.y + "px";
33741             this.collapsedEl.setSize(box.width, box.height);
33742         }
33743         if(this.tabs){
33744             this.tabs.autoSizeTabs();
33745         }
33746     },
33747
33748     updateBody : function(w, h)
33749     {
33750         if(w !== null){
33751             this.el.setWidth(w);
33752             w -= this.el.getBorderWidth("rl");
33753             if(this.config.adjustments){
33754                 w += this.config.adjustments[0];
33755             }
33756         }
33757         if(h !== null && h > 0){
33758             this.el.setHeight(h);
33759             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33760             h -= this.el.getBorderWidth("tb");
33761             if(this.config.adjustments){
33762                 h += this.config.adjustments[1];
33763             }
33764             this.bodyEl.setHeight(h);
33765             if(this.tabs){
33766                 h = this.tabs.syncHeight(h);
33767             }
33768         }
33769         if(this.panelSize){
33770             w = w !== null ? w : this.panelSize.width;
33771             h = h !== null ? h : this.panelSize.height;
33772         }
33773         if(this.activePanel){
33774             var el = this.activePanel.getEl();
33775             w = w !== null ? w : el.getWidth();
33776             h = h !== null ? h : el.getHeight();
33777             this.panelSize = {width: w, height: h};
33778             this.activePanel.setSize(w, h);
33779         }
33780         if(Roo.isIE && this.tabs){
33781             this.tabs.el.repaint();
33782         }
33783     },
33784
33785     /**
33786      * Returns the container element for this region.
33787      * @return {Roo.Element}
33788      */
33789     getEl : function(){
33790         return this.el;
33791     },
33792
33793     /**
33794      * Hides this region.
33795      */
33796     hide : function(){
33797         //if(!this.collapsed){
33798             this.el.dom.style.left = "-2000px";
33799             this.el.hide();
33800         //}else{
33801          //   this.collapsedEl.dom.style.left = "-2000px";
33802          //   this.collapsedEl.hide();
33803        // }
33804         this.visible = false;
33805         this.fireEvent("visibilitychange", this, false);
33806     },
33807
33808     /**
33809      * Shows this region if it was previously hidden.
33810      */
33811     show : function(){
33812         //if(!this.collapsed){
33813             this.el.show();
33814         //}else{
33815         //    this.collapsedEl.show();
33816        // }
33817         this.visible = true;
33818         this.fireEvent("visibilitychange", this, true);
33819     },
33820 /*
33821     closeClicked : function(){
33822         if(this.activePanel){
33823             this.remove(this.activePanel);
33824         }
33825     },
33826
33827     collapseClick : function(e){
33828         if(this.isSlid){
33829            e.stopPropagation();
33830            this.slideIn();
33831         }else{
33832            e.stopPropagation();
33833            this.slideOut();
33834         }
33835     },
33836 */
33837     /**
33838      * Collapses this region.
33839      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33840      */
33841     /*
33842     collapse : function(skipAnim, skipCheck = false){
33843         if(this.collapsed) {
33844             return;
33845         }
33846         
33847         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33848             
33849             this.collapsed = true;
33850             if(this.split){
33851                 this.split.el.hide();
33852             }
33853             if(this.config.animate && skipAnim !== true){
33854                 this.fireEvent("invalidated", this);
33855                 this.animateCollapse();
33856             }else{
33857                 this.el.setLocation(-20000,-20000);
33858                 this.el.hide();
33859                 this.collapsedEl.show();
33860                 this.fireEvent("collapsed", this);
33861                 this.fireEvent("invalidated", this);
33862             }
33863         }
33864         
33865     },
33866 */
33867     animateCollapse : function(){
33868         // overridden
33869     },
33870
33871     /**
33872      * Expands this region if it was previously collapsed.
33873      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33874      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33875      */
33876     /*
33877     expand : function(e, skipAnim){
33878         if(e) {
33879             e.stopPropagation();
33880         }
33881         if(!this.collapsed || this.el.hasActiveFx()) {
33882             return;
33883         }
33884         if(this.isSlid){
33885             this.afterSlideIn();
33886             skipAnim = true;
33887         }
33888         this.collapsed = false;
33889         if(this.config.animate && skipAnim !== true){
33890             this.animateExpand();
33891         }else{
33892             this.el.show();
33893             if(this.split){
33894                 this.split.el.show();
33895             }
33896             this.collapsedEl.setLocation(-2000,-2000);
33897             this.collapsedEl.hide();
33898             this.fireEvent("invalidated", this);
33899             this.fireEvent("expanded", this);
33900         }
33901     },
33902 */
33903     animateExpand : function(){
33904         // overridden
33905     },
33906
33907     initTabs : function()
33908     {
33909         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33910         
33911         var ts = new Roo.bootstrap.panel.Tabs({
33912                 el: this.bodyEl.dom,
33913                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33914                 disableTooltips: this.config.disableTabTips,
33915                 toolbar : this.config.toolbar
33916             });
33917         
33918         if(this.config.hideTabs){
33919             ts.stripWrap.setDisplayed(false);
33920         }
33921         this.tabs = ts;
33922         ts.resizeTabs = this.config.resizeTabs === true;
33923         ts.minTabWidth = this.config.minTabWidth || 40;
33924         ts.maxTabWidth = this.config.maxTabWidth || 250;
33925         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33926         ts.monitorResize = false;
33927         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33928         ts.bodyEl.addClass('roo-layout-tabs-body');
33929         this.panels.each(this.initPanelAsTab, this);
33930     },
33931
33932     initPanelAsTab : function(panel){
33933         var ti = this.tabs.addTab(
33934             panel.getEl().id,
33935             panel.getTitle(),
33936             null,
33937             this.config.closeOnTab && panel.isClosable(),
33938             panel.tpl
33939         );
33940         if(panel.tabTip !== undefined){
33941             ti.setTooltip(panel.tabTip);
33942         }
33943         ti.on("activate", function(){
33944               this.setActivePanel(panel);
33945         }, this);
33946         
33947         if(this.config.closeOnTab){
33948             ti.on("beforeclose", function(t, e){
33949                 e.cancel = true;
33950                 this.remove(panel);
33951             }, this);
33952         }
33953         
33954         panel.tabItem = ti;
33955         
33956         return ti;
33957     },
33958
33959     updatePanelTitle : function(panel, title)
33960     {
33961         if(this.activePanel == panel){
33962             this.updateTitle(title);
33963         }
33964         if(this.tabs){
33965             var ti = this.tabs.getTab(panel.getEl().id);
33966             ti.setText(title);
33967             if(panel.tabTip !== undefined){
33968                 ti.setTooltip(panel.tabTip);
33969             }
33970         }
33971     },
33972
33973     updateTitle : function(title){
33974         if(this.titleTextEl && !this.config.title){
33975             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33976         }
33977     },
33978
33979     setActivePanel : function(panel)
33980     {
33981         panel = this.getPanel(panel);
33982         if(this.activePanel && this.activePanel != panel){
33983             this.activePanel.setActiveState(false);
33984         }
33985         this.activePanel = panel;
33986         panel.setActiveState(true);
33987         if(this.panelSize){
33988             panel.setSize(this.panelSize.width, this.panelSize.height);
33989         }
33990         if(this.closeBtn){
33991             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33992         }
33993         this.updateTitle(panel.getTitle());
33994         if(this.tabs){
33995             this.fireEvent("invalidated", this);
33996         }
33997         this.fireEvent("panelactivated", this, panel);
33998     },
33999
34000     /**
34001      * Shows the specified panel.
34002      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34003      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34004      */
34005     showPanel : function(panel)
34006     {
34007         panel = this.getPanel(panel);
34008         if(panel){
34009             if(this.tabs){
34010                 var tab = this.tabs.getTab(panel.getEl().id);
34011                 if(tab.isHidden()){
34012                     this.tabs.unhideTab(tab.id);
34013                 }
34014                 tab.activate();
34015             }else{
34016                 this.setActivePanel(panel);
34017             }
34018         }
34019         return panel;
34020     },
34021
34022     /**
34023      * Get the active panel for this region.
34024      * @return {Roo.ContentPanel} The active panel or null
34025      */
34026     getActivePanel : function(){
34027         return this.activePanel;
34028     },
34029
34030     validateVisibility : function(){
34031         if(this.panels.getCount() < 1){
34032             this.updateTitle("&#160;");
34033             this.closeBtn.hide();
34034             this.hide();
34035         }else{
34036             if(!this.isVisible()){
34037                 this.show();
34038             }
34039         }
34040     },
34041
34042     /**
34043      * Adds the passed ContentPanel(s) to this region.
34044      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34045      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34046      */
34047     add : function(panel)
34048     {
34049         if(arguments.length > 1){
34050             for(var i = 0, len = arguments.length; i < len; i++) {
34051                 this.add(arguments[i]);
34052             }
34053             return null;
34054         }
34055         
34056         // if we have not been rendered yet, then we can not really do much of this..
34057         if (!this.bodyEl) {
34058             this.unrendered_panels.push(panel);
34059             return panel;
34060         }
34061         
34062         
34063         
34064         
34065         if(this.hasPanel(panel)){
34066             this.showPanel(panel);
34067             return panel;
34068         }
34069         panel.setRegion(this);
34070         this.panels.add(panel);
34071        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34072             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34073             // and hide them... ???
34074             this.bodyEl.dom.appendChild(panel.getEl().dom);
34075             if(panel.background !== true){
34076                 this.setActivePanel(panel);
34077             }
34078             this.fireEvent("paneladded", this, panel);
34079             return panel;
34080         }
34081         */
34082         if(!this.tabs){
34083             this.initTabs();
34084         }else{
34085             this.initPanelAsTab(panel);
34086         }
34087         
34088         
34089         if(panel.background !== true){
34090             this.tabs.activate(panel.getEl().id);
34091         }
34092         this.fireEvent("paneladded", this, panel);
34093         return panel;
34094     },
34095
34096     /**
34097      * Hides the tab for the specified panel.
34098      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34099      */
34100     hidePanel : function(panel){
34101         if(this.tabs && (panel = this.getPanel(panel))){
34102             this.tabs.hideTab(panel.getEl().id);
34103         }
34104     },
34105
34106     /**
34107      * Unhides the tab for a previously hidden panel.
34108      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34109      */
34110     unhidePanel : function(panel){
34111         if(this.tabs && (panel = this.getPanel(panel))){
34112             this.tabs.unhideTab(panel.getEl().id);
34113         }
34114     },
34115
34116     clearPanels : function(){
34117         while(this.panels.getCount() > 0){
34118              this.remove(this.panels.first());
34119         }
34120     },
34121
34122     /**
34123      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34124      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34125      * @param {Boolean} preservePanel Overrides the config preservePanel option
34126      * @return {Roo.ContentPanel} The panel that was removed
34127      */
34128     remove : function(panel, preservePanel)
34129     {
34130         panel = this.getPanel(panel);
34131         if(!panel){
34132             return null;
34133         }
34134         var e = {};
34135         this.fireEvent("beforeremove", this, panel, e);
34136         if(e.cancel === true){
34137             return null;
34138         }
34139         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34140         var panelId = panel.getId();
34141         this.panels.removeKey(panelId);
34142         if(preservePanel){
34143             document.body.appendChild(panel.getEl().dom);
34144         }
34145         if(this.tabs){
34146             this.tabs.removeTab(panel.getEl().id);
34147         }else if (!preservePanel){
34148             this.bodyEl.dom.removeChild(panel.getEl().dom);
34149         }
34150         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34151             var p = this.panels.first();
34152             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34153             tempEl.appendChild(p.getEl().dom);
34154             this.bodyEl.update("");
34155             this.bodyEl.dom.appendChild(p.getEl().dom);
34156             tempEl = null;
34157             this.updateTitle(p.getTitle());
34158             this.tabs = null;
34159             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34160             this.setActivePanel(p);
34161         }
34162         panel.setRegion(null);
34163         if(this.activePanel == panel){
34164             this.activePanel = null;
34165         }
34166         if(this.config.autoDestroy !== false && preservePanel !== true){
34167             try{panel.destroy();}catch(e){}
34168         }
34169         this.fireEvent("panelremoved", this, panel);
34170         return panel;
34171     },
34172
34173     /**
34174      * Returns the TabPanel component used by this region
34175      * @return {Roo.TabPanel}
34176      */
34177     getTabs : function(){
34178         return this.tabs;
34179     },
34180
34181     createTool : function(parentEl, className){
34182         var btn = Roo.DomHelper.append(parentEl, {
34183             tag: "div",
34184             cls: "x-layout-tools-button",
34185             children: [ {
34186                 tag: "div",
34187                 cls: "roo-layout-tools-button-inner " + className,
34188                 html: "&#160;"
34189             }]
34190         }, true);
34191         btn.addClassOnOver("roo-layout-tools-button-over");
34192         return btn;
34193     }
34194 });/*
34195  * Based on:
34196  * Ext JS Library 1.1.1
34197  * Copyright(c) 2006-2007, Ext JS, LLC.
34198  *
34199  * Originally Released Under LGPL - original licence link has changed is not relivant.
34200  *
34201  * Fork - LGPL
34202  * <script type="text/javascript">
34203  */
34204  
34205
34206
34207 /**
34208  * @class Roo.SplitLayoutRegion
34209  * @extends Roo.LayoutRegion
34210  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34211  */
34212 Roo.bootstrap.layout.Split = function(config){
34213     this.cursor = config.cursor;
34214     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34215 };
34216
34217 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34218 {
34219     splitTip : "Drag to resize.",
34220     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34221     useSplitTips : false,
34222
34223     applyConfig : function(config){
34224         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34225     },
34226     
34227     onRender : function(ctr,pos) {
34228         
34229         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34230         if(!this.config.split){
34231             return;
34232         }
34233         if(!this.split){
34234             
34235             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34236                             tag: "div",
34237                             id: this.el.id + "-split",
34238                             cls: "roo-layout-split roo-layout-split-"+this.position,
34239                             html: "&#160;"
34240             });
34241             /** The SplitBar for this region 
34242             * @type Roo.SplitBar */
34243             // does not exist yet...
34244             Roo.log([this.position, this.orientation]);
34245             
34246             this.split = new Roo.bootstrap.SplitBar({
34247                 dragElement : splitEl,
34248                 resizingElement: this.el,
34249                 orientation : this.orientation
34250             });
34251             
34252             this.split.on("moved", this.onSplitMove, this);
34253             this.split.useShim = this.config.useShim === true;
34254             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34255             if(this.useSplitTips){
34256                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34257             }
34258             //if(config.collapsible){
34259             //    this.split.el.on("dblclick", this.collapse,  this);
34260             //}
34261         }
34262         if(typeof this.config.minSize != "undefined"){
34263             this.split.minSize = this.config.minSize;
34264         }
34265         if(typeof this.config.maxSize != "undefined"){
34266             this.split.maxSize = this.config.maxSize;
34267         }
34268         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34269             this.hideSplitter();
34270         }
34271         
34272     },
34273
34274     getHMaxSize : function(){
34275          var cmax = this.config.maxSize || 10000;
34276          var center = this.mgr.getRegion("center");
34277          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34278     },
34279
34280     getVMaxSize : function(){
34281          var cmax = this.config.maxSize || 10000;
34282          var center = this.mgr.getRegion("center");
34283          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34284     },
34285
34286     onSplitMove : function(split, newSize){
34287         this.fireEvent("resized", this, newSize);
34288     },
34289     
34290     /** 
34291      * Returns the {@link Roo.SplitBar} for this region.
34292      * @return {Roo.SplitBar}
34293      */
34294     getSplitBar : function(){
34295         return this.split;
34296     },
34297     
34298     hide : function(){
34299         this.hideSplitter();
34300         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34301     },
34302
34303     hideSplitter : function(){
34304         if(this.split){
34305             this.split.el.setLocation(-2000,-2000);
34306             this.split.el.hide();
34307         }
34308     },
34309
34310     show : function(){
34311         if(this.split){
34312             this.split.el.show();
34313         }
34314         Roo.bootstrap.layout.Split.superclass.show.call(this);
34315     },
34316     
34317     beforeSlide: function(){
34318         if(Roo.isGecko){// firefox overflow auto bug workaround
34319             this.bodyEl.clip();
34320             if(this.tabs) {
34321                 this.tabs.bodyEl.clip();
34322             }
34323             if(this.activePanel){
34324                 this.activePanel.getEl().clip();
34325                 
34326                 if(this.activePanel.beforeSlide){
34327                     this.activePanel.beforeSlide();
34328                 }
34329             }
34330         }
34331     },
34332     
34333     afterSlide : function(){
34334         if(Roo.isGecko){// firefox overflow auto bug workaround
34335             this.bodyEl.unclip();
34336             if(this.tabs) {
34337                 this.tabs.bodyEl.unclip();
34338             }
34339             if(this.activePanel){
34340                 this.activePanel.getEl().unclip();
34341                 if(this.activePanel.afterSlide){
34342                     this.activePanel.afterSlide();
34343                 }
34344             }
34345         }
34346     },
34347
34348     initAutoHide : function(){
34349         if(this.autoHide !== false){
34350             if(!this.autoHideHd){
34351                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34352                 this.autoHideHd = {
34353                     "mouseout": function(e){
34354                         if(!e.within(this.el, true)){
34355                             st.delay(500);
34356                         }
34357                     },
34358                     "mouseover" : function(e){
34359                         st.cancel();
34360                     },
34361                     scope : this
34362                 };
34363             }
34364             this.el.on(this.autoHideHd);
34365         }
34366     },
34367
34368     clearAutoHide : function(){
34369         if(this.autoHide !== false){
34370             this.el.un("mouseout", this.autoHideHd.mouseout);
34371             this.el.un("mouseover", this.autoHideHd.mouseover);
34372         }
34373     },
34374
34375     clearMonitor : function(){
34376         Roo.get(document).un("click", this.slideInIf, this);
34377     },
34378
34379     // these names are backwards but not changed for compat
34380     slideOut : function(){
34381         if(this.isSlid || this.el.hasActiveFx()){
34382             return;
34383         }
34384         this.isSlid = true;
34385         if(this.collapseBtn){
34386             this.collapseBtn.hide();
34387         }
34388         this.closeBtnState = this.closeBtn.getStyle('display');
34389         this.closeBtn.hide();
34390         if(this.stickBtn){
34391             this.stickBtn.show();
34392         }
34393         this.el.show();
34394         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34395         this.beforeSlide();
34396         this.el.setStyle("z-index", 10001);
34397         this.el.slideIn(this.getSlideAnchor(), {
34398             callback: function(){
34399                 this.afterSlide();
34400                 this.initAutoHide();
34401                 Roo.get(document).on("click", this.slideInIf, this);
34402                 this.fireEvent("slideshow", this);
34403             },
34404             scope: this,
34405             block: true
34406         });
34407     },
34408
34409     afterSlideIn : function(){
34410         this.clearAutoHide();
34411         this.isSlid = false;
34412         this.clearMonitor();
34413         this.el.setStyle("z-index", "");
34414         if(this.collapseBtn){
34415             this.collapseBtn.show();
34416         }
34417         this.closeBtn.setStyle('display', this.closeBtnState);
34418         if(this.stickBtn){
34419             this.stickBtn.hide();
34420         }
34421         this.fireEvent("slidehide", this);
34422     },
34423
34424     slideIn : function(cb){
34425         if(!this.isSlid || this.el.hasActiveFx()){
34426             Roo.callback(cb);
34427             return;
34428         }
34429         this.isSlid = false;
34430         this.beforeSlide();
34431         this.el.slideOut(this.getSlideAnchor(), {
34432             callback: function(){
34433                 this.el.setLeftTop(-10000, -10000);
34434                 this.afterSlide();
34435                 this.afterSlideIn();
34436                 Roo.callback(cb);
34437             },
34438             scope: this,
34439             block: true
34440         });
34441     },
34442     
34443     slideInIf : function(e){
34444         if(!e.within(this.el)){
34445             this.slideIn();
34446         }
34447     },
34448
34449     animateCollapse : function(){
34450         this.beforeSlide();
34451         this.el.setStyle("z-index", 20000);
34452         var anchor = this.getSlideAnchor();
34453         this.el.slideOut(anchor, {
34454             callback : function(){
34455                 this.el.setStyle("z-index", "");
34456                 this.collapsedEl.slideIn(anchor, {duration:.3});
34457                 this.afterSlide();
34458                 this.el.setLocation(-10000,-10000);
34459                 this.el.hide();
34460                 this.fireEvent("collapsed", this);
34461             },
34462             scope: this,
34463             block: true
34464         });
34465     },
34466
34467     animateExpand : function(){
34468         this.beforeSlide();
34469         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34470         this.el.setStyle("z-index", 20000);
34471         this.collapsedEl.hide({
34472             duration:.1
34473         });
34474         this.el.slideIn(this.getSlideAnchor(), {
34475             callback : function(){
34476                 this.el.setStyle("z-index", "");
34477                 this.afterSlide();
34478                 if(this.split){
34479                     this.split.el.show();
34480                 }
34481                 this.fireEvent("invalidated", this);
34482                 this.fireEvent("expanded", this);
34483             },
34484             scope: this,
34485             block: true
34486         });
34487     },
34488
34489     anchors : {
34490         "west" : "left",
34491         "east" : "right",
34492         "north" : "top",
34493         "south" : "bottom"
34494     },
34495
34496     sanchors : {
34497         "west" : "l",
34498         "east" : "r",
34499         "north" : "t",
34500         "south" : "b"
34501     },
34502
34503     canchors : {
34504         "west" : "tl-tr",
34505         "east" : "tr-tl",
34506         "north" : "tl-bl",
34507         "south" : "bl-tl"
34508     },
34509
34510     getAnchor : function(){
34511         return this.anchors[this.position];
34512     },
34513
34514     getCollapseAnchor : function(){
34515         return this.canchors[this.position];
34516     },
34517
34518     getSlideAnchor : function(){
34519         return this.sanchors[this.position];
34520     },
34521
34522     getAlignAdj : function(){
34523         var cm = this.cmargins;
34524         switch(this.position){
34525             case "west":
34526                 return [0, 0];
34527             break;
34528             case "east":
34529                 return [0, 0];
34530             break;
34531             case "north":
34532                 return [0, 0];
34533             break;
34534             case "south":
34535                 return [0, 0];
34536             break;
34537         }
34538     },
34539
34540     getExpandAdj : function(){
34541         var c = this.collapsedEl, cm = this.cmargins;
34542         switch(this.position){
34543             case "west":
34544                 return [-(cm.right+c.getWidth()+cm.left), 0];
34545             break;
34546             case "east":
34547                 return [cm.right+c.getWidth()+cm.left, 0];
34548             break;
34549             case "north":
34550                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34551             break;
34552             case "south":
34553                 return [0, cm.top+cm.bottom+c.getHeight()];
34554             break;
34555         }
34556     }
34557 });/*
34558  * Based on:
34559  * Ext JS Library 1.1.1
34560  * Copyright(c) 2006-2007, Ext JS, LLC.
34561  *
34562  * Originally Released Under LGPL - original licence link has changed is not relivant.
34563  *
34564  * Fork - LGPL
34565  * <script type="text/javascript">
34566  */
34567 /*
34568  * These classes are private internal classes
34569  */
34570 Roo.bootstrap.layout.Center = function(config){
34571     config.region = "center";
34572     Roo.bootstrap.layout.Region.call(this, config);
34573     this.visible = true;
34574     this.minWidth = config.minWidth || 20;
34575     this.minHeight = config.minHeight || 20;
34576 };
34577
34578 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34579     hide : function(){
34580         // center panel can't be hidden
34581     },
34582     
34583     show : function(){
34584         // center panel can't be hidden
34585     },
34586     
34587     getMinWidth: function(){
34588         return this.minWidth;
34589     },
34590     
34591     getMinHeight: function(){
34592         return this.minHeight;
34593     }
34594 });
34595
34596
34597
34598
34599  
34600
34601
34602
34603
34604
34605 Roo.bootstrap.layout.North = function(config)
34606 {
34607     config.region = 'north';
34608     config.cursor = 'n-resize';
34609     
34610     Roo.bootstrap.layout.Split.call(this, config);
34611     
34612     
34613     if(this.split){
34614         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34615         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34616         this.split.el.addClass("roo-layout-split-v");
34617     }
34618     var size = config.initialSize || config.height;
34619     if(typeof size != "undefined"){
34620         this.el.setHeight(size);
34621     }
34622 };
34623 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34624 {
34625     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34626     
34627     
34628     
34629     getBox : function(){
34630         if(this.collapsed){
34631             return this.collapsedEl.getBox();
34632         }
34633         var box = this.el.getBox();
34634         if(this.split){
34635             box.height += this.split.el.getHeight();
34636         }
34637         return box;
34638     },
34639     
34640     updateBox : function(box){
34641         if(this.split && !this.collapsed){
34642             box.height -= this.split.el.getHeight();
34643             this.split.el.setLeft(box.x);
34644             this.split.el.setTop(box.y+box.height);
34645             this.split.el.setWidth(box.width);
34646         }
34647         if(this.collapsed){
34648             this.updateBody(box.width, null);
34649         }
34650         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34651     }
34652 });
34653
34654
34655
34656
34657
34658 Roo.bootstrap.layout.South = function(config){
34659     config.region = 'south';
34660     config.cursor = 's-resize';
34661     Roo.bootstrap.layout.Split.call(this, config);
34662     if(this.split){
34663         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34664         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34665         this.split.el.addClass("roo-layout-split-v");
34666     }
34667     var size = config.initialSize || config.height;
34668     if(typeof size != "undefined"){
34669         this.el.setHeight(size);
34670     }
34671 };
34672
34673 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34674     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34675     getBox : function(){
34676         if(this.collapsed){
34677             return this.collapsedEl.getBox();
34678         }
34679         var box = this.el.getBox();
34680         if(this.split){
34681             var sh = this.split.el.getHeight();
34682             box.height += sh;
34683             box.y -= sh;
34684         }
34685         return box;
34686     },
34687     
34688     updateBox : function(box){
34689         if(this.split && !this.collapsed){
34690             var sh = this.split.el.getHeight();
34691             box.height -= sh;
34692             box.y += sh;
34693             this.split.el.setLeft(box.x);
34694             this.split.el.setTop(box.y-sh);
34695             this.split.el.setWidth(box.width);
34696         }
34697         if(this.collapsed){
34698             this.updateBody(box.width, null);
34699         }
34700         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34701     }
34702 });
34703
34704 Roo.bootstrap.layout.East = function(config){
34705     config.region = "east";
34706     config.cursor = "e-resize";
34707     Roo.bootstrap.layout.Split.call(this, config);
34708     if(this.split){
34709         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34710         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34711         this.split.el.addClass("roo-layout-split-h");
34712     }
34713     var size = config.initialSize || config.width;
34714     if(typeof size != "undefined"){
34715         this.el.setWidth(size);
34716     }
34717 };
34718 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34719     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34720     getBox : function(){
34721         if(this.collapsed){
34722             return this.collapsedEl.getBox();
34723         }
34724         var box = this.el.getBox();
34725         if(this.split){
34726             var sw = this.split.el.getWidth();
34727             box.width += sw;
34728             box.x -= sw;
34729         }
34730         return box;
34731     },
34732
34733     updateBox : function(box){
34734         if(this.split && !this.collapsed){
34735             var sw = this.split.el.getWidth();
34736             box.width -= sw;
34737             this.split.el.setLeft(box.x);
34738             this.split.el.setTop(box.y);
34739             this.split.el.setHeight(box.height);
34740             box.x += sw;
34741         }
34742         if(this.collapsed){
34743             this.updateBody(null, box.height);
34744         }
34745         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34746     }
34747 });
34748
34749 Roo.bootstrap.layout.West = function(config){
34750     config.region = "west";
34751     config.cursor = "w-resize";
34752     
34753     Roo.bootstrap.layout.Split.call(this, config);
34754     if(this.split){
34755         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34756         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34757         this.split.el.addClass("roo-layout-split-h");
34758     }
34759     
34760 };
34761 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34762     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34763     
34764     onRender: function(ctr, pos)
34765     {
34766         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34767         var size = this.config.initialSize || this.config.width;
34768         if(typeof size != "undefined"){
34769             this.el.setWidth(size);
34770         }
34771     },
34772     
34773     getBox : function(){
34774         if(this.collapsed){
34775             return this.collapsedEl.getBox();
34776         }
34777         var box = this.el.getBox();
34778         if(this.split){
34779             box.width += this.split.el.getWidth();
34780         }
34781         return box;
34782     },
34783     
34784     updateBox : function(box){
34785         if(this.split && !this.collapsed){
34786             var sw = this.split.el.getWidth();
34787             box.width -= sw;
34788             this.split.el.setLeft(box.x+box.width);
34789             this.split.el.setTop(box.y);
34790             this.split.el.setHeight(box.height);
34791         }
34792         if(this.collapsed){
34793             this.updateBody(null, box.height);
34794         }
34795         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34796     }
34797 });
34798 Roo.namespace("Roo.bootstrap.panel");/*
34799  * Based on:
34800  * Ext JS Library 1.1.1
34801  * Copyright(c) 2006-2007, Ext JS, LLC.
34802  *
34803  * Originally Released Under LGPL - original licence link has changed is not relivant.
34804  *
34805  * Fork - LGPL
34806  * <script type="text/javascript">
34807  */
34808 /**
34809  * @class Roo.ContentPanel
34810  * @extends Roo.util.Observable
34811  * A basic ContentPanel element.
34812  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34813  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34814  * @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
34815  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34816  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34817  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34818  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34819  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34820  * @cfg {String} title          The title for this panel
34821  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34822  * @cfg {String} url            Calls {@link #setUrl} with this value
34823  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34824  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34825  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34826  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34827  * @cfg {Boolean} badges render the badges
34828
34829  * @constructor
34830  * Create a new ContentPanel.
34831  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34832  * @param {String/Object} config A string to set only the title or a config object
34833  * @param {String} content (optional) Set the HTML content for this panel
34834  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34835  */
34836 Roo.bootstrap.panel.Content = function( config){
34837     
34838     this.tpl = config.tpl || false;
34839     
34840     var el = config.el;
34841     var content = config.content;
34842
34843     if(config.autoCreate){ // xtype is available if this is called from factory
34844         el = Roo.id();
34845     }
34846     this.el = Roo.get(el);
34847     if(!this.el && config && config.autoCreate){
34848         if(typeof config.autoCreate == "object"){
34849             if(!config.autoCreate.id){
34850                 config.autoCreate.id = config.id||el;
34851             }
34852             this.el = Roo.DomHelper.append(document.body,
34853                         config.autoCreate, true);
34854         }else{
34855             var elcfg =  {   tag: "div",
34856                             cls: "roo-layout-inactive-content",
34857                             id: config.id||el
34858                             };
34859             if (config.html) {
34860                 elcfg.html = config.html;
34861                 
34862             }
34863                         
34864             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34865         }
34866     } 
34867     this.closable = false;
34868     this.loaded = false;
34869     this.active = false;
34870    
34871       
34872     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34873         
34874         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34875         
34876         this.wrapEl = this.el; //this.el.wrap();
34877         var ti = [];
34878         if (config.toolbar.items) {
34879             ti = config.toolbar.items ;
34880             delete config.toolbar.items ;
34881         }
34882         
34883         var nitems = [];
34884         this.toolbar.render(this.wrapEl, 'before');
34885         for(var i =0;i < ti.length;i++) {
34886           //  Roo.log(['add child', items[i]]);
34887             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34888         }
34889         this.toolbar.items = nitems;
34890         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34891         delete config.toolbar;
34892         
34893     }
34894     /*
34895     // xtype created footer. - not sure if will work as we normally have to render first..
34896     if (this.footer && !this.footer.el && this.footer.xtype) {
34897         if (!this.wrapEl) {
34898             this.wrapEl = this.el.wrap();
34899         }
34900     
34901         this.footer.container = this.wrapEl.createChild();
34902          
34903         this.footer = Roo.factory(this.footer, Roo);
34904         
34905     }
34906     */
34907     
34908      if(typeof config == "string"){
34909         this.title = config;
34910     }else{
34911         Roo.apply(this, config);
34912     }
34913     
34914     if(this.resizeEl){
34915         this.resizeEl = Roo.get(this.resizeEl, true);
34916     }else{
34917         this.resizeEl = this.el;
34918     }
34919     // handle view.xtype
34920     
34921  
34922     
34923     
34924     this.addEvents({
34925         /**
34926          * @event activate
34927          * Fires when this panel is activated. 
34928          * @param {Roo.ContentPanel} this
34929          */
34930         "activate" : true,
34931         /**
34932          * @event deactivate
34933          * Fires when this panel is activated. 
34934          * @param {Roo.ContentPanel} this
34935          */
34936         "deactivate" : true,
34937
34938         /**
34939          * @event resize
34940          * Fires when this panel is resized if fitToFrame is true.
34941          * @param {Roo.ContentPanel} this
34942          * @param {Number} width The width after any component adjustments
34943          * @param {Number} height The height after any component adjustments
34944          */
34945         "resize" : true,
34946         
34947          /**
34948          * @event render
34949          * Fires when this tab is created
34950          * @param {Roo.ContentPanel} this
34951          */
34952         "render" : true
34953         
34954         
34955         
34956     });
34957     
34958
34959     
34960     
34961     if(this.autoScroll){
34962         this.resizeEl.setStyle("overflow", "auto");
34963     } else {
34964         // fix randome scrolling
34965         //this.el.on('scroll', function() {
34966         //    Roo.log('fix random scolling');
34967         //    this.scrollTo('top',0); 
34968         //});
34969     }
34970     content = content || this.content;
34971     if(content){
34972         this.setContent(content);
34973     }
34974     if(config && config.url){
34975         this.setUrl(this.url, this.params, this.loadOnce);
34976     }
34977     
34978     
34979     
34980     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34981     
34982     if (this.view && typeof(this.view.xtype) != 'undefined') {
34983         this.view.el = this.el.appendChild(document.createElement("div"));
34984         this.view = Roo.factory(this.view); 
34985         this.view.render  &&  this.view.render(false, '');  
34986     }
34987     
34988     
34989     this.fireEvent('render', this);
34990 };
34991
34992 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34993     
34994     tabTip : '',
34995     
34996     setRegion : function(region){
34997         this.region = region;
34998         this.setActiveClass(region && !this.background);
34999     },
35000     
35001     
35002     setActiveClass: function(state)
35003     {
35004         if(state){
35005            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35006            this.el.setStyle('position','relative');
35007         }else{
35008            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35009            this.el.setStyle('position', 'absolute');
35010         } 
35011     },
35012     
35013     /**
35014      * Returns the toolbar for this Panel if one was configured. 
35015      * @return {Roo.Toolbar} 
35016      */
35017     getToolbar : function(){
35018         return this.toolbar;
35019     },
35020     
35021     setActiveState : function(active)
35022     {
35023         this.active = active;
35024         this.setActiveClass(active);
35025         if(!active){
35026             this.fireEvent("deactivate", this);
35027         }else{
35028             this.fireEvent("activate", this);
35029         }
35030     },
35031     /**
35032      * Updates this panel's element
35033      * @param {String} content The new content
35034      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35035     */
35036     setContent : function(content, loadScripts){
35037         this.el.update(content, loadScripts);
35038     },
35039
35040     ignoreResize : function(w, h){
35041         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35042             return true;
35043         }else{
35044             this.lastSize = {width: w, height: h};
35045             return false;
35046         }
35047     },
35048     /**
35049      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35050      * @return {Roo.UpdateManager} The UpdateManager
35051      */
35052     getUpdateManager : function(){
35053         return this.el.getUpdateManager();
35054     },
35055      /**
35056      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35057      * @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:
35058 <pre><code>
35059 panel.load({
35060     url: "your-url.php",
35061     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35062     callback: yourFunction,
35063     scope: yourObject, //(optional scope)
35064     discardUrl: false,
35065     nocache: false,
35066     text: "Loading...",
35067     timeout: 30,
35068     scripts: false
35069 });
35070 </code></pre>
35071      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35072      * 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.
35073      * @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}
35074      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35075      * @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.
35076      * @return {Roo.ContentPanel} this
35077      */
35078     load : function(){
35079         var um = this.el.getUpdateManager();
35080         um.update.apply(um, arguments);
35081         return this;
35082     },
35083
35084
35085     /**
35086      * 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.
35087      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35088      * @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)
35089      * @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)
35090      * @return {Roo.UpdateManager} The UpdateManager
35091      */
35092     setUrl : function(url, params, loadOnce){
35093         if(this.refreshDelegate){
35094             this.removeListener("activate", this.refreshDelegate);
35095         }
35096         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35097         this.on("activate", this.refreshDelegate);
35098         return this.el.getUpdateManager();
35099     },
35100     
35101     _handleRefresh : function(url, params, loadOnce){
35102         if(!loadOnce || !this.loaded){
35103             var updater = this.el.getUpdateManager();
35104             updater.update(url, params, this._setLoaded.createDelegate(this));
35105         }
35106     },
35107     
35108     _setLoaded : function(){
35109         this.loaded = true;
35110     }, 
35111     
35112     /**
35113      * Returns this panel's id
35114      * @return {String} 
35115      */
35116     getId : function(){
35117         return this.el.id;
35118     },
35119     
35120     /** 
35121      * Returns this panel's element - used by regiosn to add.
35122      * @return {Roo.Element} 
35123      */
35124     getEl : function(){
35125         return this.wrapEl || this.el;
35126     },
35127     
35128    
35129     
35130     adjustForComponents : function(width, height)
35131     {
35132         //Roo.log('adjustForComponents ');
35133         if(this.resizeEl != this.el){
35134             width -= this.el.getFrameWidth('lr');
35135             height -= this.el.getFrameWidth('tb');
35136         }
35137         if(this.toolbar){
35138             var te = this.toolbar.getEl();
35139             height -= te.getHeight();
35140             te.setWidth(width);
35141         }
35142         if(this.footer){
35143             var te = this.footer.getEl();
35144             Roo.log("footer:" + te.getHeight());
35145             
35146             height -= te.getHeight();
35147             te.setWidth(width);
35148         }
35149         
35150         
35151         if(this.adjustments){
35152             width += this.adjustments[0];
35153             height += this.adjustments[1];
35154         }
35155         return {"width": width, "height": height};
35156     },
35157     
35158     setSize : function(width, height){
35159         if(this.fitToFrame && !this.ignoreResize(width, height)){
35160             if(this.fitContainer && this.resizeEl != this.el){
35161                 this.el.setSize(width, height);
35162             }
35163             var size = this.adjustForComponents(width, height);
35164             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35165             this.fireEvent('resize', this, size.width, size.height);
35166         }
35167     },
35168     
35169     /**
35170      * Returns this panel's title
35171      * @return {String} 
35172      */
35173     getTitle : function(){
35174         return this.title;
35175     },
35176     
35177     /**
35178      * Set this panel's title
35179      * @param {String} title
35180      */
35181     setTitle : function(title){
35182         this.title = title;
35183         if(this.region){
35184             this.region.updatePanelTitle(this, title);
35185         }
35186     },
35187     
35188     /**
35189      * Returns true is this panel was configured to be closable
35190      * @return {Boolean} 
35191      */
35192     isClosable : function(){
35193         return this.closable;
35194     },
35195     
35196     beforeSlide : function(){
35197         this.el.clip();
35198         this.resizeEl.clip();
35199     },
35200     
35201     afterSlide : function(){
35202         this.el.unclip();
35203         this.resizeEl.unclip();
35204     },
35205     
35206     /**
35207      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35208      *   Will fail silently if the {@link #setUrl} method has not been called.
35209      *   This does not activate the panel, just updates its content.
35210      */
35211     refresh : function(){
35212         if(this.refreshDelegate){
35213            this.loaded = false;
35214            this.refreshDelegate();
35215         }
35216     },
35217     
35218     /**
35219      * Destroys this panel
35220      */
35221     destroy : function(){
35222         this.el.removeAllListeners();
35223         var tempEl = document.createElement("span");
35224         tempEl.appendChild(this.el.dom);
35225         tempEl.innerHTML = "";
35226         this.el.remove();
35227         this.el = null;
35228     },
35229     
35230     /**
35231      * form - if the content panel contains a form - this is a reference to it.
35232      * @type {Roo.form.Form}
35233      */
35234     form : false,
35235     /**
35236      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35237      *    This contains a reference to it.
35238      * @type {Roo.View}
35239      */
35240     view : false,
35241     
35242       /**
35243      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35244      * <pre><code>
35245
35246 layout.addxtype({
35247        xtype : 'Form',
35248        items: [ .... ]
35249    }
35250 );
35251
35252 </code></pre>
35253      * @param {Object} cfg Xtype definition of item to add.
35254      */
35255     
35256     
35257     getChildContainer: function () {
35258         return this.getEl();
35259     }
35260     
35261     
35262     /*
35263         var  ret = new Roo.factory(cfg);
35264         return ret;
35265         
35266         
35267         // add form..
35268         if (cfg.xtype.match(/^Form$/)) {
35269             
35270             var el;
35271             //if (this.footer) {
35272             //    el = this.footer.container.insertSibling(false, 'before');
35273             //} else {
35274                 el = this.el.createChild();
35275             //}
35276
35277             this.form = new  Roo.form.Form(cfg);
35278             
35279             
35280             if ( this.form.allItems.length) {
35281                 this.form.render(el.dom);
35282             }
35283             return this.form;
35284         }
35285         // should only have one of theses..
35286         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35287             // views.. should not be just added - used named prop 'view''
35288             
35289             cfg.el = this.el.appendChild(document.createElement("div"));
35290             // factory?
35291             
35292             var ret = new Roo.factory(cfg);
35293              
35294              ret.render && ret.render(false, ''); // render blank..
35295             this.view = ret;
35296             return ret;
35297         }
35298         return false;
35299     }
35300     \*/
35301 });
35302  
35303 /**
35304  * @class Roo.bootstrap.panel.Grid
35305  * @extends Roo.bootstrap.panel.Content
35306  * @constructor
35307  * Create a new GridPanel.
35308  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35309  * @param {Object} config A the config object
35310   
35311  */
35312
35313
35314
35315 Roo.bootstrap.panel.Grid = function(config)
35316 {
35317     
35318       
35319     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35320         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35321
35322     config.el = this.wrapper;
35323     //this.el = this.wrapper;
35324     
35325       if (config.container) {
35326         // ctor'ed from a Border/panel.grid
35327         
35328         
35329         this.wrapper.setStyle("overflow", "hidden");
35330         this.wrapper.addClass('roo-grid-container');
35331
35332     }
35333     
35334     
35335     if(config.toolbar){
35336         var tool_el = this.wrapper.createChild();    
35337         this.toolbar = Roo.factory(config.toolbar);
35338         var ti = [];
35339         if (config.toolbar.items) {
35340             ti = config.toolbar.items ;
35341             delete config.toolbar.items ;
35342         }
35343         
35344         var nitems = [];
35345         this.toolbar.render(tool_el);
35346         for(var i =0;i < ti.length;i++) {
35347           //  Roo.log(['add child', items[i]]);
35348             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35349         }
35350         this.toolbar.items = nitems;
35351         
35352         delete config.toolbar;
35353     }
35354     
35355     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35356     config.grid.scrollBody = true;;
35357     config.grid.monitorWindowResize = false; // turn off autosizing
35358     config.grid.autoHeight = false;
35359     config.grid.autoWidth = false;
35360     
35361     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35362     
35363     if (config.background) {
35364         // render grid on panel activation (if panel background)
35365         this.on('activate', function(gp) {
35366             if (!gp.grid.rendered) {
35367                 gp.grid.render(this.wrapper);
35368                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35369             }
35370         });
35371             
35372     } else {
35373         this.grid.render(this.wrapper);
35374         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35375
35376     }
35377     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35378     // ??? needed ??? config.el = this.wrapper;
35379     
35380     
35381     
35382   
35383     // xtype created footer. - not sure if will work as we normally have to render first..
35384     if (this.footer && !this.footer.el && this.footer.xtype) {
35385         
35386         var ctr = this.grid.getView().getFooterPanel(true);
35387         this.footer.dataSource = this.grid.dataSource;
35388         this.footer = Roo.factory(this.footer, Roo);
35389         this.footer.render(ctr);
35390         
35391     }
35392     
35393     
35394     
35395     
35396      
35397 };
35398
35399 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35400     getId : function(){
35401         return this.grid.id;
35402     },
35403     
35404     /**
35405      * Returns the grid for this panel
35406      * @return {Roo.bootstrap.Table} 
35407      */
35408     getGrid : function(){
35409         return this.grid;    
35410     },
35411     
35412     setSize : function(width, height){
35413         if(!this.ignoreResize(width, height)){
35414             var grid = this.grid;
35415             var size = this.adjustForComponents(width, height);
35416             var gridel = grid.getGridEl();
35417             gridel.setSize(size.width, size.height);
35418             /*
35419             var thd = grid.getGridEl().select('thead',true).first();
35420             var tbd = grid.getGridEl().select('tbody', true).first();
35421             if (tbd) {
35422                 tbd.setSize(width, height - thd.getHeight());
35423             }
35424             */
35425             grid.autoSize();
35426         }
35427     },
35428      
35429     
35430     
35431     beforeSlide : function(){
35432         this.grid.getView().scroller.clip();
35433     },
35434     
35435     afterSlide : function(){
35436         this.grid.getView().scroller.unclip();
35437     },
35438     
35439     destroy : function(){
35440         this.grid.destroy();
35441         delete this.grid;
35442         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35443     }
35444 });
35445
35446 /**
35447  * @class Roo.bootstrap.panel.Nest
35448  * @extends Roo.bootstrap.panel.Content
35449  * @constructor
35450  * Create a new Panel, that can contain a layout.Border.
35451  * 
35452  * 
35453  * @param {Roo.BorderLayout} layout The layout for this panel
35454  * @param {String/Object} config A string to set only the title or a config object
35455  */
35456 Roo.bootstrap.panel.Nest = function(config)
35457 {
35458     // construct with only one argument..
35459     /* FIXME - implement nicer consturctors
35460     if (layout.layout) {
35461         config = layout;
35462         layout = config.layout;
35463         delete config.layout;
35464     }
35465     if (layout.xtype && !layout.getEl) {
35466         // then layout needs constructing..
35467         layout = Roo.factory(layout, Roo);
35468     }
35469     */
35470     
35471     config.el =  config.layout.getEl();
35472     
35473     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35474     
35475     config.layout.monitorWindowResize = false; // turn off autosizing
35476     this.layout = config.layout;
35477     this.layout.getEl().addClass("roo-layout-nested-layout");
35478     
35479     
35480     
35481     
35482 };
35483
35484 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35485
35486     setSize : function(width, height){
35487         if(!this.ignoreResize(width, height)){
35488             var size = this.adjustForComponents(width, height);
35489             var el = this.layout.getEl();
35490             if (size.height < 1) {
35491                 el.setWidth(size.width);   
35492             } else {
35493                 el.setSize(size.width, size.height);
35494             }
35495             var touch = el.dom.offsetWidth;
35496             this.layout.layout();
35497             // ie requires a double layout on the first pass
35498             if(Roo.isIE && !this.initialized){
35499                 this.initialized = true;
35500                 this.layout.layout();
35501             }
35502         }
35503     },
35504     
35505     // activate all subpanels if not currently active..
35506     
35507     setActiveState : function(active){
35508         this.active = active;
35509         this.setActiveClass(active);
35510         
35511         if(!active){
35512             this.fireEvent("deactivate", this);
35513             return;
35514         }
35515         
35516         this.fireEvent("activate", this);
35517         // not sure if this should happen before or after..
35518         if (!this.layout) {
35519             return; // should not happen..
35520         }
35521         var reg = false;
35522         for (var r in this.layout.regions) {
35523             reg = this.layout.getRegion(r);
35524             if (reg.getActivePanel()) {
35525                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35526                 reg.setActivePanel(reg.getActivePanel());
35527                 continue;
35528             }
35529             if (!reg.panels.length) {
35530                 continue;
35531             }
35532             reg.showPanel(reg.getPanel(0));
35533         }
35534         
35535         
35536         
35537         
35538     },
35539     
35540     /**
35541      * Returns the nested BorderLayout for this panel
35542      * @return {Roo.BorderLayout} 
35543      */
35544     getLayout : function(){
35545         return this.layout;
35546     },
35547     
35548      /**
35549      * Adds a xtype elements to the layout of the nested panel
35550      * <pre><code>
35551
35552 panel.addxtype({
35553        xtype : 'ContentPanel',
35554        region: 'west',
35555        items: [ .... ]
35556    }
35557 );
35558
35559 panel.addxtype({
35560         xtype : 'NestedLayoutPanel',
35561         region: 'west',
35562         layout: {
35563            center: { },
35564            west: { }   
35565         },
35566         items : [ ... list of content panels or nested layout panels.. ]
35567    }
35568 );
35569 </code></pre>
35570      * @param {Object} cfg Xtype definition of item to add.
35571      */
35572     addxtype : function(cfg) {
35573         return this.layout.addxtype(cfg);
35574     
35575     }
35576 });        /*
35577  * Based on:
35578  * Ext JS Library 1.1.1
35579  * Copyright(c) 2006-2007, Ext JS, LLC.
35580  *
35581  * Originally Released Under LGPL - original licence link has changed is not relivant.
35582  *
35583  * Fork - LGPL
35584  * <script type="text/javascript">
35585  */
35586 /**
35587  * @class Roo.TabPanel
35588  * @extends Roo.util.Observable
35589  * A lightweight tab container.
35590  * <br><br>
35591  * Usage:
35592  * <pre><code>
35593 // basic tabs 1, built from existing content
35594 var tabs = new Roo.TabPanel("tabs1");
35595 tabs.addTab("script", "View Script");
35596 tabs.addTab("markup", "View Markup");
35597 tabs.activate("script");
35598
35599 // more advanced tabs, built from javascript
35600 var jtabs = new Roo.TabPanel("jtabs");
35601 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35602
35603 // set up the UpdateManager
35604 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35605 var updater = tab2.getUpdateManager();
35606 updater.setDefaultUrl("ajax1.htm");
35607 tab2.on('activate', updater.refresh, updater, true);
35608
35609 // Use setUrl for Ajax loading
35610 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35611 tab3.setUrl("ajax2.htm", null, true);
35612
35613 // Disabled tab
35614 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35615 tab4.disable();
35616
35617 jtabs.activate("jtabs-1");
35618  * </code></pre>
35619  * @constructor
35620  * Create a new TabPanel.
35621  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35622  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35623  */
35624 Roo.bootstrap.panel.Tabs = function(config){
35625     /**
35626     * The container element for this TabPanel.
35627     * @type Roo.Element
35628     */
35629     this.el = Roo.get(config.el);
35630     delete config.el;
35631     if(config){
35632         if(typeof config == "boolean"){
35633             this.tabPosition = config ? "bottom" : "top";
35634         }else{
35635             Roo.apply(this, config);
35636         }
35637     }
35638     
35639     if(this.tabPosition == "bottom"){
35640         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35641         this.el.addClass("roo-tabs-bottom");
35642     }
35643     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35644     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35645     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35646     if(Roo.isIE){
35647         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35648     }
35649     if(this.tabPosition != "bottom"){
35650         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35651          * @type Roo.Element
35652          */
35653         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35654         this.el.addClass("roo-tabs-top");
35655     }
35656     this.items = [];
35657
35658     this.bodyEl.setStyle("position", "relative");
35659
35660     this.active = null;
35661     this.activateDelegate = this.activate.createDelegate(this);
35662
35663     this.addEvents({
35664         /**
35665          * @event tabchange
35666          * Fires when the active tab changes
35667          * @param {Roo.TabPanel} this
35668          * @param {Roo.TabPanelItem} activePanel The new active tab
35669          */
35670         "tabchange": true,
35671         /**
35672          * @event beforetabchange
35673          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35674          * @param {Roo.TabPanel} this
35675          * @param {Object} e Set cancel to true on this object to cancel the tab change
35676          * @param {Roo.TabPanelItem} tab The tab being changed to
35677          */
35678         "beforetabchange" : true
35679     });
35680
35681     Roo.EventManager.onWindowResize(this.onResize, this);
35682     this.cpad = this.el.getPadding("lr");
35683     this.hiddenCount = 0;
35684
35685
35686     // toolbar on the tabbar support...
35687     if (this.toolbar) {
35688         alert("no toolbar support yet");
35689         this.toolbar  = false;
35690         /*
35691         var tcfg = this.toolbar;
35692         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35693         this.toolbar = new Roo.Toolbar(tcfg);
35694         if (Roo.isSafari) {
35695             var tbl = tcfg.container.child('table', true);
35696             tbl.setAttribute('width', '100%');
35697         }
35698         */
35699         
35700     }
35701    
35702
35703
35704     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35705 };
35706
35707 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35708     /*
35709      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35710      */
35711     tabPosition : "top",
35712     /*
35713      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35714      */
35715     currentTabWidth : 0,
35716     /*
35717      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35718      */
35719     minTabWidth : 40,
35720     /*
35721      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35722      */
35723     maxTabWidth : 250,
35724     /*
35725      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35726      */
35727     preferredTabWidth : 175,
35728     /*
35729      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35730      */
35731     resizeTabs : false,
35732     /*
35733      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35734      */
35735     monitorResize : true,
35736     /*
35737      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35738      */
35739     toolbar : false,
35740
35741     /**
35742      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35743      * @param {String} id The id of the div to use <b>or create</b>
35744      * @param {String} text The text for the tab
35745      * @param {String} content (optional) Content to put in the TabPanelItem body
35746      * @param {Boolean} closable (optional) True to create a close icon on the tab
35747      * @return {Roo.TabPanelItem} The created TabPanelItem
35748      */
35749     addTab : function(id, text, content, closable, tpl)
35750     {
35751         var item = new Roo.bootstrap.panel.TabItem({
35752             panel: this,
35753             id : id,
35754             text : text,
35755             closable : closable,
35756             tpl : tpl
35757         });
35758         this.addTabItem(item);
35759         if(content){
35760             item.setContent(content);
35761         }
35762         return item;
35763     },
35764
35765     /**
35766      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35767      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35768      * @return {Roo.TabPanelItem}
35769      */
35770     getTab : function(id){
35771         return this.items[id];
35772     },
35773
35774     /**
35775      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35776      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35777      */
35778     hideTab : function(id){
35779         var t = this.items[id];
35780         if(!t.isHidden()){
35781            t.setHidden(true);
35782            this.hiddenCount++;
35783            this.autoSizeTabs();
35784         }
35785     },
35786
35787     /**
35788      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35789      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35790      */
35791     unhideTab : function(id){
35792         var t = this.items[id];
35793         if(t.isHidden()){
35794            t.setHidden(false);
35795            this.hiddenCount--;
35796            this.autoSizeTabs();
35797         }
35798     },
35799
35800     /**
35801      * Adds an existing {@link Roo.TabPanelItem}.
35802      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35803      */
35804     addTabItem : function(item){
35805         this.items[item.id] = item;
35806         this.items.push(item);
35807       //  if(this.resizeTabs){
35808     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35809   //         this.autoSizeTabs();
35810 //        }else{
35811 //            item.autoSize();
35812        // }
35813     },
35814
35815     /**
35816      * Removes a {@link Roo.TabPanelItem}.
35817      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35818      */
35819     removeTab : function(id){
35820         var items = this.items;
35821         var tab = items[id];
35822         if(!tab) { return; }
35823         var index = items.indexOf(tab);
35824         if(this.active == tab && items.length > 1){
35825             var newTab = this.getNextAvailable(index);
35826             if(newTab) {
35827                 newTab.activate();
35828             }
35829         }
35830         this.stripEl.dom.removeChild(tab.pnode.dom);
35831         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35832             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35833         }
35834         items.splice(index, 1);
35835         delete this.items[tab.id];
35836         tab.fireEvent("close", tab);
35837         tab.purgeListeners();
35838         this.autoSizeTabs();
35839     },
35840
35841     getNextAvailable : function(start){
35842         var items = this.items;
35843         var index = start;
35844         // look for a next tab that will slide over to
35845         // replace the one being removed
35846         while(index < items.length){
35847             var item = items[++index];
35848             if(item && !item.isHidden()){
35849                 return item;
35850             }
35851         }
35852         // if one isn't found select the previous tab (on the left)
35853         index = start;
35854         while(index >= 0){
35855             var item = items[--index];
35856             if(item && !item.isHidden()){
35857                 return item;
35858             }
35859         }
35860         return null;
35861     },
35862
35863     /**
35864      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35865      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35866      */
35867     disableTab : function(id){
35868         var tab = this.items[id];
35869         if(tab && this.active != tab){
35870             tab.disable();
35871         }
35872     },
35873
35874     /**
35875      * Enables a {@link Roo.TabPanelItem} that is disabled.
35876      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35877      */
35878     enableTab : function(id){
35879         var tab = this.items[id];
35880         tab.enable();
35881     },
35882
35883     /**
35884      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35885      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35886      * @return {Roo.TabPanelItem} The TabPanelItem.
35887      */
35888     activate : function(id){
35889         var tab = this.items[id];
35890         if(!tab){
35891             return null;
35892         }
35893         if(tab == this.active || tab.disabled){
35894             return tab;
35895         }
35896         var e = {};
35897         this.fireEvent("beforetabchange", this, e, tab);
35898         if(e.cancel !== true && !tab.disabled){
35899             if(this.active){
35900                 this.active.hide();
35901             }
35902             this.active = this.items[id];
35903             this.active.show();
35904             this.fireEvent("tabchange", this, this.active);
35905         }
35906         return tab;
35907     },
35908
35909     /**
35910      * Gets the active {@link Roo.TabPanelItem}.
35911      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35912      */
35913     getActiveTab : function(){
35914         return this.active;
35915     },
35916
35917     /**
35918      * Updates the tab body element to fit the height of the container element
35919      * for overflow scrolling
35920      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35921      */
35922     syncHeight : function(targetHeight){
35923         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35924         var bm = this.bodyEl.getMargins();
35925         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35926         this.bodyEl.setHeight(newHeight);
35927         return newHeight;
35928     },
35929
35930     onResize : function(){
35931         if(this.monitorResize){
35932             this.autoSizeTabs();
35933         }
35934     },
35935
35936     /**
35937      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35938      */
35939     beginUpdate : function(){
35940         this.updating = true;
35941     },
35942
35943     /**
35944      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35945      */
35946     endUpdate : function(){
35947         this.updating = false;
35948         this.autoSizeTabs();
35949     },
35950
35951     /**
35952      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35953      */
35954     autoSizeTabs : function(){
35955         var count = this.items.length;
35956         var vcount = count - this.hiddenCount;
35957         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35958             return;
35959         }
35960         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35961         var availWidth = Math.floor(w / vcount);
35962         var b = this.stripBody;
35963         if(b.getWidth() > w){
35964             var tabs = this.items;
35965             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35966             if(availWidth < this.minTabWidth){
35967                 /*if(!this.sleft){    // incomplete scrolling code
35968                     this.createScrollButtons();
35969                 }
35970                 this.showScroll();
35971                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35972             }
35973         }else{
35974             if(this.currentTabWidth < this.preferredTabWidth){
35975                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35976             }
35977         }
35978     },
35979
35980     /**
35981      * Returns the number of tabs in this TabPanel.
35982      * @return {Number}
35983      */
35984      getCount : function(){
35985          return this.items.length;
35986      },
35987
35988     /**
35989      * Resizes all the tabs to the passed width
35990      * @param {Number} The new width
35991      */
35992     setTabWidth : function(width){
35993         this.currentTabWidth = width;
35994         for(var i = 0, len = this.items.length; i < len; i++) {
35995                 if(!this.items[i].isHidden()) {
35996                 this.items[i].setWidth(width);
35997             }
35998         }
35999     },
36000
36001     /**
36002      * Destroys this TabPanel
36003      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36004      */
36005     destroy : function(removeEl){
36006         Roo.EventManager.removeResizeListener(this.onResize, this);
36007         for(var i = 0, len = this.items.length; i < len; i++){
36008             this.items[i].purgeListeners();
36009         }
36010         if(removeEl === true){
36011             this.el.update("");
36012             this.el.remove();
36013         }
36014     },
36015     
36016     createStrip : function(container)
36017     {
36018         var strip = document.createElement("nav");
36019         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36020         container.appendChild(strip);
36021         return strip;
36022     },
36023     
36024     createStripList : function(strip)
36025     {
36026         // div wrapper for retard IE
36027         // returns the "tr" element.
36028         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36029         //'<div class="x-tabs-strip-wrap">'+
36030           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36031           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36032         return strip.firstChild; //.firstChild.firstChild.firstChild;
36033     },
36034     createBody : function(container)
36035     {
36036         var body = document.createElement("div");
36037         Roo.id(body, "tab-body");
36038         //Roo.fly(body).addClass("x-tabs-body");
36039         Roo.fly(body).addClass("tab-content");
36040         container.appendChild(body);
36041         return body;
36042     },
36043     createItemBody :function(bodyEl, id){
36044         var body = Roo.getDom(id);
36045         if(!body){
36046             body = document.createElement("div");
36047             body.id = id;
36048         }
36049         //Roo.fly(body).addClass("x-tabs-item-body");
36050         Roo.fly(body).addClass("tab-pane");
36051          bodyEl.insertBefore(body, bodyEl.firstChild);
36052         return body;
36053     },
36054     /** @private */
36055     createStripElements :  function(stripEl, text, closable, tpl)
36056     {
36057         var td = document.createElement("li"); // was td..
36058         
36059         
36060         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36061         
36062         
36063         stripEl.appendChild(td);
36064         /*if(closable){
36065             td.className = "x-tabs-closable";
36066             if(!this.closeTpl){
36067                 this.closeTpl = new Roo.Template(
36068                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36069                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36070                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36071                 );
36072             }
36073             var el = this.closeTpl.overwrite(td, {"text": text});
36074             var close = el.getElementsByTagName("div")[0];
36075             var inner = el.getElementsByTagName("em")[0];
36076             return {"el": el, "close": close, "inner": inner};
36077         } else {
36078         */
36079         // not sure what this is..
36080 //            if(!this.tabTpl){
36081                 //this.tabTpl = new Roo.Template(
36082                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36083                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36084                 //);
36085 //                this.tabTpl = new Roo.Template(
36086 //                   '<a href="#">' +
36087 //                   '<span unselectable="on"' +
36088 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36089 //                            ' >{text}</span></a>'
36090 //                );
36091 //                
36092 //            }
36093
36094
36095             var template = tpl || this.tabTpl || false;
36096             
36097             if(!template){
36098                 
36099                 template = new Roo.Template(
36100                    '<a href="#">' +
36101                    '<span unselectable="on"' +
36102                             (this.disableTooltips ? '' : ' title="{text}"') +
36103                             ' >{text}</span></a>'
36104                 );
36105             }
36106             
36107             switch (typeof(template)) {
36108                 case 'object' :
36109                     break;
36110                 case 'string' :
36111                     template = new Roo.Template(template);
36112                     break;
36113                 default :
36114                     break;
36115             }
36116             
36117             var el = template.overwrite(td, {"text": text});
36118             
36119             var inner = el.getElementsByTagName("span")[0];
36120             
36121             return {"el": el, "inner": inner};
36122             
36123     }
36124         
36125     
36126 });
36127
36128 /**
36129  * @class Roo.TabPanelItem
36130  * @extends Roo.util.Observable
36131  * Represents an individual item (tab plus body) in a TabPanel.
36132  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36133  * @param {String} id The id of this TabPanelItem
36134  * @param {String} text The text for the tab of this TabPanelItem
36135  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36136  */
36137 Roo.bootstrap.panel.TabItem = function(config){
36138     /**
36139      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36140      * @type Roo.TabPanel
36141      */
36142     this.tabPanel = config.panel;
36143     /**
36144      * The id for this TabPanelItem
36145      * @type String
36146      */
36147     this.id = config.id;
36148     /** @private */
36149     this.disabled = false;
36150     /** @private */
36151     this.text = config.text;
36152     /** @private */
36153     this.loaded = false;
36154     this.closable = config.closable;
36155
36156     /**
36157      * The body element for this TabPanelItem.
36158      * @type Roo.Element
36159      */
36160     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36161     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36162     this.bodyEl.setStyle("display", "block");
36163     this.bodyEl.setStyle("zoom", "1");
36164     //this.hideAction();
36165
36166     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36167     /** @private */
36168     this.el = Roo.get(els.el);
36169     this.inner = Roo.get(els.inner, true);
36170     this.textEl = Roo.get(this.el.dom.firstChild, true);
36171     this.pnode = Roo.get(els.el.parentNode, true);
36172     this.el.on("mousedown", this.onTabMouseDown, this);
36173     this.el.on("click", this.onTabClick, this);
36174     /** @private */
36175     if(config.closable){
36176         var c = Roo.get(els.close, true);
36177         c.dom.title = this.closeText;
36178         c.addClassOnOver("close-over");
36179         c.on("click", this.closeClick, this);
36180      }
36181
36182     this.addEvents({
36183          /**
36184          * @event activate
36185          * Fires when this tab becomes the active tab.
36186          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36187          * @param {Roo.TabPanelItem} this
36188          */
36189         "activate": true,
36190         /**
36191          * @event beforeclose
36192          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36193          * @param {Roo.TabPanelItem} this
36194          * @param {Object} e Set cancel to true on this object to cancel the close.
36195          */
36196         "beforeclose": true,
36197         /**
36198          * @event close
36199          * Fires when this tab is closed.
36200          * @param {Roo.TabPanelItem} this
36201          */
36202          "close": true,
36203         /**
36204          * @event deactivate
36205          * Fires when this tab is no longer the active tab.
36206          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36207          * @param {Roo.TabPanelItem} this
36208          */
36209          "deactivate" : true
36210     });
36211     this.hidden = false;
36212
36213     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36214 };
36215
36216 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36217            {
36218     purgeListeners : function(){
36219        Roo.util.Observable.prototype.purgeListeners.call(this);
36220        this.el.removeAllListeners();
36221     },
36222     /**
36223      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36224      */
36225     show : function(){
36226         this.pnode.addClass("active");
36227         this.showAction();
36228         if(Roo.isOpera){
36229             this.tabPanel.stripWrap.repaint();
36230         }
36231         this.fireEvent("activate", this.tabPanel, this);
36232     },
36233
36234     /**
36235      * Returns true if this tab is the active tab.
36236      * @return {Boolean}
36237      */
36238     isActive : function(){
36239         return this.tabPanel.getActiveTab() == this;
36240     },
36241
36242     /**
36243      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36244      */
36245     hide : function(){
36246         this.pnode.removeClass("active");
36247         this.hideAction();
36248         this.fireEvent("deactivate", this.tabPanel, this);
36249     },
36250
36251     hideAction : function(){
36252         this.bodyEl.hide();
36253         this.bodyEl.setStyle("position", "absolute");
36254         this.bodyEl.setLeft("-20000px");
36255         this.bodyEl.setTop("-20000px");
36256     },
36257
36258     showAction : function(){
36259         this.bodyEl.setStyle("position", "relative");
36260         this.bodyEl.setTop("");
36261         this.bodyEl.setLeft("");
36262         this.bodyEl.show();
36263     },
36264
36265     /**
36266      * Set the tooltip for the tab.
36267      * @param {String} tooltip The tab's tooltip
36268      */
36269     setTooltip : function(text){
36270         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36271             this.textEl.dom.qtip = text;
36272             this.textEl.dom.removeAttribute('title');
36273         }else{
36274             this.textEl.dom.title = text;
36275         }
36276     },
36277
36278     onTabClick : function(e){
36279         e.preventDefault();
36280         this.tabPanel.activate(this.id);
36281     },
36282
36283     onTabMouseDown : function(e){
36284         e.preventDefault();
36285         this.tabPanel.activate(this.id);
36286     },
36287 /*
36288     getWidth : function(){
36289         return this.inner.getWidth();
36290     },
36291
36292     setWidth : function(width){
36293         var iwidth = width - this.pnode.getPadding("lr");
36294         this.inner.setWidth(iwidth);
36295         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36296         this.pnode.setWidth(width);
36297     },
36298 */
36299     /**
36300      * Show or hide the tab
36301      * @param {Boolean} hidden True to hide or false to show.
36302      */
36303     setHidden : function(hidden){
36304         this.hidden = hidden;
36305         this.pnode.setStyle("display", hidden ? "none" : "");
36306     },
36307
36308     /**
36309      * Returns true if this tab is "hidden"
36310      * @return {Boolean}
36311      */
36312     isHidden : function(){
36313         return this.hidden;
36314     },
36315
36316     /**
36317      * Returns the text for this tab
36318      * @return {String}
36319      */
36320     getText : function(){
36321         return this.text;
36322     },
36323     /*
36324     autoSize : function(){
36325         //this.el.beginMeasure();
36326         this.textEl.setWidth(1);
36327         /*
36328          *  #2804 [new] Tabs in Roojs
36329          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36330          */
36331         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36332         //this.el.endMeasure();
36333     //},
36334
36335     /**
36336      * Sets the text for the tab (Note: this also sets the tooltip text)
36337      * @param {String} text The tab's text and tooltip
36338      */
36339     setText : function(text){
36340         this.text = text;
36341         this.textEl.update(text);
36342         this.setTooltip(text);
36343         //if(!this.tabPanel.resizeTabs){
36344         //    this.autoSize();
36345         //}
36346     },
36347     /**
36348      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36349      */
36350     activate : function(){
36351         this.tabPanel.activate(this.id);
36352     },
36353
36354     /**
36355      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36356      */
36357     disable : function(){
36358         if(this.tabPanel.active != this){
36359             this.disabled = true;
36360             this.pnode.addClass("disabled");
36361         }
36362     },
36363
36364     /**
36365      * Enables this TabPanelItem if it was previously disabled.
36366      */
36367     enable : function(){
36368         this.disabled = false;
36369         this.pnode.removeClass("disabled");
36370     },
36371
36372     /**
36373      * Sets the content for this TabPanelItem.
36374      * @param {String} content The content
36375      * @param {Boolean} loadScripts true to look for and load scripts
36376      */
36377     setContent : function(content, loadScripts){
36378         this.bodyEl.update(content, loadScripts);
36379     },
36380
36381     /**
36382      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36383      * @return {Roo.UpdateManager} The UpdateManager
36384      */
36385     getUpdateManager : function(){
36386         return this.bodyEl.getUpdateManager();
36387     },
36388
36389     /**
36390      * Set a URL to be used to load the content for this TabPanelItem.
36391      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36392      * @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)
36393      * @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)
36394      * @return {Roo.UpdateManager} The UpdateManager
36395      */
36396     setUrl : function(url, params, loadOnce){
36397         if(this.refreshDelegate){
36398             this.un('activate', this.refreshDelegate);
36399         }
36400         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36401         this.on("activate", this.refreshDelegate);
36402         return this.bodyEl.getUpdateManager();
36403     },
36404
36405     /** @private */
36406     _handleRefresh : function(url, params, loadOnce){
36407         if(!loadOnce || !this.loaded){
36408             var updater = this.bodyEl.getUpdateManager();
36409             updater.update(url, params, this._setLoaded.createDelegate(this));
36410         }
36411     },
36412
36413     /**
36414      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36415      *   Will fail silently if the setUrl method has not been called.
36416      *   This does not activate the panel, just updates its content.
36417      */
36418     refresh : function(){
36419         if(this.refreshDelegate){
36420            this.loaded = false;
36421            this.refreshDelegate();
36422         }
36423     },
36424
36425     /** @private */
36426     _setLoaded : function(){
36427         this.loaded = true;
36428     },
36429
36430     /** @private */
36431     closeClick : function(e){
36432         var o = {};
36433         e.stopEvent();
36434         this.fireEvent("beforeclose", this, o);
36435         if(o.cancel !== true){
36436             this.tabPanel.removeTab(this.id);
36437         }
36438     },
36439     /**
36440      * The text displayed in the tooltip for the close icon.
36441      * @type String
36442      */
36443     closeText : "Close this tab"
36444 });