roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         
1528         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529             this.el.dom.src =  url;
1530             return;
1531         }
1532         
1533         this.el.select('img', true).first().dom.src =  url;
1534     }
1535     
1536     
1537    
1538 });
1539
1540  /*
1541  * - LGPL
1542  *
1543  * image
1544  * 
1545  */
1546
1547
1548 /**
1549  * @class Roo.bootstrap.Link
1550  * @extends Roo.bootstrap.Component
1551  * Bootstrap Link Class
1552  * @cfg {String} alt image alternative text
1553  * @cfg {String} href a tag href
1554  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555  * @cfg {String} html the content of the link.
1556  * @cfg {String} anchor name for the anchor link
1557  * @cfg {String} fa - favicon
1558
1559  * @cfg {Boolean} preventDefault (true | false) default false
1560
1561  * 
1562  * @constructor
1563  * Create a new Input
1564  * @param {Object} config The config object
1565  */
1566
1567 Roo.bootstrap.Link = function(config){
1568     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1569     
1570     this.addEvents({
1571         // img events
1572         /**
1573          * @event click
1574          * The img click event for the img.
1575          * @param {Roo.EventObject} e
1576          */
1577         "click" : true
1578     });
1579 };
1580
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1582     
1583     href: false,
1584     target: false,
1585     preventDefault: false,
1586     anchor : false,
1587     alt : false,
1588     fa: false,
1589
1590
1591     getAutoCreate : function()
1592     {
1593         var html = this.html || '';
1594         
1595         if (this.fa !== false) {
1596             html = '<i class="fa fa-' + this.fa + '"></i>';
1597         }
1598         var cfg = {
1599             tag: 'a'
1600         };
1601         // anchor's do not require html/href...
1602         if (this.anchor === false) {
1603             cfg.html = html;
1604             cfg.href = this.href || '#';
1605         } else {
1606             cfg.name = this.anchor;
1607             if (this.html !== false || this.fa !== false) {
1608                 cfg.html = html;
1609             }
1610             if (this.href !== false) {
1611                 cfg.href = this.href;
1612             }
1613         }
1614         
1615         if(this.alt !== false){
1616             cfg.alt = this.alt;
1617         }
1618         
1619         
1620         if(this.target !== false) {
1621             cfg.target = this.target;
1622         }
1623         
1624         return cfg;
1625     },
1626     
1627     initEvents: function() {
1628         
1629         if(!this.href || this.preventDefault){
1630             this.el.on('click', this.onClick, this);
1631         }
1632     },
1633     
1634     onClick : function(e)
1635     {
1636         if(this.preventDefault){
1637             e.preventDefault();
1638         }
1639         //Roo.log('img onclick');
1640         this.fireEvent('click', this, e);
1641     }
1642    
1643 });
1644
1645  /*
1646  * - LGPL
1647  *
1648  * header
1649  * 
1650  */
1651
1652 /**
1653  * @class Roo.bootstrap.Header
1654  * @extends Roo.bootstrap.Component
1655  * Bootstrap Header class
1656  * @cfg {String} html content of header
1657  * @cfg {Number} level (1|2|3|4|5|6) default 1
1658  * 
1659  * @constructor
1660  * Create a new Header
1661  * @param {Object} config The config object
1662  */
1663
1664
1665 Roo.bootstrap.Header  = function(config){
1666     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1667 };
1668
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1670     
1671     //href : false,
1672     html : false,
1673     level : 1,
1674     
1675     
1676     
1677     getAutoCreate : function(){
1678         
1679         
1680         
1681         var cfg = {
1682             tag: 'h' + (1 *this.level),
1683             html: this.html || ''
1684         } ;
1685         
1686         return cfg;
1687     }
1688    
1689 });
1690
1691  
1692
1693  /*
1694  * Based on:
1695  * Ext JS Library 1.1.1
1696  * Copyright(c) 2006-2007, Ext JS, LLC.
1697  *
1698  * Originally Released Under LGPL - original licence link has changed is not relivant.
1699  *
1700  * Fork - LGPL
1701  * <script type="text/javascript">
1702  */
1703  
1704 /**
1705  * @class Roo.bootstrap.MenuMgr
1706  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1707  * @singleton
1708  */
1709 Roo.bootstrap.MenuMgr = function(){
1710    var menus, active, groups = {}, attached = false, lastShow = new Date();
1711
1712    // private - called when first menu is created
1713    function init(){
1714        menus = {};
1715        active = new Roo.util.MixedCollection();
1716        Roo.get(document).addKeyListener(27, function(){
1717            if(active.length > 0){
1718                hideAll();
1719            }
1720        });
1721    }
1722
1723    // private
1724    function hideAll(){
1725        if(active && active.length > 0){
1726            var c = active.clone();
1727            c.each(function(m){
1728                m.hide();
1729            });
1730        }
1731    }
1732
1733    // private
1734    function onHide(m){
1735        active.remove(m);
1736        if(active.length < 1){
1737            Roo.get(document).un("mouseup", onMouseDown);
1738             
1739            attached = false;
1740        }
1741    }
1742
1743    // private
1744    function onShow(m){
1745        var last = active.last();
1746        lastShow = new Date();
1747        active.add(m);
1748        if(!attached){
1749           Roo.get(document).on("mouseup", onMouseDown);
1750            
1751            attached = true;
1752        }
1753        if(m.parentMenu){
1754           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755           m.parentMenu.activeChild = m;
1756        }else if(last && last.isVisible()){
1757           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1758        }
1759    }
1760
1761    // private
1762    function onBeforeHide(m){
1763        if(m.activeChild){
1764            m.activeChild.hide();
1765        }
1766        if(m.autoHideTimer){
1767            clearTimeout(m.autoHideTimer);
1768            delete m.autoHideTimer;
1769        }
1770    }
1771
1772    // private
1773    function onBeforeShow(m){
1774        var pm = m.parentMenu;
1775        if(!pm && !m.allowOtherMenus){
1776            hideAll();
1777        }else if(pm && pm.activeChild && active != m){
1778            pm.activeChild.hide();
1779        }
1780    }
1781
1782    // private this should really trigger on mouseup..
1783    function onMouseDown(e){
1784         Roo.log("on Mouse Up");
1785         
1786         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787             Roo.log("MenuManager hideAll");
1788             hideAll();
1789             e.stopEvent();
1790         }
1791         
1792         
1793    }
1794
1795    // private
1796    function onBeforeCheck(mi, state){
1797        if(state){
1798            var g = groups[mi.group];
1799            for(var i = 0, l = g.length; i < l; i++){
1800                if(g[i] != mi){
1801                    g[i].setChecked(false);
1802                }
1803            }
1804        }
1805    }
1806
1807    return {
1808
1809        /**
1810         * Hides all menus that are currently visible
1811         */
1812        hideAll : function(){
1813             hideAll();  
1814        },
1815
1816        // private
1817        register : function(menu){
1818            if(!menus){
1819                init();
1820            }
1821            menus[menu.id] = menu;
1822            menu.on("beforehide", onBeforeHide);
1823            menu.on("hide", onHide);
1824            menu.on("beforeshow", onBeforeShow);
1825            menu.on("show", onShow);
1826            var g = menu.group;
1827            if(g && menu.events["checkchange"]){
1828                if(!groups[g]){
1829                    groups[g] = [];
1830                }
1831                groups[g].push(menu);
1832                menu.on("checkchange", onCheck);
1833            }
1834        },
1835
1836         /**
1837          * Returns a {@link Roo.menu.Menu} object
1838          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839          * be used to generate and return a new Menu instance.
1840          */
1841        get : function(menu){
1842            if(typeof menu == "string"){ // menu id
1843                return menus[menu];
1844            }else if(menu.events){  // menu instance
1845                return menu;
1846            }
1847            /*else if(typeof menu.length == 'number'){ // array of menu items?
1848                return new Roo.bootstrap.Menu({items:menu});
1849            }else{ // otherwise, must be a config
1850                return new Roo.bootstrap.Menu(menu);
1851            }
1852            */
1853            return false;
1854        },
1855
1856        // private
1857        unregister : function(menu){
1858            delete menus[menu.id];
1859            menu.un("beforehide", onBeforeHide);
1860            menu.un("hide", onHide);
1861            menu.un("beforeshow", onBeforeShow);
1862            menu.un("show", onShow);
1863            var g = menu.group;
1864            if(g && menu.events["checkchange"]){
1865                groups[g].remove(menu);
1866                menu.un("checkchange", onCheck);
1867            }
1868        },
1869
1870        // private
1871        registerCheckable : function(menuItem){
1872            var g = menuItem.group;
1873            if(g){
1874                if(!groups[g]){
1875                    groups[g] = [];
1876                }
1877                groups[g].push(menuItem);
1878                menuItem.on("beforecheckchange", onBeforeCheck);
1879            }
1880        },
1881
1882        // private
1883        unregisterCheckable : function(menuItem){
1884            var g = menuItem.group;
1885            if(g){
1886                groups[g].remove(menuItem);
1887                menuItem.un("beforecheckchange", onBeforeCheck);
1888            }
1889        }
1890    };
1891 }();/*
1892  * - LGPL
1893  *
1894  * menu
1895  * 
1896  */
1897
1898 /**
1899  * @class Roo.bootstrap.Menu
1900  * @extends Roo.bootstrap.Component
1901  * Bootstrap Menu class - container for MenuItems
1902  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1904  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1905  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1906  * 
1907  * @constructor
1908  * Create a new Menu
1909  * @param {Object} config The config object
1910  */
1911
1912
1913 Roo.bootstrap.Menu = function(config){
1914     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915     if (this.registerMenu && this.type != 'treeview')  {
1916         Roo.bootstrap.MenuMgr.register(this);
1917     }
1918     this.addEvents({
1919         /**
1920          * @event beforeshow
1921          * Fires before this menu is displayed
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforeshow : true,
1925         /**
1926          * @event beforehide
1927          * Fires before this menu is hidden
1928          * @param {Roo.menu.Menu} this
1929          */
1930         beforehide : true,
1931         /**
1932          * @event show
1933          * Fires after this menu is displayed
1934          * @param {Roo.menu.Menu} this
1935          */
1936         show : true,
1937         /**
1938          * @event hide
1939          * Fires after this menu is hidden
1940          * @param {Roo.menu.Menu} this
1941          */
1942         hide : true,
1943         /**
1944          * @event click
1945          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946          * @param {Roo.menu.Menu} this
1947          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948          * @param {Roo.EventObject} e
1949          */
1950         click : true,
1951         /**
1952          * @event mouseover
1953          * Fires when the mouse is hovering over this menu
1954          * @param {Roo.menu.Menu} this
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          */
1958         mouseover : true,
1959         /**
1960          * @event mouseout
1961          * Fires when the mouse exits this menu
1962          * @param {Roo.menu.Menu} this
1963          * @param {Roo.EventObject} e
1964          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1965          */
1966         mouseout : true,
1967         /**
1968          * @event itemclick
1969          * Fires when a menu item contained in this menu is clicked
1970          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971          * @param {Roo.EventObject} e
1972          */
1973         itemclick: true
1974     });
1975     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1976 };
1977
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1979     
1980    /// html : false,
1981     //align : '',
1982     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1983     type: false,
1984     /**
1985      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1986      */
1987     registerMenu : true,
1988     
1989     menuItems :false, // stores the menu items..
1990     
1991     hidden:true,
1992         
1993     parentMenu : false,
1994     
1995     stopEvent : true,
1996     
1997     isLink : false,
1998     
1999     getChildContainer : function() {
2000         return this.el;  
2001     },
2002     
2003     getAutoCreate : function(){
2004          
2005         //if (['right'].indexOf(this.align)!==-1) {
2006         //    cfg.cn[1].cls += ' pull-right'
2007         //}
2008         
2009         
2010         var cfg = {
2011             tag : 'ul',
2012             cls : 'dropdown-menu' ,
2013             style : 'z-index:1000'
2014             
2015         };
2016         
2017         if (this.type === 'submenu') {
2018             cfg.cls = 'submenu active';
2019         }
2020         if (this.type === 'treeview') {
2021             cfg.cls = 'treeview-menu';
2022         }
2023         
2024         return cfg;
2025     },
2026     initEvents : function() {
2027         
2028        // Roo.log("ADD event");
2029        // Roo.log(this.triggerEl.dom);
2030         
2031         this.triggerEl.on('click', this.onTriggerClick, this);
2032         
2033         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2034         
2035         this.triggerEl.addClass('dropdown-toggle');
2036         
2037         if (Roo.isTouch) {
2038             this.el.on('touchstart'  , this.onTouch, this);
2039         }
2040         this.el.on('click' , this.onClick, this);
2041
2042         this.el.on("mouseover", this.onMouseOver, this);
2043         this.el.on("mouseout", this.onMouseOut, this);
2044         
2045     },
2046     
2047     findTargetItem : function(e)
2048     {
2049         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2050         if(!t){
2051             return false;
2052         }
2053         //Roo.log(t);         Roo.log(t.id);
2054         if(t && t.id){
2055             //Roo.log(this.menuitems);
2056             return this.menuitems.get(t.id);
2057             
2058             //return this.items.get(t.menuItemId);
2059         }
2060         
2061         return false;
2062     },
2063     
2064     onTouch : function(e) 
2065     {
2066         Roo.log("menu.onTouch");
2067         //e.stopEvent(); this make the user popdown broken
2068         this.onClick(e);
2069     },
2070     
2071     onClick : function(e)
2072     {
2073         Roo.log("menu.onClick");
2074         
2075         var t = this.findTargetItem(e);
2076         if(!t || t.isContainer){
2077             return;
2078         }
2079         Roo.log(e);
2080         /*
2081         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2082             if(t == this.activeItem && t.shouldDeactivate(e)){
2083                 this.activeItem.deactivate();
2084                 delete this.activeItem;
2085                 return;
2086             }
2087             if(t.canActivate){
2088                 this.setActiveItem(t, true);
2089             }
2090             return;
2091             
2092             
2093         }
2094         */
2095        
2096         Roo.log('pass click event');
2097         
2098         t.onClick(e);
2099         
2100         this.fireEvent("click", this, t, e);
2101         
2102         var _this = this;
2103         
2104         (function() { _this.hide(); }).defer(100);
2105     },
2106     
2107     onMouseOver : function(e){
2108         var t  = this.findTargetItem(e);
2109         //Roo.log(t);
2110         //if(t){
2111         //    if(t.canActivate && !t.disabled){
2112         //        this.setActiveItem(t, true);
2113         //    }
2114         //}
2115         
2116         this.fireEvent("mouseover", this, e, t);
2117     },
2118     isVisible : function(){
2119         return !this.hidden;
2120     },
2121      onMouseOut : function(e){
2122         var t  = this.findTargetItem(e);
2123         
2124         //if(t ){
2125         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2126         //        this.activeItem.deactivate();
2127         //        delete this.activeItem;
2128         //    }
2129         //}
2130         this.fireEvent("mouseout", this, e, t);
2131     },
2132     
2133     
2134     /**
2135      * Displays this menu relative to another element
2136      * @param {String/HTMLElement/Roo.Element} element The element to align to
2137      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138      * the element (defaults to this.defaultAlign)
2139      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2140      */
2141     show : function(el, pos, parentMenu){
2142         this.parentMenu = parentMenu;
2143         if(!this.el){
2144             this.render();
2145         }
2146         this.fireEvent("beforeshow", this);
2147         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2148     },
2149      /**
2150      * Displays this menu at a specific xy position
2151      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2153      */
2154     showAt : function(xy, parentMenu, /* private: */_e){
2155         this.parentMenu = parentMenu;
2156         if(!this.el){
2157             this.render();
2158         }
2159         if(_e !== false){
2160             this.fireEvent("beforeshow", this);
2161             //xy = this.el.adjustForConstraints(xy);
2162         }
2163         
2164         //this.el.show();
2165         this.hideMenuItems();
2166         this.hidden = false;
2167         this.triggerEl.addClass('open');
2168         
2169         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2171         }
2172         
2173         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2174             this.el.setXY(xy);
2175         }
2176         
2177         this.focus();
2178         this.fireEvent("show", this);
2179     },
2180     
2181     focus : function(){
2182         return;
2183         if(!this.hidden){
2184             this.doFocus.defer(50, this);
2185         }
2186     },
2187
2188     doFocus : function(){
2189         if(!this.hidden){
2190             this.focusEl.focus();
2191         }
2192     },
2193
2194     /**
2195      * Hides this menu and optionally all parent menus
2196      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2197      */
2198     hide : function(deep)
2199     {
2200         
2201         this.hideMenuItems();
2202         if(this.el && this.isVisible()){
2203             this.fireEvent("beforehide", this);
2204             if(this.activeItem){
2205                 this.activeItem.deactivate();
2206                 this.activeItem = null;
2207             }
2208             this.triggerEl.removeClass('open');;
2209             this.hidden = true;
2210             this.fireEvent("hide", this);
2211         }
2212         if(deep === true && this.parentMenu){
2213             this.parentMenu.hide(true);
2214         }
2215     },
2216     
2217     onTriggerClick : function(e)
2218     {
2219         Roo.log('trigger click');
2220         
2221         var target = e.getTarget();
2222         
2223         Roo.log(target.nodeName.toLowerCase());
2224         
2225         if(target.nodeName.toLowerCase() === 'i'){
2226             e.preventDefault();
2227         }
2228         
2229     },
2230     
2231     onTriggerPress  : function(e)
2232     {
2233         Roo.log('trigger press');
2234         //Roo.log(e.getTarget());
2235        // Roo.log(this.triggerEl.dom);
2236        
2237         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238         var pel = Roo.get(e.getTarget());
2239         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240             Roo.log('is treeview or dropdown?');
2241             return;
2242         }
2243         
2244         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2245             return;
2246         }
2247         
2248         if (this.isVisible()) {
2249             Roo.log('hide');
2250             this.hide();
2251         } else {
2252             Roo.log('show');
2253             this.show(this.triggerEl, false, false);
2254         }
2255         
2256         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2257             e.stopEvent();
2258         }
2259         
2260     },
2261        
2262     
2263     hideMenuItems : function()
2264     {
2265         Roo.log("hide Menu Items");
2266         if (!this.el) { 
2267             return;
2268         }
2269         //$(backdrop).remove()
2270         this.el.select('.open',true).each(function(aa) {
2271             
2272             aa.removeClass('open');
2273           //var parent = getParent($(this))
2274           //var relatedTarget = { relatedTarget: this }
2275           
2276            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277           //if (e.isDefaultPrevented()) return
2278            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2279         });
2280     },
2281     addxtypeChild : function (tree, cntr) {
2282         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2283           
2284         this.menuitems.add(comp);
2285         return comp;
2286
2287     },
2288     getEl : function()
2289     {
2290         Roo.log(this.el);
2291         return this.el;
2292     }
2293 });
2294
2295  
2296  /*
2297  * - LGPL
2298  *
2299  * menu item
2300  * 
2301  */
2302
2303
2304 /**
2305  * @class Roo.bootstrap.MenuItem
2306  * @extends Roo.bootstrap.Component
2307  * Bootstrap MenuItem class
2308  * @cfg {String} html the menu label
2309  * @cfg {String} href the link
2310  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2313  * @cfg {String} fa favicon to show on left of menu item.
2314  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2315  * 
2316  * 
2317  * @constructor
2318  * Create a new MenuItem
2319  * @param {Object} config The config object
2320  */
2321
2322
2323 Roo.bootstrap.MenuItem = function(config){
2324     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2325     this.addEvents({
2326         // raw events
2327         /**
2328          * @event click
2329          * The raw click event for the entire grid.
2330          * @param {Roo.bootstrap.MenuItem} this
2331          * @param {Roo.EventObject} e
2332          */
2333         "click" : true
2334     });
2335 };
2336
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2338     
2339     href : false,
2340     html : false,
2341     preventDefault: false,
2342     isContainer : false,
2343     active : false,
2344     fa: false,
2345     
2346     getAutoCreate : function(){
2347         
2348         if(this.isContainer){
2349             return {
2350                 tag: 'li',
2351                 cls: 'dropdown-menu-item'
2352             };
2353         }
2354         var ctag = {
2355             tag: 'span',
2356             html: 'Link'
2357         };
2358         
2359         var anc = {
2360             tag : 'a',
2361             href : '#',
2362             cn : [  ]
2363         };
2364         
2365         if (this.fa !== false) {
2366             anc.cn.push({
2367                 tag : 'i',
2368                 cls : 'fa fa-' + this.fa
2369             });
2370         }
2371         
2372         anc.cn.push(ctag);
2373         
2374         
2375         var cfg= {
2376             tag: 'li',
2377             cls: 'dropdown-menu-item',
2378             cn: [ anc ]
2379         };
2380         if (this.parent().type == 'treeview') {
2381             cfg.cls = 'treeview-menu';
2382         }
2383         if (this.active) {
2384             cfg.cls += ' active';
2385         }
2386         
2387         
2388         
2389         anc.href = this.href || cfg.cn[0].href ;
2390         ctag.html = this.html || cfg.cn[0].html ;
2391         return cfg;
2392     },
2393     
2394     initEvents: function()
2395     {
2396         if (this.parent().type == 'treeview') {
2397             this.el.select('a').on('click', this.onClick, this);
2398         }
2399         if (this.menu) {
2400             this.menu.parentType = this.xtype;
2401             this.menu.triggerEl = this.el;
2402             this.menu = this.addxtype(Roo.apply({}, this.menu));
2403         }
2404         
2405     },
2406     onClick : function(e)
2407     {
2408         Roo.log('item on click ');
2409         
2410         if(this.preventDefault){
2411             e.preventDefault();
2412         }
2413         //this.parent().hideMenuItems();
2414         
2415         this.fireEvent('click', this, e);
2416     },
2417     getEl : function()
2418     {
2419         return this.el;
2420     } 
2421 });
2422
2423  
2424
2425  /*
2426  * - LGPL
2427  *
2428  * menu separator
2429  * 
2430  */
2431
2432
2433 /**
2434  * @class Roo.bootstrap.MenuSeparator
2435  * @extends Roo.bootstrap.Component
2436  * Bootstrap MenuSeparator class
2437  * 
2438  * @constructor
2439  * Create a new MenuItem
2440  * @param {Object} config The config object
2441  */
2442
2443
2444 Roo.bootstrap.MenuSeparator = function(config){
2445     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2446 };
2447
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2449     
2450     getAutoCreate : function(){
2451         var cfg = {
2452             cls: 'divider',
2453             tag : 'li'
2454         };
2455         
2456         return cfg;
2457     }
2458    
2459 });
2460
2461  
2462
2463  
2464 /*
2465 * Licence: LGPL
2466 */
2467
2468 /**
2469  * @class Roo.bootstrap.Modal
2470  * @extends Roo.bootstrap.Component
2471  * Bootstrap Modal class
2472  * @cfg {String} title Title of dialog
2473  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2475  * @cfg {Boolean} specificTitle default false
2476  * @cfg {Array} buttons Array of buttons or standard button set..
2477  * @cfg {String} buttonPosition (left|right|center) default right
2478  * @cfg {Boolean} animate default true
2479  * @cfg {Boolean} allow_close default true
2480  * @cfg {Boolean} fitwindow default false
2481  * @cfg {String} size (sm|lg) default empty
2482  *
2483  *
2484  * @constructor
2485  * Create a new Modal Dialog
2486  * @param {Object} config The config object
2487  */
2488
2489 Roo.bootstrap.Modal = function(config){
2490     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event btnclick
2495          * The raw btnclick event for the button
2496          * @param {Roo.EventObject} e
2497          */
2498         "btnclick" : true
2499     });
2500     this.buttons = this.buttons || [];
2501
2502     if (this.tmpl) {
2503         this.tmpl = Roo.factory(this.tmpl);
2504     }
2505
2506 };
2507
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2509
2510     title : 'test dialog',
2511
2512     buttons : false,
2513
2514     // set on load...
2515
2516     html: false,
2517
2518     tmp: false,
2519
2520     specificTitle: false,
2521
2522     buttonPosition: 'right',
2523
2524     allow_close : true,
2525
2526     animate : true,
2527
2528     fitwindow: false,
2529
2530
2531      // private
2532     dialogEl: false,
2533     bodyEl:  false,
2534     footerEl:  false,
2535     titleEl:  false,
2536     closeEl:  false,
2537
2538     size: '',
2539
2540
2541     onRender : function(ct, position)
2542     {
2543         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2544
2545         if(!this.el){
2546             var cfg = Roo.apply({},  this.getAutoCreate());
2547             cfg.id = Roo.id();
2548             //if(!cfg.name){
2549             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2550             //}
2551             //if (!cfg.name.length) {
2552             //    delete cfg.name;
2553            // }
2554             if (this.cls) {
2555                 cfg.cls += ' ' + this.cls;
2556             }
2557             if (this.style) {
2558                 cfg.style = this.style;
2559             }
2560             this.el = Roo.get(document.body).createChild(cfg, position);
2561         }
2562         //var type = this.el.dom.type;
2563
2564
2565         if(this.tabIndex !== undefined){
2566             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2567         }
2568
2569         this.dialogEl = this.el.select('.modal-dialog',true).first();
2570         this.bodyEl = this.el.select('.modal-body',true).first();
2571         this.closeEl = this.el.select('.modal-header .close', true).first();
2572         this.headerEl = this.el.select('.modal-header',true).first();
2573         this.titleEl = this.el.select('.modal-title',true).first();
2574         this.footerEl = this.el.select('.modal-footer',true).first();
2575
2576         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577         this.maskEl.enableDisplayMode("block");
2578         this.maskEl.hide();
2579         //this.el.addClass("x-dlg-modal");
2580
2581         if (this.buttons.length) {
2582             Roo.each(this.buttons, function(bb) {
2583                 var b = Roo.apply({}, bb);
2584                 b.xns = b.xns || Roo.bootstrap;
2585                 b.xtype = b.xtype || 'Button';
2586                 if (typeof(b.listeners) == 'undefined') {
2587                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2588                 }
2589
2590                 var btn = Roo.factory(b);
2591
2592                 btn.render(this.el.select('.modal-footer div').first());
2593
2594             },this);
2595         }
2596         // render the children.
2597         var nitems = [];
2598
2599         if(typeof(this.items) != 'undefined'){
2600             var items = this.items;
2601             delete this.items;
2602
2603             for(var i =0;i < items.length;i++) {
2604                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2605             }
2606         }
2607
2608         this.items = nitems;
2609
2610         // where are these used - they used to be body/close/footer
2611
2612
2613         this.initEvents();
2614         //this.el.addClass([this.fieldClass, this.cls]);
2615
2616     },
2617
2618     getAutoCreate : function(){
2619
2620
2621         var bdy = {
2622                 cls : 'modal-body',
2623                 html : this.html || ''
2624         };
2625
2626         var title = {
2627             tag: 'h4',
2628             cls : 'modal-title',
2629             html : this.title
2630         };
2631
2632         if(this.specificTitle){
2633             title = this.title;
2634
2635         };
2636
2637         var header = [];
2638         if (this.allow_close) {
2639             header.push({
2640                 tag: 'button',
2641                 cls : 'close',
2642                 html : '&times'
2643             });
2644         }
2645
2646         header.push(title);
2647
2648         var size = '';
2649
2650         if(this.size.length){
2651             size = 'modal-' + this.size;
2652         }
2653
2654         var modal = {
2655             cls: "modal",
2656             style : 'display: none',
2657             cn : [
2658                 {
2659                     cls: "modal-dialog " + size,
2660                     cn : [
2661                         {
2662                             cls : "modal-content",
2663                             cn : [
2664                                 {
2665                                     cls : 'modal-header',
2666                                     cn : header
2667                                 },
2668                                 bdy,
2669                                 {
2670                                     cls : 'modal-footer',
2671                                     cn : [
2672                                         {
2673                                             tag: 'div',
2674                                             cls: 'btn-' + this.buttonPosition
2675                                         }
2676                                     ]
2677
2678                                 }
2679
2680
2681                             ]
2682
2683                         }
2684                     ]
2685
2686                 }
2687             ]
2688         };
2689
2690         if(this.animate){
2691             modal.cls += ' fade';
2692         }
2693
2694         return modal;
2695
2696     },
2697     getChildContainer : function() {
2698
2699          return this.bodyEl;
2700
2701     },
2702     getButtonContainer : function() {
2703          return this.el.select('.modal-footer div',true).first();
2704
2705     },
2706     initEvents : function()
2707     {
2708         if (this.allow_close) {
2709             this.closeEl.on('click', this.hide, this);
2710         }
2711         Roo.EventManager.onWindowResize(this.resize, this, true);
2712
2713
2714     },
2715
2716     resize : function()
2717     {
2718         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2719         if (this.fitwindow) {
2720             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2722             this.setSize(w,h);
2723         }
2724     },
2725
2726     setSize : function(w,h)
2727     {
2728         if (!w && !h) {
2729             return;
2730         }
2731         this.resizeTo(w,h);
2732     },
2733
2734     show : function() {
2735
2736         if (!this.rendered) {
2737             this.render();
2738         }
2739
2740         this.el.setStyle('display', 'block');
2741
2742         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2743             var _this = this;
2744             (function(){
2745                 this.el.addClass('in');
2746             }).defer(50, this);
2747         }else{
2748             this.el.addClass('in');
2749
2750         }
2751
2752         // not sure how we can show data in here..
2753         //if (this.tmpl) {
2754         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2755         //}
2756
2757         Roo.get(document.body).addClass("x-body-masked");
2758         
2759         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2760         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2761         this.maskEl.show();
2762         
2763         this.resize();
2764         
2765         this.fireEvent('show', this);
2766
2767         // set zindex here - otherwise it appears to be ignored...
2768         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2769
2770         (function () {
2771             this.items.forEach( function(e) {
2772                 e.layout ? e.layout() : false;
2773
2774             });
2775         }).defer(100,this);
2776
2777     },
2778     hide : function()
2779     {
2780         if(this.fireEvent("beforehide", this) !== false){
2781             this.maskEl.hide();
2782             Roo.get(document.body).removeClass("x-body-masked");
2783             this.el.removeClass('in');
2784             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2785
2786             if(this.animate){ // why
2787                 var _this = this;
2788                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2789             }else{
2790                 this.el.setStyle('display', 'none');
2791             }
2792             this.fireEvent('hide', this);
2793         }
2794     },
2795
2796     addButton : function(str, cb)
2797     {
2798
2799
2800         var b = Roo.apply({}, { html : str } );
2801         b.xns = b.xns || Roo.bootstrap;
2802         b.xtype = b.xtype || 'Button';
2803         if (typeof(b.listeners) == 'undefined') {
2804             b.listeners = { click : cb.createDelegate(this)  };
2805         }
2806
2807         var btn = Roo.factory(b);
2808
2809         btn.render(this.el.select('.modal-footer div').first());
2810
2811         return btn;
2812
2813     },
2814
2815     setDefaultButton : function(btn)
2816     {
2817         //this.el.select('.modal-footer').()
2818     },
2819     diff : false,
2820
2821     resizeTo: function(w,h)
2822     {
2823         // skip.. ?? why??
2824
2825         this.dialogEl.setWidth(w);
2826         if (this.diff === false) {
2827             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2828         }
2829
2830         this.bodyEl.setHeight(h-this.diff);
2831
2832
2833     },
2834     setContentSize  : function(w, h)
2835     {
2836
2837     },
2838     onButtonClick: function(btn,e)
2839     {
2840         //Roo.log([a,b,c]);
2841         this.fireEvent('btnclick', btn.name, e);
2842     },
2843      /**
2844      * Set the title of the Dialog
2845      * @param {String} str new Title
2846      */
2847     setTitle: function(str) {
2848         this.titleEl.dom.innerHTML = str;
2849     },
2850     /**
2851      * Set the body of the Dialog
2852      * @param {String} str new Title
2853      */
2854     setBody: function(str) {
2855         this.bodyEl.dom.innerHTML = str;
2856     },
2857     /**
2858      * Set the body of the Dialog using the template
2859      * @param {Obj} data - apply this data to the template and replace the body contents.
2860      */
2861     applyBody: function(obj)
2862     {
2863         if (!this.tmpl) {
2864             Roo.log("Error - using apply Body without a template");
2865             //code
2866         }
2867         this.tmpl.overwrite(this.bodyEl, obj);
2868     }
2869
2870 });
2871
2872
2873 Roo.apply(Roo.bootstrap.Modal,  {
2874     /**
2875          * Button config that displays a single OK button
2876          * @type Object
2877          */
2878         OK :  [{
2879             name : 'ok',
2880             weight : 'primary',
2881             html : 'OK'
2882         }],
2883         /**
2884          * Button config that displays Yes and No buttons
2885          * @type Object
2886          */
2887         YESNO : [
2888             {
2889                 name  : 'no',
2890                 html : 'No'
2891             },
2892             {
2893                 name  :'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             }
2897         ],
2898
2899         /**
2900          * Button config that displays OK and Cancel buttons
2901          * @type Object
2902          */
2903         OKCANCEL : [
2904             {
2905                name : 'cancel',
2906                 html : 'Cancel'
2907             },
2908             {
2909                 name : 'ok',
2910                 weight : 'primary',
2911                 html : 'OK'
2912             }
2913         ],
2914         /**
2915          * Button config that displays Yes, No and Cancel buttons
2916          * @type Object
2917          */
2918         YESNOCANCEL : [
2919             {
2920                 name : 'yes',
2921                 weight : 'primary',
2922                 html : 'Yes'
2923             },
2924             {
2925                 name : 'no',
2926                 html : 'No'
2927             },
2928             {
2929                 name : 'cancel',
2930                 html : 'Cancel'
2931             }
2932         ],
2933         
2934         zIndex : 10001
2935 });
2936 /*
2937  * - LGPL
2938  *
2939  * messagebox - can be used as a replace
2940  * 
2941  */
2942 /**
2943  * @class Roo.MessageBox
2944  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2945  * Example usage:
2946  *<pre><code>
2947 // Basic alert:
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2949
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2952     if (btn == 'ok'){
2953         // process text value...
2954     }
2955 });
2956
2957 // Show a dialog using config options:
2958 Roo.Msg.show({
2959    title:'Save Changes?',
2960    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961    buttons: Roo.Msg.YESNOCANCEL,
2962    fn: processResult,
2963    animEl: 'elId'
2964 });
2965 </code></pre>
2966  * @singleton
2967  */
2968 Roo.bootstrap.MessageBox = function(){
2969     var dlg, opt, mask, waitTimer;
2970     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971     var buttons, activeTextEl, bwidth;
2972
2973     
2974     // private
2975     var handleButton = function(button){
2976         dlg.hide();
2977         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2978     };
2979
2980     // private
2981     var handleHide = function(){
2982         if(opt && opt.cls){
2983             dlg.el.removeClass(opt.cls);
2984         }
2985         //if(waitTimer){
2986         //    Roo.TaskMgr.stop(waitTimer);
2987         //    waitTimer = null;
2988         //}
2989     };
2990
2991     // private
2992     var updateButtons = function(b){
2993         var width = 0;
2994         if(!b){
2995             buttons["ok"].hide();
2996             buttons["cancel"].hide();
2997             buttons["yes"].hide();
2998             buttons["no"].hide();
2999             //dlg.footer.dom.style.display = 'none';
3000             return width;
3001         }
3002         dlg.footerEl.dom.style.display = '';
3003         for(var k in buttons){
3004             if(typeof buttons[k] != "function"){
3005                 if(b[k]){
3006                     buttons[k].show();
3007                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008                     width += buttons[k].el.getWidth()+15;
3009                 }else{
3010                     buttons[k].hide();
3011                 }
3012             }
3013         }
3014         return width;
3015     };
3016
3017     // private
3018     var handleEsc = function(d, k, e){
3019         if(opt && opt.closable !== false){
3020             dlg.hide();
3021         }
3022         if(e){
3023             e.stopEvent();
3024         }
3025     };
3026
3027     return {
3028         /**
3029          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030          * @return {Roo.BasicDialog} The BasicDialog element
3031          */
3032         getDialog : function(){
3033            if(!dlg){
3034                 dlg = new Roo.bootstrap.Modal( {
3035                     //draggable: true,
3036                     //resizable:false,
3037                     //constraintoviewport:false,
3038                     //fixedcenter:true,
3039                     //collapsible : false,
3040                     //shim:true,
3041                     //modal: true,
3042                   //  width:400,
3043                   //  height:100,
3044                     //buttonAlign:"center",
3045                     closeClick : function(){
3046                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3047                             handleButton("no");
3048                         }else{
3049                             handleButton("cancel");
3050                         }
3051                     }
3052                 });
3053                 dlg.render();
3054                 dlg.on("hide", handleHide);
3055                 mask = dlg.mask;
3056                 //dlg.addKeyListener(27, handleEsc);
3057                 buttons = {};
3058                 this.buttons = buttons;
3059                 var bt = this.buttonText;
3060                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3061                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3062                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3063                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3064                 //Roo.log(buttons);
3065                 bodyEl = dlg.bodyEl.createChild({
3066
3067                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3068                         '<textarea class="roo-mb-textarea"></textarea>' +
3069                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3070                 });
3071                 msgEl = bodyEl.dom.firstChild;
3072                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3073                 textboxEl.enableDisplayMode();
3074                 textboxEl.addKeyListener([10,13], function(){
3075                     if(dlg.isVisible() && opt && opt.buttons){
3076                         if(opt.buttons.ok){
3077                             handleButton("ok");
3078                         }else if(opt.buttons.yes){
3079                             handleButton("yes");
3080                         }
3081                     }
3082                 });
3083                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3084                 textareaEl.enableDisplayMode();
3085                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3086                 progressEl.enableDisplayMode();
3087                 
3088                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3089                 //var pf = progressEl.dom.firstChild;
3090                 //if (pf) {
3091                     //pp = Roo.get(pf.firstChild);
3092                     //pp.setHeight(pf.offsetHeight);
3093                 //}
3094                 
3095             }
3096             return dlg;
3097         },
3098
3099         /**
3100          * Updates the message box body text
3101          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3102          * the XHTML-compliant non-breaking space character '&amp;#160;')
3103          * @return {Roo.MessageBox} This message box
3104          */
3105         updateText : function(text)
3106         {
3107             if(!dlg.isVisible() && !opt.width){
3108                 dlg.dialogEl.setWidth(this.maxWidth);
3109                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3110             }
3111             msgEl.innerHTML = text || '&#160;';
3112       
3113             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3114             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3115             var w = Math.max(
3116                     Math.min(opt.width || cw , this.maxWidth), 
3117                     Math.max(opt.minWidth || this.minWidth, bwidth)
3118             );
3119             if(opt.prompt){
3120                 activeTextEl.setWidth(w);
3121             }
3122             if(dlg.isVisible()){
3123                 dlg.fixedcenter = false;
3124             }
3125             // to big, make it scroll. = But as usual stupid IE does not support
3126             // !important..
3127             
3128             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3129                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3130                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3131             } else {
3132                 bodyEl.dom.style.height = '';
3133                 bodyEl.dom.style.overflowY = '';
3134             }
3135             if (cw > w) {
3136                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3137             } else {
3138                 bodyEl.dom.style.overflowX = '';
3139             }
3140             
3141             dlg.setContentSize(w, bodyEl.getHeight());
3142             if(dlg.isVisible()){
3143                 dlg.fixedcenter = true;
3144             }
3145             return this;
3146         },
3147
3148         /**
3149          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3150          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3151          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3152          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3153          * @return {Roo.MessageBox} This message box
3154          */
3155         updateProgress : function(value, text){
3156             if(text){
3157                 this.updateText(text);
3158             }
3159             if (pp) { // weird bug on my firefox - for some reason this is not defined
3160                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3161             }
3162             return this;
3163         },        
3164
3165         /**
3166          * Returns true if the message box is currently displayed
3167          * @return {Boolean} True if the message box is visible, else false
3168          */
3169         isVisible : function(){
3170             return dlg && dlg.isVisible();  
3171         },
3172
3173         /**
3174          * Hides the message box if it is displayed
3175          */
3176         hide : function(){
3177             if(this.isVisible()){
3178                 dlg.hide();
3179             }  
3180         },
3181
3182         /**
3183          * Displays a new message box, or reinitializes an existing message box, based on the config options
3184          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3185          * The following config object properties are supported:
3186          * <pre>
3187 Property    Type             Description
3188 ----------  ---------------  ------------------------------------------------------------------------------------
3189 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3190                                    closes (defaults to undefined)
3191 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3192                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3193 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3194                                    progress and wait dialogs will ignore this property and always hide the
3195                                    close button as they can only be closed programmatically.
3196 cls               String           A custom CSS class to apply to the message box element
3197 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3198                                    displayed (defaults to 75)
3199 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3200                                    function will be btn (the name of the button that was clicked, if applicable,
3201                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3202                                    Progress and wait dialogs will ignore this option since they do not respond to
3203                                    user actions and can only be closed programmatically, so any required function
3204                                    should be called by the same code after it closes the dialog.
3205 icon              String           A CSS class that provides a background image to be used as an icon for
3206                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3207 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3208 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3209 modal             Boolean          False to allow user interaction with the page while the message box is
3210                                    displayed (defaults to true)
3211 msg               String           A string that will replace the existing message box body text (defaults
3212                                    to the XHTML-compliant non-breaking space character '&#160;')
3213 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3214 progress          Boolean          True to display a progress bar (defaults to false)
3215 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3216 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3217 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3218 title             String           The title text
3219 value             String           The string value to set into the active textbox element if displayed
3220 wait              Boolean          True to display a progress bar (defaults to false)
3221 width             Number           The width of the dialog in pixels
3222 </pre>
3223          *
3224          * Example usage:
3225          * <pre><code>
3226 Roo.Msg.show({
3227    title: 'Address',
3228    msg: 'Please enter your address:',
3229    width: 300,
3230    buttons: Roo.MessageBox.OKCANCEL,
3231    multiline: true,
3232    fn: saveAddress,
3233    animEl: 'addAddressBtn'
3234 });
3235 </code></pre>
3236          * @param {Object} config Configuration options
3237          * @return {Roo.MessageBox} This message box
3238          */
3239         show : function(options)
3240         {
3241             
3242             // this causes nightmares if you show one dialog after another
3243             // especially on callbacks..
3244              
3245             if(this.isVisible()){
3246                 
3247                 this.hide();
3248                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3249                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3250                 Roo.log("New Dialog Message:" +  options.msg )
3251                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3252                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3253                 
3254             }
3255             var d = this.getDialog();
3256             opt = options;
3257             d.setTitle(opt.title || "&#160;");
3258             d.closeEl.setDisplayed(opt.closable !== false);
3259             activeTextEl = textboxEl;
3260             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3261             if(opt.prompt){
3262                 if(opt.multiline){
3263                     textboxEl.hide();
3264                     textareaEl.show();
3265                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3266                         opt.multiline : this.defaultTextHeight);
3267                     activeTextEl = textareaEl;
3268                 }else{
3269                     textboxEl.show();
3270                     textareaEl.hide();
3271                 }
3272             }else{
3273                 textboxEl.hide();
3274                 textareaEl.hide();
3275             }
3276             progressEl.setDisplayed(opt.progress === true);
3277             this.updateProgress(0);
3278             activeTextEl.dom.value = opt.value || "";
3279             if(opt.prompt){
3280                 dlg.setDefaultButton(activeTextEl);
3281             }else{
3282                 var bs = opt.buttons;
3283                 var db = null;
3284                 if(bs && bs.ok){
3285                     db = buttons["ok"];
3286                 }else if(bs && bs.yes){
3287                     db = buttons["yes"];
3288                 }
3289                 dlg.setDefaultButton(db);
3290             }
3291             bwidth = updateButtons(opt.buttons);
3292             this.updateText(opt.msg);
3293             if(opt.cls){
3294                 d.el.addClass(opt.cls);
3295             }
3296             d.proxyDrag = opt.proxyDrag === true;
3297             d.modal = opt.modal !== false;
3298             d.mask = opt.modal !== false ? mask : false;
3299             if(!d.isVisible()){
3300                 // force it to the end of the z-index stack so it gets a cursor in FF
3301                 document.body.appendChild(dlg.el.dom);
3302                 d.animateTarget = null;
3303                 d.show(options.animEl);
3304             }
3305             return this;
3306         },
3307
3308         /**
3309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3311          * and closing the message box when the process is complete.
3312          * @param {String} title The title bar text
3313          * @param {String} msg The message box body text
3314          * @return {Roo.MessageBox} This message box
3315          */
3316         progress : function(title, msg){
3317             this.show({
3318                 title : title,
3319                 msg : msg,
3320                 buttons: false,
3321                 progress:true,
3322                 closable:false,
3323                 minWidth: this.minProgressWidth,
3324                 modal : true
3325             });
3326             return this;
3327         },
3328
3329         /**
3330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3331          * If a callback function is passed it will be called after the user clicks the button, and the
3332          * id of the button that was clicked will be passed as the only parameter to the callback
3333          * (could also be the top-right close button).
3334          * @param {String} title The title bar text
3335          * @param {String} msg The message box body text
3336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3337          * @param {Object} scope (optional) The scope of the callback function
3338          * @return {Roo.MessageBox} This message box
3339          */
3340         alert : function(title, msg, fn, scope)
3341         {
3342             this.show({
3343                 title : title,
3344                 msg : msg,
3345                 buttons: this.OK,
3346                 fn: fn,
3347                 closable : false,
3348                 scope : scope,
3349                 modal : true
3350             });
3351             return this;
3352         },
3353
3354         /**
3355          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3356          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3357          * You are responsible for closing the message box when the process is complete.
3358          * @param {String} msg The message box body text
3359          * @param {String} title (optional) The title bar text
3360          * @return {Roo.MessageBox} This message box
3361          */
3362         wait : function(msg, title){
3363             this.show({
3364                 title : title,
3365                 msg : msg,
3366                 buttons: false,
3367                 closable:false,
3368                 progress:true,
3369                 modal:true,
3370                 width:300,
3371                 wait:true
3372             });
3373             waitTimer = Roo.TaskMgr.start({
3374                 run: function(i){
3375                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3376                 },
3377                 interval: 1000
3378             });
3379             return this;
3380         },
3381
3382         /**
3383          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3384          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3385          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3386          * @param {String} title The title bar text
3387          * @param {String} msg The message box body text
3388          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3389          * @param {Object} scope (optional) The scope of the callback function
3390          * @return {Roo.MessageBox} This message box
3391          */
3392         confirm : function(title, msg, fn, scope){
3393             this.show({
3394                 title : title,
3395                 msg : msg,
3396                 buttons: this.YESNO,
3397                 fn: fn,
3398                 scope : scope,
3399                 modal : true
3400             });
3401             return this;
3402         },
3403
3404         /**
3405          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3406          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3407          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3408          * (could also be the top-right close button) and the text that was entered will be passed as the two
3409          * parameters to the callback.
3410          * @param {String} title The title bar text
3411          * @param {String} msg The message box body text
3412          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3413          * @param {Object} scope (optional) The scope of the callback function
3414          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3415          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3416          * @return {Roo.MessageBox} This message box
3417          */
3418         prompt : function(title, msg, fn, scope, multiline){
3419             this.show({
3420                 title : title,
3421                 msg : msg,
3422                 buttons: this.OKCANCEL,
3423                 fn: fn,
3424                 minWidth:250,
3425                 scope : scope,
3426                 prompt:true,
3427                 multiline: multiline,
3428                 modal : true
3429             });
3430             return this;
3431         },
3432
3433         /**
3434          * Button config that displays a single OK button
3435          * @type Object
3436          */
3437         OK : {ok:true},
3438         /**
3439          * Button config that displays Yes and No buttons
3440          * @type Object
3441          */
3442         YESNO : {yes:true, no:true},
3443         /**
3444          * Button config that displays OK and Cancel buttons
3445          * @type Object
3446          */
3447         OKCANCEL : {ok:true, cancel:true},
3448         /**
3449          * Button config that displays Yes, No and Cancel buttons
3450          * @type Object
3451          */
3452         YESNOCANCEL : {yes:true, no:true, cancel:true},
3453
3454         /**
3455          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3456          * @type Number
3457          */
3458         defaultTextHeight : 75,
3459         /**
3460          * The maximum width in pixels of the message box (defaults to 600)
3461          * @type Number
3462          */
3463         maxWidth : 600,
3464         /**
3465          * The minimum width in pixels of the message box (defaults to 100)
3466          * @type Number
3467          */
3468         minWidth : 100,
3469         /**
3470          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3471          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3472          * @type Number
3473          */
3474         minProgressWidth : 250,
3475         /**
3476          * An object containing the default button text strings that can be overriden for localized language support.
3477          * Supported properties are: ok, cancel, yes and no.
3478          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3479          * @type Object
3480          */
3481         buttonText : {
3482             ok : "OK",
3483             cancel : "Cancel",
3484             yes : "Yes",
3485             no : "No"
3486         }
3487     };
3488 }();
3489
3490 /**
3491  * Shorthand for {@link Roo.MessageBox}
3492  */
3493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3494 Roo.Msg = Roo.Msg || Roo.MessageBox;
3495 /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.Navbar
3504  * @extends Roo.bootstrap.Component
3505  * Bootstrap Navbar class
3506
3507  * @constructor
3508  * Create a new Navbar
3509  * @param {Object} config The config object
3510  */
3511
3512
3513 Roo.bootstrap.Navbar = function(config){
3514     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3515     this.addEvents({
3516         // raw events
3517         /**
3518          * @event beforetoggle
3519          * Fire before toggle the menu
3520          * @param {Roo.EventObject} e
3521          */
3522         "beforetoggle" : true
3523     });
3524 };
3525
3526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3527     
3528     
3529    
3530     // private
3531     navItems : false,
3532     loadMask : false,
3533     
3534     
3535     getAutoCreate : function(){
3536         
3537         
3538         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3539         
3540     },
3541     
3542     initEvents :function ()
3543     {
3544         //Roo.log(this.el.select('.navbar-toggle',true));
3545         this.el.select('.navbar-toggle',true).on('click', function() {
3546             if(this.fireEvent('beforetoggle', this) !== false){
3547                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3548             }
3549             
3550         }, this);
3551         
3552         var mark = {
3553             tag: "div",
3554             cls:"x-dlg-mask"
3555         };
3556         
3557         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3558         
3559         var size = this.el.getSize();
3560         this.maskEl.setSize(size.width, size.height);
3561         this.maskEl.enableDisplayMode("block");
3562         this.maskEl.hide();
3563         
3564         if(this.loadMask){
3565             this.maskEl.show();
3566         }
3567     },
3568     
3569     
3570     getChildContainer : function()
3571     {
3572         if (this.el.select('.collapse').getCount()) {
3573             return this.el.select('.collapse',true).first();
3574         }
3575         
3576         return this.el;
3577     },
3578     
3579     mask : function()
3580     {
3581         this.maskEl.show();
3582     },
3583     
3584     unmask : function()
3585     {
3586         this.maskEl.hide();
3587     } 
3588     
3589     
3590     
3591     
3592 });
3593
3594
3595
3596  
3597
3598  /*
3599  * - LGPL
3600  *
3601  * navbar
3602  * 
3603  */
3604
3605 /**
3606  * @class Roo.bootstrap.NavSimplebar
3607  * @extends Roo.bootstrap.Navbar
3608  * Bootstrap Sidebar class
3609  *
3610  * @cfg {Boolean} inverse is inverted color
3611  * 
3612  * @cfg {String} type (nav | pills | tabs)
3613  * @cfg {Boolean} arrangement stacked | justified
3614  * @cfg {String} align (left | right) alignment
3615  * 
3616  * @cfg {Boolean} main (true|false) main nav bar? default false
3617  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3618  * 
3619  * @cfg {String} tag (header|footer|nav|div) default is nav 
3620
3621  * 
3622  * 
3623  * 
3624  * @constructor
3625  * Create a new Sidebar
3626  * @param {Object} config The config object
3627  */
3628
3629
3630 Roo.bootstrap.NavSimplebar = function(config){
3631     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3632 };
3633
3634 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3635     
3636     inverse: false,
3637     
3638     type: false,
3639     arrangement: '',
3640     align : false,
3641     
3642     
3643     
3644     main : false,
3645     
3646     
3647     tag : false,
3648     
3649     
3650     getAutoCreate : function(){
3651         
3652         
3653         var cfg = {
3654             tag : this.tag || 'div',
3655             cls : 'navbar'
3656         };
3657           
3658         
3659         cfg.cn = [
3660             {
3661                 cls: 'nav',
3662                 tag : 'ul'
3663             }
3664         ];
3665         
3666          
3667         this.type = this.type || 'nav';
3668         if (['tabs','pills'].indexOf(this.type)!==-1) {
3669             cfg.cn[0].cls += ' nav-' + this.type
3670         
3671         
3672         } else {
3673             if (this.type!=='nav') {
3674                 Roo.log('nav type must be nav/tabs/pills')
3675             }
3676             cfg.cn[0].cls += ' navbar-nav'
3677         }
3678         
3679         
3680         
3681         
3682         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3683             cfg.cn[0].cls += ' nav-' + this.arrangement;
3684         }
3685         
3686         
3687         if (this.align === 'right') {
3688             cfg.cn[0].cls += ' navbar-right';
3689         }
3690         
3691         if (this.inverse) {
3692             cfg.cls += ' navbar-inverse';
3693             
3694         }
3695         
3696         
3697         return cfg;
3698     
3699         
3700     }
3701     
3702     
3703     
3704 });
3705
3706
3707
3708  
3709
3710  
3711        /*
3712  * - LGPL
3713  *
3714  * navbar
3715  * 
3716  */
3717
3718 /**
3719  * @class Roo.bootstrap.NavHeaderbar
3720  * @extends Roo.bootstrap.NavSimplebar
3721  * Bootstrap Sidebar class
3722  *
3723  * @cfg {String} brand what is brand
3724  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3725  * @cfg {String} brand_href href of the brand
3726  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3727  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3728  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3729  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3730  * 
3731  * @constructor
3732  * Create a new Sidebar
3733  * @param {Object} config The config object
3734  */
3735
3736
3737 Roo.bootstrap.NavHeaderbar = function(config){
3738     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3739       
3740 };
3741
3742 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3743     
3744     position: '',
3745     brand: '',
3746     brand_href: false,
3747     srButton : true,
3748     autohide : false,
3749     desktopCenter : false,
3750    
3751     
3752     getAutoCreate : function(){
3753         
3754         var   cfg = {
3755             tag: this.nav || 'nav',
3756             cls: 'navbar',
3757             role: 'navigation',
3758             cn: []
3759         };
3760         
3761         var cn = cfg.cn;
3762         if (this.desktopCenter) {
3763             cn.push({cls : 'container', cn : []});
3764             cn = cn[0].cn;
3765         }
3766         
3767         if(this.srButton){
3768             cn.push({
3769                 tag: 'div',
3770                 cls: 'navbar-header',
3771                 cn: [
3772                     {
3773                         tag: 'button',
3774                         type: 'button',
3775                         cls: 'navbar-toggle',
3776                         'data-toggle': 'collapse',
3777                         cn: [
3778                             {
3779                                 tag: 'span',
3780                                 cls: 'sr-only',
3781                                 html: 'Toggle navigation'
3782                             },
3783                             {
3784                                 tag: 'span',
3785                                 cls: 'icon-bar'
3786                             },
3787                             {
3788                                 tag: 'span',
3789                                 cls: 'icon-bar'
3790                             },
3791                             {
3792                                 tag: 'span',
3793                                 cls: 'icon-bar'
3794                             }
3795                         ]
3796                     }
3797                 ]
3798             });
3799         }
3800         
3801         cn.push({
3802             tag: 'div',
3803             cls: 'collapse navbar-collapse',
3804             cn : []
3805         });
3806         
3807         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3808         
3809         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3810             cfg.cls += ' navbar-' + this.position;
3811             
3812             // tag can override this..
3813             
3814             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3815         }
3816         
3817         if (this.brand !== '') {
3818             cn[0].cn.push({
3819                 tag: 'a',
3820                 href: this.brand_href ? this.brand_href : '#',
3821                 cls: 'navbar-brand',
3822                 cn: [
3823                 this.brand
3824                 ]
3825             });
3826         }
3827         
3828         if(this.main){
3829             cfg.cls += ' main-nav';
3830         }
3831         
3832         
3833         return cfg;
3834
3835         
3836     },
3837     getHeaderChildContainer : function()
3838     {
3839         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3840             return this.el.select('.navbar-header',true).first();
3841         }
3842         
3843         return this.getChildContainer();
3844     },
3845     
3846     
3847     initEvents : function()
3848     {
3849         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3850         
3851         if (this.autohide) {
3852             
3853             var prevScroll = 0;
3854             var ft = this.el;
3855             
3856             Roo.get(document).on('scroll',function(e) {
3857                 var ns = Roo.get(document).getScroll().top;
3858                 var os = prevScroll;
3859                 prevScroll = ns;
3860                 
3861                 if(ns > os){
3862                     ft.removeClass('slideDown');
3863                     ft.addClass('slideUp');
3864                     return;
3865                 }
3866                 ft.removeClass('slideUp');
3867                 ft.addClass('slideDown');
3868                  
3869               
3870           },this);
3871         }
3872     }    
3873     
3874 });
3875
3876
3877
3878  
3879
3880  /*
3881  * - LGPL
3882  *
3883  * navbar
3884  * 
3885  */
3886
3887 /**
3888  * @class Roo.bootstrap.NavSidebar
3889  * @extends Roo.bootstrap.Navbar
3890  * Bootstrap Sidebar class
3891  * 
3892  * @constructor
3893  * Create a new Sidebar
3894  * @param {Object} config The config object
3895  */
3896
3897
3898 Roo.bootstrap.NavSidebar = function(config){
3899     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3900 };
3901
3902 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3903     
3904     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3905     
3906     getAutoCreate : function(){
3907         
3908         
3909         return  {
3910             tag: 'div',
3911             cls: 'sidebar sidebar-nav'
3912         };
3913     
3914         
3915     }
3916     
3917     
3918     
3919 });
3920
3921
3922
3923  
3924
3925  /*
3926  * - LGPL
3927  *
3928  * nav group
3929  * 
3930  */
3931
3932 /**
3933  * @class Roo.bootstrap.NavGroup
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap NavGroup class
3936  * @cfg {String} align (left|right)
3937  * @cfg {Boolean} inverse
3938  * @cfg {String} type (nav|pills|tab) default nav
3939  * @cfg {String} navId - reference Id for navbar.
3940
3941  * 
3942  * @constructor
3943  * Create a new nav group
3944  * @param {Object} config The config object
3945  */
3946
3947 Roo.bootstrap.NavGroup = function(config){
3948     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3949     this.navItems = [];
3950    
3951     Roo.bootstrap.NavGroup.register(this);
3952      this.addEvents({
3953         /**
3954              * @event changed
3955              * Fires when the active item changes
3956              * @param {Roo.bootstrap.NavGroup} this
3957              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3958              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3959          */
3960         'changed': true
3961      });
3962     
3963 };
3964
3965 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3966     
3967     align: '',
3968     inverse: false,
3969     form: false,
3970     type: 'nav',
3971     navId : '',
3972     // private
3973     
3974     navItems : false, 
3975     
3976     getAutoCreate : function()
3977     {
3978         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3979         
3980         cfg = {
3981             tag : 'ul',
3982             cls: 'nav' 
3983         };
3984         
3985         if (['tabs','pills'].indexOf(this.type)!==-1) {
3986             cfg.cls += ' nav-' + this.type
3987         } else {
3988             if (this.type!=='nav') {
3989                 Roo.log('nav type must be nav/tabs/pills')
3990             }
3991             cfg.cls += ' navbar-nav'
3992         }
3993         
3994         if (this.parent().sidebar) {
3995             cfg = {
3996                 tag: 'ul',
3997                 cls: 'dashboard-menu sidebar-menu'
3998             };
3999             
4000             return cfg;
4001         }
4002         
4003         if (this.form === true) {
4004             cfg = {
4005                 tag: 'form',
4006                 cls: 'navbar-form'
4007             };
4008             
4009             if (this.align === 'right') {
4010                 cfg.cls += ' navbar-right';
4011             } else {
4012                 cfg.cls += ' navbar-left';
4013             }
4014         }
4015         
4016         if (this.align === 'right') {
4017             cfg.cls += ' navbar-right';
4018         }
4019         
4020         if (this.inverse) {
4021             cfg.cls += ' navbar-inverse';
4022             
4023         }
4024         
4025         
4026         return cfg;
4027     },
4028     /**
4029     * sets the active Navigation item
4030     * @param {Roo.bootstrap.NavItem} the new current navitem
4031     */
4032     setActiveItem : function(item)
4033     {
4034         var prev = false;
4035         Roo.each(this.navItems, function(v){
4036             if (v == item) {
4037                 return ;
4038             }
4039             if (v.isActive()) {
4040                 v.setActive(false, true);
4041                 prev = v;
4042                 
4043             }
4044             
4045         });
4046
4047         item.setActive(true, true);
4048         this.fireEvent('changed', this, item, prev);
4049         
4050         
4051     },
4052     /**
4053     * gets the active Navigation item
4054     * @return {Roo.bootstrap.NavItem} the current navitem
4055     */
4056     getActive : function()
4057     {
4058         
4059         var prev = false;
4060         Roo.each(this.navItems, function(v){
4061             
4062             if (v.isActive()) {
4063                 prev = v;
4064                 
4065             }
4066             
4067         });
4068         return prev;
4069     },
4070     
4071     indexOfNav : function()
4072     {
4073         
4074         var prev = false;
4075         Roo.each(this.navItems, function(v,i){
4076             
4077             if (v.isActive()) {
4078                 prev = i;
4079                 
4080             }
4081             
4082         });
4083         return prev;
4084     },
4085     /**
4086     * adds a Navigation item
4087     * @param {Roo.bootstrap.NavItem} the navitem to add
4088     */
4089     addItem : function(cfg)
4090     {
4091         var cn = new Roo.bootstrap.NavItem(cfg);
4092         this.register(cn);
4093         cn.parentId = this.id;
4094         cn.onRender(this.el, null);
4095         return cn;
4096     },
4097     /**
4098     * register a Navigation item
4099     * @param {Roo.bootstrap.NavItem} the navitem to add
4100     */
4101     register : function(item)
4102     {
4103         this.navItems.push( item);
4104         item.navId = this.navId;
4105     
4106     },
4107     
4108     /**
4109     * clear all the Navigation item
4110     */
4111    
4112     clearAll : function()
4113     {
4114         this.navItems = [];
4115         this.el.dom.innerHTML = '';
4116     },
4117     
4118     getNavItem: function(tabId)
4119     {
4120         var ret = false;
4121         Roo.each(this.navItems, function(e) {
4122             if (e.tabId == tabId) {
4123                ret =  e;
4124                return false;
4125             }
4126             return true;
4127             
4128         });
4129         return ret;
4130     },
4131     
4132     setActiveNext : function()
4133     {
4134         var i = this.indexOfNav(this.getActive());
4135         if (i > this.navItems.length) {
4136             return;
4137         }
4138         this.setActiveItem(this.navItems[i+1]);
4139     },
4140     setActivePrev : function()
4141     {
4142         var i = this.indexOfNav(this.getActive());
4143         if (i  < 1) {
4144             return;
4145         }
4146         this.setActiveItem(this.navItems[i-1]);
4147     },
4148     clearWasActive : function(except) {
4149         Roo.each(this.navItems, function(e) {
4150             if (e.tabId != except.tabId && e.was_active) {
4151                e.was_active = false;
4152                return false;
4153             }
4154             return true;
4155             
4156         });
4157     },
4158     getWasActive : function ()
4159     {
4160         var r = false;
4161         Roo.each(this.navItems, function(e) {
4162             if (e.was_active) {
4163                r = e;
4164                return false;
4165             }
4166             return true;
4167             
4168         });
4169         return r;
4170     }
4171     
4172     
4173 });
4174
4175  
4176 Roo.apply(Roo.bootstrap.NavGroup, {
4177     
4178     groups: {},
4179      /**
4180     * register a Navigation Group
4181     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4182     */
4183     register : function(navgrp)
4184     {
4185         this.groups[navgrp.navId] = navgrp;
4186         
4187     },
4188     /**
4189     * fetch a Navigation Group based on the navigation ID
4190     * @param {string} the navgroup to add
4191     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4192     */
4193     get: function(navId) {
4194         if (typeof(this.groups[navId]) == 'undefined') {
4195             return false;
4196             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4197         }
4198         return this.groups[navId] ;
4199     }
4200     
4201     
4202     
4203 });
4204
4205  /*
4206  * - LGPL
4207  *
4208  * row
4209  * 
4210  */
4211
4212 /**
4213  * @class Roo.bootstrap.NavItem
4214  * @extends Roo.bootstrap.Component
4215  * Bootstrap Navbar.NavItem class
4216  * @cfg {String} href  link to
4217  * @cfg {String} html content of button
4218  * @cfg {String} badge text inside badge
4219  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4220  * @cfg {String} glyphicon name of glyphicon
4221  * @cfg {String} icon name of font awesome icon
4222  * @cfg {Boolean} active Is item active
4223  * @cfg {Boolean} disabled Is item disabled
4224  
4225  * @cfg {Boolean} preventDefault (true | false) default false
4226  * @cfg {String} tabId the tab that this item activates.
4227  * @cfg {String} tagtype (a|span) render as a href or span?
4228  * @cfg {Boolean} animateRef (true|false) link to element default false  
4229   
4230  * @constructor
4231  * Create a new Navbar Item
4232  * @param {Object} config The config object
4233  */
4234 Roo.bootstrap.NavItem = function(config){
4235     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4236     this.addEvents({
4237         // raw events
4238         /**
4239          * @event click
4240          * The raw click event for the entire grid.
4241          * @param {Roo.EventObject} e
4242          */
4243         "click" : true,
4244          /**
4245             * @event changed
4246             * Fires when the active item active state changes
4247             * @param {Roo.bootstrap.NavItem} this
4248             * @param {boolean} state the new state
4249              
4250          */
4251         'changed': true,
4252         /**
4253             * @event scrollto
4254             * Fires when scroll to element
4255             * @param {Roo.bootstrap.NavItem} this
4256             * @param {Object} options
4257             * @param {Roo.EventObject} e
4258              
4259          */
4260         'scrollto': true
4261     });
4262    
4263 };
4264
4265 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4266     
4267     href: false,
4268     html: '',
4269     badge: '',
4270     icon: false,
4271     glyphicon: false,
4272     active: false,
4273     preventDefault : false,
4274     tabId : false,
4275     tagtype : 'a',
4276     disabled : false,
4277     animateRef : false,
4278     was_active : false,
4279     
4280     getAutoCreate : function(){
4281          
4282         var cfg = {
4283             tag: 'li',
4284             cls: 'nav-item'
4285             
4286         };
4287         
4288         if (this.active) {
4289             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4290         }
4291         if (this.disabled) {
4292             cfg.cls += ' disabled';
4293         }
4294         
4295         if (this.href || this.html || this.glyphicon || this.icon) {
4296             cfg.cn = [
4297                 {
4298                     tag: this.tagtype,
4299                     href : this.href || "#",
4300                     html: this.html || ''
4301                 }
4302             ];
4303             
4304             if (this.icon) {
4305                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4306             }
4307
4308             if(this.glyphicon) {
4309                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4310             }
4311             
4312             if (this.menu) {
4313                 
4314                 cfg.cn[0].html += " <span class='caret'></span>";
4315              
4316             }
4317             
4318             if (this.badge !== '') {
4319                  
4320                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4321             }
4322         }
4323         
4324         
4325         
4326         return cfg;
4327     },
4328     initEvents: function() 
4329     {
4330         if (typeof (this.menu) != 'undefined') {
4331             this.menu.parentType = this.xtype;
4332             this.menu.triggerEl = this.el;
4333             this.menu = this.addxtype(Roo.apply({}, this.menu));
4334         }
4335         
4336         this.el.select('a',true).on('click', this.onClick, this);
4337         
4338         if(this.tagtype == 'span'){
4339             this.el.select('span',true).on('click', this.onClick, this);
4340         }
4341        
4342         // at this point parent should be available..
4343         this.parent().register(this);
4344     },
4345     
4346     onClick : function(e)
4347     {
4348         if (e.getTarget('.dropdown-menu-item')) {
4349             // did you click on a menu itemm.... - then don't trigger onclick..
4350             return;
4351         }
4352         
4353         if(
4354                 this.preventDefault || 
4355                 this.href == '#' 
4356         ){
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359         }
4360         
4361         if (this.disabled) {
4362             return;
4363         }
4364         
4365         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4366         if (tg && tg.transition) {
4367             Roo.log("waiting for the transitionend");
4368             return;
4369         }
4370         
4371         
4372         
4373         //Roo.log("fire event clicked");
4374         if(this.fireEvent('click', this, e) === false){
4375             return;
4376         };
4377         
4378         if(this.tagtype == 'span'){
4379             return;
4380         }
4381         
4382         //Roo.log(this.href);
4383         var ael = this.el.select('a',true).first();
4384         //Roo.log(ael);
4385         
4386         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4387             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4388             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4389                 return; // ignore... - it's a 'hash' to another page.
4390             }
4391             Roo.log("NavItem - prevent Default?");
4392             e.preventDefault();
4393             this.scrollToElement(e);
4394         }
4395         
4396         
4397         var p =  this.parent();
4398    
4399         if (['tabs','pills'].indexOf(p.type)!==-1) {
4400             if (typeof(p.setActiveItem) !== 'undefined') {
4401                 p.setActiveItem(this);
4402             }
4403         }
4404         
4405         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4406         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4407             // remove the collapsed menu expand...
4408             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4409         }
4410     },
4411     
4412     isActive: function () {
4413         return this.active
4414     },
4415     setActive : function(state, fire, is_was_active)
4416     {
4417         if (this.active && !state && this.navId) {
4418             this.was_active = true;
4419             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4420             if (nv) {
4421                 nv.clearWasActive(this);
4422             }
4423             
4424         }
4425         this.active = state;
4426         
4427         if (!state ) {
4428             this.el.removeClass('active');
4429         } else if (!this.el.hasClass('active')) {
4430             this.el.addClass('active');
4431         }
4432         if (fire) {
4433             this.fireEvent('changed', this, state);
4434         }
4435         
4436         // show a panel if it's registered and related..
4437         
4438         if (!this.navId || !this.tabId || !state || is_was_active) {
4439             return;
4440         }
4441         
4442         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4443         if (!tg) {
4444             return;
4445         }
4446         var pan = tg.getPanelByName(this.tabId);
4447         if (!pan) {
4448             return;
4449         }
4450         // if we can not flip to new panel - go back to old nav highlight..
4451         if (false == tg.showPanel(pan)) {
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 var onav = nv.getWasActive();
4455                 if (onav) {
4456                     onav.setActive(true, false, true);
4457                 }
4458             }
4459             
4460         }
4461         
4462         
4463         
4464     },
4465      // this should not be here...
4466     setDisabled : function(state)
4467     {
4468         this.disabled = state;
4469         if (!state ) {
4470             this.el.removeClass('disabled');
4471         } else if (!this.el.hasClass('disabled')) {
4472             this.el.addClass('disabled');
4473         }
4474         
4475     },
4476     
4477     /**
4478      * Fetch the element to display the tooltip on.
4479      * @return {Roo.Element} defaults to this.el
4480      */
4481     tooltipEl : function()
4482     {
4483         return this.el.select('' + this.tagtype + '', true).first();
4484     },
4485     
4486     scrollToElement : function(e)
4487     {
4488         var c = document.body;
4489         
4490         /*
4491          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4492          */
4493         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4494             c = document.documentElement;
4495         }
4496         
4497         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4498         
4499         if(!target){
4500             return;
4501         }
4502
4503         var o = target.calcOffsetsTo(c);
4504         
4505         var options = {
4506             target : target,
4507             value : o[1]
4508         };
4509         
4510         this.fireEvent('scrollto', this, options, e);
4511         
4512         Roo.get(c).scrollTo('top', options.value, true);
4513         
4514         return;
4515     }
4516 });
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * sidebar item
4523  *
4524  *  li
4525  *    <span> icon </span>
4526  *    <span> text </span>
4527  *    <span>badge </span>
4528  */
4529
4530 /**
4531  * @class Roo.bootstrap.NavSidebarItem
4532  * @extends Roo.bootstrap.NavItem
4533  * Bootstrap Navbar.NavSidebarItem class
4534  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4535  * {bool} open is the menu open
4536  * @constructor
4537  * Create a new Navbar Button
4538  * @param {Object} config The config object
4539  */
4540 Roo.bootstrap.NavSidebarItem = function(config){
4541     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4542     this.addEvents({
4543         // raw events
4544         /**
4545          * @event click
4546          * The raw click event for the entire grid.
4547          * @param {Roo.EventObject} e
4548          */
4549         "click" : true,
4550          /**
4551             * @event changed
4552             * Fires when the active item active state changes
4553             * @param {Roo.bootstrap.NavSidebarItem} this
4554             * @param {boolean} state the new state
4555              
4556          */
4557         'changed': true
4558     });
4559    
4560 };
4561
4562 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4563     
4564     badgeWeight : 'default',
4565     
4566     open: false,
4567     
4568     getAutoCreate : function(){
4569         
4570         
4571         var a = {
4572                 tag: 'a',
4573                 href : this.href || '#',
4574                 cls: '',
4575                 html : '',
4576                 cn : []
4577         };
4578         var cfg = {
4579             tag: 'li',
4580             cls: '',
4581             cn: [ a ]
4582         };
4583         var span = {
4584             tag: 'span',
4585             html : this.html || ''
4586         };
4587         
4588         
4589         if (this.active) {
4590             cfg.cls += ' active';
4591         }
4592         
4593         if (this.disabled) {
4594             cfg.cls += ' disabled';
4595         }
4596         if (this.open) {
4597             cfg.cls += ' open x-open';
4598         }
4599         // left icon..
4600         if (this.glyphicon || this.icon) {
4601             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4602             a.cn.push({ tag : 'i', cls : c }) ;
4603         }
4604         // html..
4605         a.cn.push(span);
4606         // then badge..
4607         if (this.badge !== '') {
4608             
4609             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4610         }
4611         // fi
4612         if (this.menu) {
4613             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4614             a.cls += 'dropdown-toggle treeview' ;
4615         }
4616         
4617         return cfg;
4618          
4619            
4620     },
4621     
4622     initEvents : function()
4623     { 
4624         if (typeof (this.menu) != 'undefined') {
4625             this.menu.parentType = this.xtype;
4626             this.menu.triggerEl = this.el;
4627             this.menu = this.addxtype(Roo.apply({}, this.menu));
4628         }
4629         
4630         this.el.on('click', this.onClick, this);
4631        
4632     
4633         if(this.badge !== ''){
4634  
4635             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4636         }
4637         
4638     },
4639     
4640     onClick : function(e)
4641     {
4642         if(this.disabled){
4643             e.preventDefault();
4644             return;
4645         }
4646         
4647         if(this.preventDefault){
4648             e.preventDefault();
4649         }
4650         
4651         this.fireEvent('click', this);
4652     },
4653     
4654     disable : function()
4655     {
4656         this.setDisabled(true);
4657     },
4658     
4659     enable : function()
4660     {
4661         this.setDisabled(false);
4662     },
4663     
4664     setDisabled : function(state)
4665     {
4666         if(this.disabled == state){
4667             return;
4668         }
4669         
4670         this.disabled = state;
4671         
4672         if (state) {
4673             this.el.addClass('disabled');
4674             return;
4675         }
4676         
4677         this.el.removeClass('disabled');
4678         
4679         return;
4680     },
4681     
4682     setActive : function(state)
4683     {
4684         if(this.active == state){
4685             return;
4686         }
4687         
4688         this.active = state;
4689         
4690         if (state) {
4691             this.el.addClass('active');
4692             return;
4693         }
4694         
4695         this.el.removeClass('active');
4696         
4697         return;
4698     },
4699     
4700     isActive: function () 
4701     {
4702         return this.active;
4703     },
4704     
4705     setBadge : function(str)
4706     {
4707         if(!this.badgeEl){
4708             return;
4709         }
4710         
4711         this.badgeEl.dom.innerHTML = str;
4712     }
4713     
4714    
4715      
4716  
4717 });
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * row
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Row
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Row class (contains columns...)
4731  * 
4732  * @constructor
4733  * Create a new Row
4734  * @param {Object} config The config object
4735  */
4736
4737 Roo.bootstrap.Row = function(config){
4738     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4739 };
4740
4741 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4742     
4743     getAutoCreate : function(){
4744        return {
4745             cls: 'row clearfix'
4746        };
4747     }
4748     
4749     
4750 });
4751
4752  
4753
4754  /*
4755  * - LGPL
4756  *
4757  * element
4758  * 
4759  */
4760
4761 /**
4762  * @class Roo.bootstrap.Element
4763  * @extends Roo.bootstrap.Component
4764  * Bootstrap Element class
4765  * @cfg {String} html contents of the element
4766  * @cfg {String} tag tag of the element
4767  * @cfg {String} cls class of the element
4768  * @cfg {Boolean} preventDefault (true|false) default false
4769  * @cfg {Boolean} clickable (true|false) default false
4770  * 
4771  * @constructor
4772  * Create a new Element
4773  * @param {Object} config The config object
4774  */
4775
4776 Roo.bootstrap.Element = function(config){
4777     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4778     
4779     this.addEvents({
4780         // raw events
4781         /**
4782          * @event click
4783          * When a element is chick
4784          * @param {Roo.bootstrap.Element} this
4785          * @param {Roo.EventObject} e
4786          */
4787         "click" : true
4788     });
4789 };
4790
4791 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4792     
4793     tag: 'div',
4794     cls: '',
4795     html: '',
4796     preventDefault: false, 
4797     clickable: false,
4798     
4799     getAutoCreate : function(){
4800         
4801         var cfg = {
4802             tag: this.tag,
4803             cls: this.cls,
4804             html: this.html
4805         };
4806         
4807         return cfg;
4808     },
4809     
4810     initEvents: function() 
4811     {
4812         Roo.bootstrap.Element.superclass.initEvents.call(this);
4813         
4814         if(this.clickable){
4815             this.el.on('click', this.onClick, this);
4816         }
4817         
4818     },
4819     
4820     onClick : function(e)
4821     {
4822         if(this.preventDefault){
4823             e.preventDefault();
4824         }
4825         
4826         this.fireEvent('click', this, e);
4827     },
4828     
4829     getValue : function()
4830     {
4831         return this.el.dom.innerHTML;
4832     },
4833     
4834     setValue : function(value)
4835     {
4836         this.el.dom.innerHTML = value;
4837     }
4838    
4839 });
4840
4841  
4842
4843  /*
4844  * - LGPL
4845  *
4846  * pagination
4847  * 
4848  */
4849
4850 /**
4851  * @class Roo.bootstrap.Pagination
4852  * @extends Roo.bootstrap.Component
4853  * Bootstrap Pagination class
4854  * @cfg {String} size xs | sm | md | lg
4855  * @cfg {Boolean} inverse false | true
4856  * 
4857  * @constructor
4858  * Create a new Pagination
4859  * @param {Object} config The config object
4860  */
4861
4862 Roo.bootstrap.Pagination = function(config){
4863     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4864 };
4865
4866 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4867     
4868     cls: false,
4869     size: false,
4870     inverse: false,
4871     
4872     getAutoCreate : function(){
4873         var cfg = {
4874             tag: 'ul',
4875                 cls: 'pagination'
4876         };
4877         if (this.inverse) {
4878             cfg.cls += ' inverse';
4879         }
4880         if (this.html) {
4881             cfg.html=this.html;
4882         }
4883         if (this.cls) {
4884             cfg.cls += " " + this.cls;
4885         }
4886         return cfg;
4887     }
4888    
4889 });
4890
4891  
4892
4893  /*
4894  * - LGPL
4895  *
4896  * Pagination item
4897  * 
4898  */
4899
4900
4901 /**
4902  * @class Roo.bootstrap.PaginationItem
4903  * @extends Roo.bootstrap.Component
4904  * Bootstrap PaginationItem class
4905  * @cfg {String} html text
4906  * @cfg {String} href the link
4907  * @cfg {Boolean} preventDefault (true | false) default true
4908  * @cfg {Boolean} active (true | false) default false
4909  * @cfg {Boolean} disabled default false
4910  * 
4911  * 
4912  * @constructor
4913  * Create a new PaginationItem
4914  * @param {Object} config The config object
4915  */
4916
4917
4918 Roo.bootstrap.PaginationItem = function(config){
4919     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4920     this.addEvents({
4921         // raw events
4922         /**
4923          * @event click
4924          * The raw click event for the entire grid.
4925          * @param {Roo.EventObject} e
4926          */
4927         "click" : true
4928     });
4929 };
4930
4931 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4932     
4933     href : false,
4934     html : false,
4935     preventDefault: true,
4936     active : false,
4937     cls : false,
4938     disabled: false,
4939     
4940     getAutoCreate : function(){
4941         var cfg= {
4942             tag: 'li',
4943             cn: [
4944                 {
4945                     tag : 'a',
4946                     href : this.href ? this.href : '#',
4947                     html : this.html ? this.html : ''
4948                 }
4949             ]
4950         };
4951         
4952         if(this.cls){
4953             cfg.cls = this.cls;
4954         }
4955         
4956         if(this.disabled){
4957             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4958         }
4959         
4960         if(this.active){
4961             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4962         }
4963         
4964         return cfg;
4965     },
4966     
4967     initEvents: function() {
4968         
4969         this.el.on('click', this.onClick, this);
4970         
4971     },
4972     onClick : function(e)
4973     {
4974         Roo.log('PaginationItem on click ');
4975         if(this.preventDefault){
4976             e.preventDefault();
4977         }
4978         
4979         if(this.disabled){
4980             return;
4981         }
4982         
4983         this.fireEvent('click', this, e);
4984     }
4985    
4986 });
4987
4988  
4989
4990  /*
4991  * - LGPL
4992  *
4993  * slider
4994  * 
4995  */
4996
4997
4998 /**
4999  * @class Roo.bootstrap.Slider
5000  * @extends Roo.bootstrap.Component
5001  * Bootstrap Slider class
5002  *    
5003  * @constructor
5004  * Create a new Slider
5005  * @param {Object} config The config object
5006  */
5007
5008 Roo.bootstrap.Slider = function(config){
5009     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5010 };
5011
5012 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5013     
5014     getAutoCreate : function(){
5015         
5016         var cfg = {
5017             tag: 'div',
5018             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5019             cn: [
5020                 {
5021                     tag: 'a',
5022                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5023                 }
5024             ]
5025         };
5026         
5027         return cfg;
5028     }
5029    
5030 });
5031
5032  /*
5033  * Based on:
5034  * Ext JS Library 1.1.1
5035  * Copyright(c) 2006-2007, Ext JS, LLC.
5036  *
5037  * Originally Released Under LGPL - original licence link has changed is not relivant.
5038  *
5039  * Fork - LGPL
5040  * <script type="text/javascript">
5041  */
5042  
5043
5044 /**
5045  * @class Roo.grid.ColumnModel
5046  * @extends Roo.util.Observable
5047  * This is the default implementation of a ColumnModel used by the Grid. It defines
5048  * the columns in the grid.
5049  * <br>Usage:<br>
5050  <pre><code>
5051  var colModel = new Roo.grid.ColumnModel([
5052         {header: "Ticker", width: 60, sortable: true, locked: true},
5053         {header: "Company Name", width: 150, sortable: true},
5054         {header: "Market Cap.", width: 100, sortable: true},
5055         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5056         {header: "Employees", width: 100, sortable: true, resizable: false}
5057  ]);
5058  </code></pre>
5059  * <p>
5060  
5061  * The config options listed for this class are options which may appear in each
5062  * individual column definition.
5063  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5064  * @constructor
5065  * @param {Object} config An Array of column config objects. See this class's
5066  * config objects for details.
5067 */
5068 Roo.grid.ColumnModel = function(config){
5069         /**
5070      * The config passed into the constructor
5071      */
5072     this.config = config;
5073     this.lookup = {};
5074
5075     // if no id, create one
5076     // if the column does not have a dataIndex mapping,
5077     // map it to the order it is in the config
5078     for(var i = 0, len = config.length; i < len; i++){
5079         var c = config[i];
5080         if(typeof c.dataIndex == "undefined"){
5081             c.dataIndex = i;
5082         }
5083         if(typeof c.renderer == "string"){
5084             c.renderer = Roo.util.Format[c.renderer];
5085         }
5086         if(typeof c.id == "undefined"){
5087             c.id = Roo.id();
5088         }
5089         if(c.editor && c.editor.xtype){
5090             c.editor  = Roo.factory(c.editor, Roo.grid);
5091         }
5092         if(c.editor && c.editor.isFormField){
5093             c.editor = new Roo.grid.GridEditor(c.editor);
5094         }
5095         this.lookup[c.id] = c;
5096     }
5097
5098     /**
5099      * The width of columns which have no width specified (defaults to 100)
5100      * @type Number
5101      */
5102     this.defaultWidth = 100;
5103
5104     /**
5105      * Default sortable of columns which have no sortable specified (defaults to false)
5106      * @type Boolean
5107      */
5108     this.defaultSortable = false;
5109
5110     this.addEvents({
5111         /**
5112              * @event widthchange
5113              * Fires when the width of a column changes.
5114              * @param {ColumnModel} this
5115              * @param {Number} columnIndex The column index
5116              * @param {Number} newWidth The new width
5117              */
5118             "widthchange": true,
5119         /**
5120              * @event headerchange
5121              * Fires when the text of a header changes.
5122              * @param {ColumnModel} this
5123              * @param {Number} columnIndex The column index
5124              * @param {Number} newText The new header text
5125              */
5126             "headerchange": true,
5127         /**
5128              * @event hiddenchange
5129              * Fires when a column is hidden or "unhidden".
5130              * @param {ColumnModel} this
5131              * @param {Number} columnIndex The column index
5132              * @param {Boolean} hidden true if hidden, false otherwise
5133              */
5134             "hiddenchange": true,
5135             /**
5136          * @event columnmoved
5137          * Fires when a column is moved.
5138          * @param {ColumnModel} this
5139          * @param {Number} oldIndex
5140          * @param {Number} newIndex
5141          */
5142         "columnmoved" : true,
5143         /**
5144          * @event columlockchange
5145          * Fires when a column's locked state is changed
5146          * @param {ColumnModel} this
5147          * @param {Number} colIndex
5148          * @param {Boolean} locked true if locked
5149          */
5150         "columnlockchange" : true
5151     });
5152     Roo.grid.ColumnModel.superclass.constructor.call(this);
5153 };
5154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5155     /**
5156      * @cfg {String} header The header text to display in the Grid view.
5157      */
5158     /**
5159      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5160      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5161      * specified, the column's index is used as an index into the Record's data Array.
5162      */
5163     /**
5164      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5165      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5166      */
5167     /**
5168      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5169      * Defaults to the value of the {@link #defaultSortable} property.
5170      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5171      */
5172     /**
5173      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5174      */
5175     /**
5176      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5177      */
5178     /**
5179      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5180      */
5181     /**
5182      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5183      */
5184     /**
5185      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5186      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5187      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5188      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5189      */
5190        /**
5191      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5192      */
5193     /**
5194      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5195      */
5196     /**
5197      * @cfg {String} cursor (Optional)
5198      */
5199     /**
5200      * @cfg {String} tooltip (Optional)
5201      */
5202     /**
5203      * @cfg {Number} xs (Optional)
5204      */
5205     /**
5206      * @cfg {Number} sm (Optional)
5207      */
5208     /**
5209      * @cfg {Number} md (Optional)
5210      */
5211     /**
5212      * @cfg {Number} lg (Optional)
5213      */
5214     /**
5215      * Returns the id of the column at the specified index.
5216      * @param {Number} index The column index
5217      * @return {String} the id
5218      */
5219     getColumnId : function(index){
5220         return this.config[index].id;
5221     },
5222
5223     /**
5224      * Returns the column for a specified id.
5225      * @param {String} id The column id
5226      * @return {Object} the column
5227      */
5228     getColumnById : function(id){
5229         return this.lookup[id];
5230     },
5231
5232     
5233     /**
5234      * Returns the column for a specified dataIndex.
5235      * @param {String} dataIndex The column dataIndex
5236      * @return {Object|Boolean} the column or false if not found
5237      */
5238     getColumnByDataIndex: function(dataIndex){
5239         var index = this.findColumnIndex(dataIndex);
5240         return index > -1 ? this.config[index] : false;
5241     },
5242     
5243     /**
5244      * Returns the index for a specified column id.
5245      * @param {String} id The column id
5246      * @return {Number} the index, or -1 if not found
5247      */
5248     getIndexById : function(id){
5249         for(var i = 0, len = this.config.length; i < len; i++){
5250             if(this.config[i].id == id){
5251                 return i;
5252             }
5253         }
5254         return -1;
5255     },
5256     
5257     /**
5258      * Returns the index for a specified column dataIndex.
5259      * @param {String} dataIndex The column dataIndex
5260      * @return {Number} the index, or -1 if not found
5261      */
5262     
5263     findColumnIndex : function(dataIndex){
5264         for(var i = 0, len = this.config.length; i < len; i++){
5265             if(this.config[i].dataIndex == dataIndex){
5266                 return i;
5267             }
5268         }
5269         return -1;
5270     },
5271     
5272     
5273     moveColumn : function(oldIndex, newIndex){
5274         var c = this.config[oldIndex];
5275         this.config.splice(oldIndex, 1);
5276         this.config.splice(newIndex, 0, c);
5277         this.dataMap = null;
5278         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5279     },
5280
5281     isLocked : function(colIndex){
5282         return this.config[colIndex].locked === true;
5283     },
5284
5285     setLocked : function(colIndex, value, suppressEvent){
5286         if(this.isLocked(colIndex) == value){
5287             return;
5288         }
5289         this.config[colIndex].locked = value;
5290         if(!suppressEvent){
5291             this.fireEvent("columnlockchange", this, colIndex, value);
5292         }
5293     },
5294
5295     getTotalLockedWidth : function(){
5296         var totalWidth = 0;
5297         for(var i = 0; i < this.config.length; i++){
5298             if(this.isLocked(i) && !this.isHidden(i)){
5299                 this.totalWidth += this.getColumnWidth(i);
5300             }
5301         }
5302         return totalWidth;
5303     },
5304
5305     getLockedCount : function(){
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             if(!this.isLocked(i)){
5308                 return i;
5309             }
5310         }
5311         
5312         return this.config.length;
5313     },
5314
5315     /**
5316      * Returns the number of columns.
5317      * @return {Number}
5318      */
5319     getColumnCount : function(visibleOnly){
5320         if(visibleOnly === true){
5321             var c = 0;
5322             for(var i = 0, len = this.config.length; i < len; i++){
5323                 if(!this.isHidden(i)){
5324                     c++;
5325                 }
5326             }
5327             return c;
5328         }
5329         return this.config.length;
5330     },
5331
5332     /**
5333      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5334      * @param {Function} fn
5335      * @param {Object} scope (optional)
5336      * @return {Array} result
5337      */
5338     getColumnsBy : function(fn, scope){
5339         var r = [];
5340         for(var i = 0, len = this.config.length; i < len; i++){
5341             var c = this.config[i];
5342             if(fn.call(scope||this, c, i) === true){
5343                 r[r.length] = c;
5344             }
5345         }
5346         return r;
5347     },
5348
5349     /**
5350      * Returns true if the specified column is sortable.
5351      * @param {Number} col The column index
5352      * @return {Boolean}
5353      */
5354     isSortable : function(col){
5355         if(typeof this.config[col].sortable == "undefined"){
5356             return this.defaultSortable;
5357         }
5358         return this.config[col].sortable;
5359     },
5360
5361     /**
5362      * Returns the rendering (formatting) function defined for the column.
5363      * @param {Number} col The column index.
5364      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5365      */
5366     getRenderer : function(col){
5367         if(!this.config[col].renderer){
5368             return Roo.grid.ColumnModel.defaultRenderer;
5369         }
5370         return this.config[col].renderer;
5371     },
5372
5373     /**
5374      * Sets the rendering (formatting) function for a column.
5375      * @param {Number} col The column index
5376      * @param {Function} fn The function to use to process the cell's raw data
5377      * to return HTML markup for the grid view. The render function is called with
5378      * the following parameters:<ul>
5379      * <li>Data value.</li>
5380      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5381      * <li>css A CSS style string to apply to the table cell.</li>
5382      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5383      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5384      * <li>Row index</li>
5385      * <li>Column index</li>
5386      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5387      */
5388     setRenderer : function(col, fn){
5389         this.config[col].renderer = fn;
5390     },
5391
5392     /**
5393      * Returns the width for the specified column.
5394      * @param {Number} col The column index
5395      * @return {Number}
5396      */
5397     getColumnWidth : function(col){
5398         return this.config[col].width * 1 || this.defaultWidth;
5399     },
5400
5401     /**
5402      * Sets the width for a column.
5403      * @param {Number} col The column index
5404      * @param {Number} width The new width
5405      */
5406     setColumnWidth : function(col, width, suppressEvent){
5407         this.config[col].width = width;
5408         this.totalWidth = null;
5409         if(!suppressEvent){
5410              this.fireEvent("widthchange", this, col, width);
5411         }
5412     },
5413
5414     /**
5415      * Returns the total width of all columns.
5416      * @param {Boolean} includeHidden True to include hidden column widths
5417      * @return {Number}
5418      */
5419     getTotalWidth : function(includeHidden){
5420         if(!this.totalWidth){
5421             this.totalWidth = 0;
5422             for(var i = 0, len = this.config.length; i < len; i++){
5423                 if(includeHidden || !this.isHidden(i)){
5424                     this.totalWidth += this.getColumnWidth(i);
5425                 }
5426             }
5427         }
5428         return this.totalWidth;
5429     },
5430
5431     /**
5432      * Returns the header for the specified column.
5433      * @param {Number} col The column index
5434      * @return {String}
5435      */
5436     getColumnHeader : function(col){
5437         return this.config[col].header;
5438     },
5439
5440     /**
5441      * Sets the header for a column.
5442      * @param {Number} col The column index
5443      * @param {String} header The new header
5444      */
5445     setColumnHeader : function(col, header){
5446         this.config[col].header = header;
5447         this.fireEvent("headerchange", this, col, header);
5448     },
5449
5450     /**
5451      * Returns the tooltip for the specified column.
5452      * @param {Number} col The column index
5453      * @return {String}
5454      */
5455     getColumnTooltip : function(col){
5456             return this.config[col].tooltip;
5457     },
5458     /**
5459      * Sets the tooltip for a column.
5460      * @param {Number} col The column index
5461      * @param {String} tooltip The new tooltip
5462      */
5463     setColumnTooltip : function(col, tooltip){
5464             this.config[col].tooltip = tooltip;
5465     },
5466
5467     /**
5468      * Returns the dataIndex for the specified column.
5469      * @param {Number} col The column index
5470      * @return {Number}
5471      */
5472     getDataIndex : function(col){
5473         return this.config[col].dataIndex;
5474     },
5475
5476     /**
5477      * Sets the dataIndex for a column.
5478      * @param {Number} col The column index
5479      * @param {Number} dataIndex The new dataIndex
5480      */
5481     setDataIndex : function(col, dataIndex){
5482         this.config[col].dataIndex = dataIndex;
5483     },
5484
5485     
5486     
5487     /**
5488      * Returns true if the cell is editable.
5489      * @param {Number} colIndex The column index
5490      * @param {Number} rowIndex The row index - this is nto actually used..?
5491      * @return {Boolean}
5492      */
5493     isCellEditable : function(colIndex, rowIndex){
5494         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5495     },
5496
5497     /**
5498      * Returns the editor defined for the cell/column.
5499      * return false or null to disable editing.
5500      * @param {Number} colIndex The column index
5501      * @param {Number} rowIndex The row index
5502      * @return {Object}
5503      */
5504     getCellEditor : function(colIndex, rowIndex){
5505         return this.config[colIndex].editor;
5506     },
5507
5508     /**
5509      * Sets if a column is editable.
5510      * @param {Number} col The column index
5511      * @param {Boolean} editable True if the column is editable
5512      */
5513     setEditable : function(col, editable){
5514         this.config[col].editable = editable;
5515     },
5516
5517
5518     /**
5519      * Returns true if the column is hidden.
5520      * @param {Number} colIndex The column index
5521      * @return {Boolean}
5522      */
5523     isHidden : function(colIndex){
5524         return this.config[colIndex].hidden;
5525     },
5526
5527
5528     /**
5529      * Returns true if the column width cannot be changed
5530      */
5531     isFixed : function(colIndex){
5532         return this.config[colIndex].fixed;
5533     },
5534
5535     /**
5536      * Returns true if the column can be resized
5537      * @return {Boolean}
5538      */
5539     isResizable : function(colIndex){
5540         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5541     },
5542     /**
5543      * Sets if a column is hidden.
5544      * @param {Number} colIndex The column index
5545      * @param {Boolean} hidden True if the column is hidden
5546      */
5547     setHidden : function(colIndex, hidden){
5548         this.config[colIndex].hidden = hidden;
5549         this.totalWidth = null;
5550         this.fireEvent("hiddenchange", this, colIndex, hidden);
5551     },
5552
5553     /**
5554      * Sets the editor for a column.
5555      * @param {Number} col The column index
5556      * @param {Object} editor The editor object
5557      */
5558     setEditor : function(col, editor){
5559         this.config[col].editor = editor;
5560     }
5561 });
5562
5563 Roo.grid.ColumnModel.defaultRenderer = function(value)
5564 {
5565     if(typeof value == "object") {
5566         return value;
5567     }
5568         if(typeof value == "string" && value.length < 1){
5569             return "&#160;";
5570         }
5571     
5572         return String.format("{0}", value);
5573 };
5574
5575 // Alias for backwards compatibility
5576 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5577 /*
5578  * Based on:
5579  * Ext JS Library 1.1.1
5580  * Copyright(c) 2006-2007, Ext JS, LLC.
5581  *
5582  * Originally Released Under LGPL - original licence link has changed is not relivant.
5583  *
5584  * Fork - LGPL
5585  * <script type="text/javascript">
5586  */
5587  
5588 /**
5589  * @class Roo.LoadMask
5590  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5591  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5592  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5593  * element's UpdateManager load indicator and will be destroyed after the initial load.
5594  * @constructor
5595  * Create a new LoadMask
5596  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5597  * @param {Object} config The config object
5598  */
5599 Roo.LoadMask = function(el, config){
5600     this.el = Roo.get(el);
5601     Roo.apply(this, config);
5602     if(this.store){
5603         this.store.on('beforeload', this.onBeforeLoad, this);
5604         this.store.on('load', this.onLoad, this);
5605         this.store.on('loadexception', this.onLoadException, this);
5606         this.removeMask = false;
5607     }else{
5608         var um = this.el.getUpdateManager();
5609         um.showLoadIndicator = false; // disable the default indicator
5610         um.on('beforeupdate', this.onBeforeLoad, this);
5611         um.on('update', this.onLoad, this);
5612         um.on('failure', this.onLoad, this);
5613         this.removeMask = true;
5614     }
5615 };
5616
5617 Roo.LoadMask.prototype = {
5618     /**
5619      * @cfg {Boolean} removeMask
5620      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5621      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5622      */
5623     /**
5624      * @cfg {String} msg
5625      * The text to display in a centered loading message box (defaults to 'Loading...')
5626      */
5627     msg : 'Loading...',
5628     /**
5629      * @cfg {String} msgCls
5630      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5631      */
5632     msgCls : 'x-mask-loading',
5633
5634     /**
5635      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5636      * @type Boolean
5637      */
5638     disabled: false,
5639
5640     /**
5641      * Disables the mask to prevent it from being displayed
5642      */
5643     disable : function(){
5644        this.disabled = true;
5645     },
5646
5647     /**
5648      * Enables the mask so that it can be displayed
5649      */
5650     enable : function(){
5651         this.disabled = false;
5652     },
5653     
5654     onLoadException : function()
5655     {
5656         Roo.log(arguments);
5657         
5658         if (typeof(arguments[3]) != 'undefined') {
5659             Roo.MessageBox.alert("Error loading",arguments[3]);
5660         } 
5661         /*
5662         try {
5663             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5664                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5665             }   
5666         } catch(e) {
5667             
5668         }
5669         */
5670     
5671         
5672         
5673         this.el.unmask(this.removeMask);
5674     },
5675     // private
5676     onLoad : function()
5677     {
5678         this.el.unmask(this.removeMask);
5679     },
5680
5681     // private
5682     onBeforeLoad : function(){
5683         if(!this.disabled){
5684             this.el.mask(this.msg, this.msgCls);
5685         }
5686     },
5687
5688     // private
5689     destroy : function(){
5690         if(this.store){
5691             this.store.un('beforeload', this.onBeforeLoad, this);
5692             this.store.un('load', this.onLoad, this);
5693             this.store.un('loadexception', this.onLoadException, this);
5694         }else{
5695             var um = this.el.getUpdateManager();
5696             um.un('beforeupdate', this.onBeforeLoad, this);
5697             um.un('update', this.onLoad, this);
5698             um.un('failure', this.onLoad, this);
5699         }
5700     }
5701 };/*
5702  * - LGPL
5703  *
5704  * table
5705  * 
5706  */
5707
5708 /**
5709  * @class Roo.bootstrap.Table
5710  * @extends Roo.bootstrap.Component
5711  * Bootstrap Table class
5712  * @cfg {String} cls table class
5713  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5714  * @cfg {String} bgcolor Specifies the background color for a table
5715  * @cfg {Number} border Specifies whether the table cells should have borders or not
5716  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5717  * @cfg {Number} cellspacing Specifies the space between cells
5718  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5719  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5720  * @cfg {String} sortable Specifies that the table should be sortable
5721  * @cfg {String} summary Specifies a summary of the content of a table
5722  * @cfg {Number} width Specifies the width of a table
5723  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5724  * 
5725  * @cfg {boolean} striped Should the rows be alternative striped
5726  * @cfg {boolean} bordered Add borders to the table
5727  * @cfg {boolean} hover Add hover highlighting
5728  * @cfg {boolean} condensed Format condensed
5729  * @cfg {boolean} responsive Format condensed
5730  * @cfg {Boolean} loadMask (true|false) default false
5731  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5732  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5733  * @cfg {Boolean} rowSelection (true|false) default false
5734  * @cfg {Boolean} cellSelection (true|false) default false
5735  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5736  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5737  
5738  * 
5739  * @constructor
5740  * Create a new Table
5741  * @param {Object} config The config object
5742  */
5743
5744 Roo.bootstrap.Table = function(config){
5745     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5746     
5747   
5748     
5749     // BC...
5750     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5751     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5752     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5753     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5754     
5755     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5756     if (this.sm) {
5757         this.sm.grid = this;
5758         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5759         this.sm = this.selModel;
5760         this.sm.xmodule = this.xmodule || false;
5761     }
5762     
5763     if (this.cm && typeof(this.cm.config) == 'undefined') {
5764         this.colModel = new Roo.grid.ColumnModel(this.cm);
5765         this.cm = this.colModel;
5766         this.cm.xmodule = this.xmodule || false;
5767     }
5768     if (this.store) {
5769         this.store= Roo.factory(this.store, Roo.data);
5770         this.ds = this.store;
5771         this.ds.xmodule = this.xmodule || false;
5772          
5773     }
5774     if (this.footer && this.store) {
5775         this.footer.dataSource = this.ds;
5776         this.footer = Roo.factory(this.footer);
5777     }
5778     
5779     /** @private */
5780     this.addEvents({
5781         /**
5782          * @event cellclick
5783          * Fires when a cell is clicked
5784          * @param {Roo.bootstrap.Table} this
5785          * @param {Roo.Element} el
5786          * @param {Number} rowIndex
5787          * @param {Number} columnIndex
5788          * @param {Roo.EventObject} e
5789          */
5790         "cellclick" : true,
5791         /**
5792          * @event celldblclick
5793          * Fires when a cell is double clicked
5794          * @param {Roo.bootstrap.Table} this
5795          * @param {Roo.Element} el
5796          * @param {Number} rowIndex
5797          * @param {Number} columnIndex
5798          * @param {Roo.EventObject} e
5799          */
5800         "celldblclick" : true,
5801         /**
5802          * @event rowclick
5803          * Fires when a row is clicked
5804          * @param {Roo.bootstrap.Table} this
5805          * @param {Roo.Element} el
5806          * @param {Number} rowIndex
5807          * @param {Roo.EventObject} e
5808          */
5809         "rowclick" : true,
5810         /**
5811          * @event rowdblclick
5812          * Fires when a row is double clicked
5813          * @param {Roo.bootstrap.Table} this
5814          * @param {Roo.Element} el
5815          * @param {Number} rowIndex
5816          * @param {Roo.EventObject} e
5817          */
5818         "rowdblclick" : true,
5819         /**
5820          * @event mouseover
5821          * Fires when a mouseover occur
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Roo.Element} el
5824          * @param {Number} rowIndex
5825          * @param {Number} columnIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "mouseover" : true,
5829         /**
5830          * @event mouseout
5831          * Fires when a mouseout occur
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Roo.Element} el
5834          * @param {Number} rowIndex
5835          * @param {Number} columnIndex
5836          * @param {Roo.EventObject} e
5837          */
5838         "mouseout" : true,
5839         /**
5840          * @event rowclass
5841          * Fires when a row is rendered, so you can change add a style to it.
5842          * @param {Roo.bootstrap.Table} this
5843          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5844          */
5845         'rowclass' : true,
5846           /**
5847          * @event rowsrendered
5848          * Fires when all the  rows have been rendered
5849          * @param {Roo.bootstrap.Table} this
5850          */
5851         'rowsrendered' : true,
5852         /**
5853          * @event contextmenu
5854          * The raw contextmenu event for the entire grid.
5855          * @param {Roo.EventObject} e
5856          */
5857         "contextmenu" : true,
5858         /**
5859          * @event rowcontextmenu
5860          * Fires when a row is right clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Number} rowIndex
5863          * @param {Roo.EventObject} e
5864          */
5865         "rowcontextmenu" : true,
5866         /**
5867          * @event cellcontextmenu
5868          * Fires when a cell is right clicked
5869          * @param {Roo.bootstrap.Table} this
5870          * @param {Number} rowIndex
5871          * @param {Number} cellIndex
5872          * @param {Roo.EventObject} e
5873          */
5874          "cellcontextmenu" : true,
5875          /**
5876          * @event headercontextmenu
5877          * Fires when a header is right clicked
5878          * @param {Roo.bootstrap.Table} this
5879          * @param {Number} columnIndex
5880          * @param {Roo.EventObject} e
5881          */
5882         "headercontextmenu" : true
5883     });
5884 };
5885
5886 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5887     
5888     cls: false,
5889     align: false,
5890     bgcolor: false,
5891     border: false,
5892     cellpadding: false,
5893     cellspacing: false,
5894     frame: false,
5895     rules: false,
5896     sortable: false,
5897     summary: false,
5898     width: false,
5899     striped : false,
5900     scrollBody : false,
5901     bordered: false,
5902     hover:  false,
5903     condensed : false,
5904     responsive : false,
5905     sm : false,
5906     cm : false,
5907     store : false,
5908     loadMask : false,
5909     footerShow : true,
5910     headerShow : true,
5911   
5912     rowSelection : false,
5913     cellSelection : false,
5914     layout : false,
5915     
5916     // Roo.Element - the tbody
5917     mainBody: false,
5918     // Roo.Element - thead element
5919     mainHead: false,
5920     
5921     container: false, // used by gridpanel...
5922     
5923     getAutoCreate : function()
5924     {
5925         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5926         
5927         cfg = {
5928             tag: 'table',
5929             cls : 'table',
5930             cn : []
5931         };
5932         if (this.scrollBody) {
5933             cfg.cls += ' table-body-fixed';
5934         }    
5935         if (this.striped) {
5936             cfg.cls += ' table-striped';
5937         }
5938         
5939         if (this.hover) {
5940             cfg.cls += ' table-hover';
5941         }
5942         if (this.bordered) {
5943             cfg.cls += ' table-bordered';
5944         }
5945         if (this.condensed) {
5946             cfg.cls += ' table-condensed';
5947         }
5948         if (this.responsive) {
5949             cfg.cls += ' table-responsive';
5950         }
5951         
5952         if (this.cls) {
5953             cfg.cls+=  ' ' +this.cls;
5954         }
5955         
5956         // this lot should be simplifed...
5957         
5958         if (this.align) {
5959             cfg.align=this.align;
5960         }
5961         if (this.bgcolor) {
5962             cfg.bgcolor=this.bgcolor;
5963         }
5964         if (this.border) {
5965             cfg.border=this.border;
5966         }
5967         if (this.cellpadding) {
5968             cfg.cellpadding=this.cellpadding;
5969         }
5970         if (this.cellspacing) {
5971             cfg.cellspacing=this.cellspacing;
5972         }
5973         if (this.frame) {
5974             cfg.frame=this.frame;
5975         }
5976         if (this.rules) {
5977             cfg.rules=this.rules;
5978         }
5979         if (this.sortable) {
5980             cfg.sortable=this.sortable;
5981         }
5982         if (this.summary) {
5983             cfg.summary=this.summary;
5984         }
5985         if (this.width) {
5986             cfg.width=this.width;
5987         }
5988         if (this.layout) {
5989             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5990         }
5991         
5992         if(this.store || this.cm){
5993             if(this.headerShow){
5994                 cfg.cn.push(this.renderHeader());
5995             }
5996             
5997             cfg.cn.push(this.renderBody());
5998             
5999             if(this.footerShow){
6000                 cfg.cn.push(this.renderFooter());
6001             }
6002             // where does this come from?
6003             //cfg.cls+=  ' TableGrid';
6004         }
6005         
6006         return { cn : [ cfg ] };
6007     },
6008     
6009     initEvents : function()
6010     {   
6011         if(!this.store || !this.cm){
6012             return;
6013         }
6014         if (this.selModel) {
6015             this.selModel.initEvents();
6016         }
6017         
6018         
6019         //Roo.log('initEvents with ds!!!!');
6020         
6021         this.mainBody = this.el.select('tbody', true).first();
6022         this.mainHead = this.el.select('thead', true).first();
6023         
6024         
6025         
6026         
6027         var _this = this;
6028         
6029         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6030             e.on('click', _this.sort, _this);
6031         });
6032         
6033         this.mainBody.on("click", this.onClick, this);
6034         this.mainBody.on("dblclick", this.onDblClick, this);
6035         
6036         // why is this done????? = it breaks dialogs??
6037         //this.parent().el.setStyle('position', 'relative');
6038         
6039         
6040         if (this.footer) {
6041             this.footer.parentId = this.id;
6042             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6043         } 
6044         
6045         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6046         
6047         this.store.on('load', this.onLoad, this);
6048         this.store.on('beforeload', this.onBeforeLoad, this);
6049         this.store.on('update', this.onUpdate, this);
6050         this.store.on('add', this.onAdd, this);
6051         this.store.on("clear", this.clear, this);
6052         
6053         this.el.on("contextmenu", this.onContextMenu, this);
6054         
6055         this.mainBody.on('scroll', this.onBodyScroll, this);
6056         
6057         
6058     },
6059     
6060     onContextMenu : function(e, t)
6061     {
6062         this.processEvent("contextmenu", e);
6063     },
6064     
6065     processEvent : function(name, e)
6066     {
6067         if (name != 'touchstart' ) {
6068             this.fireEvent(name, e);    
6069         }
6070         
6071         var t = e.getTarget();
6072         
6073         var cell = Roo.get(t);
6074         
6075         if(!cell){
6076             return;
6077         }
6078         
6079         if(cell.findParent('tfoot', false, true)){
6080             return;
6081         }
6082         
6083         if(cell.findParent('thead', false, true)){
6084             
6085             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6086                 cell = Roo.get(t).findParent('th', false, true);
6087                 if (!cell) {
6088                     Roo.log("failed to find th in thead?");
6089                     Roo.log(e.getTarget());
6090                     return;
6091                 }
6092             }
6093             
6094             var cellIndex = cell.dom.cellIndex;
6095             
6096             var ename = name == 'touchstart' ? 'click' : name;
6097             this.fireEvent("header" + ename, this, cellIndex, e);
6098             
6099             return;
6100         }
6101         
6102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6103             cell = Roo.get(t).findParent('td', false, true);
6104             if (!cell) {
6105                 Roo.log("failed to find th in tbody?");
6106                 Roo.log(e.getTarget());
6107                 return;
6108             }
6109         }
6110         
6111         var row = cell.findParent('tr', false, true);
6112         var cellIndex = cell.dom.cellIndex;
6113         var rowIndex = row.dom.rowIndex - 1;
6114         
6115         if(row !== false){
6116             
6117             this.fireEvent("row" + name, this, rowIndex, e);
6118             
6119             if(cell !== false){
6120             
6121                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6122             }
6123         }
6124         
6125     },
6126     
6127     onMouseover : function(e, el)
6128     {
6129         var cell = Roo.get(el);
6130         
6131         if(!cell){
6132             return;
6133         }
6134         
6135         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6136             cell = cell.findParent('td', false, true);
6137         }
6138         
6139         var row = cell.findParent('tr', false, true);
6140         var cellIndex = cell.dom.cellIndex;
6141         var rowIndex = row.dom.rowIndex - 1; // start from 0
6142         
6143         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6144         
6145     },
6146     
6147     onMouseout : function(e, el)
6148     {
6149         var cell = Roo.get(el);
6150         
6151         if(!cell){
6152             return;
6153         }
6154         
6155         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156             cell = cell.findParent('td', false, true);
6157         }
6158         
6159         var row = cell.findParent('tr', false, true);
6160         var cellIndex = cell.dom.cellIndex;
6161         var rowIndex = row.dom.rowIndex - 1; // start from 0
6162         
6163         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6164         
6165     },
6166     
6167     onClick : function(e, el)
6168     {
6169         var cell = Roo.get(el);
6170         
6171         if(!cell || (!this.cellSelection && !this.rowSelection)){
6172             return;
6173         }
6174         
6175         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176             cell = cell.findParent('td', false, true);
6177         }
6178         
6179         if(!cell || typeof(cell) == 'undefined'){
6180             return;
6181         }
6182         
6183         var row = cell.findParent('tr', false, true);
6184         
6185         if(!row || typeof(row) == 'undefined'){
6186             return;
6187         }
6188         
6189         var cellIndex = cell.dom.cellIndex;
6190         var rowIndex = this.getRowIndex(row);
6191         
6192         // why??? - should these not be based on SelectionModel?
6193         if(this.cellSelection){
6194             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6195         }
6196         
6197         if(this.rowSelection){
6198             this.fireEvent('rowclick', this, row, rowIndex, e);
6199         }
6200         
6201         
6202     },
6203         
6204     onDblClick : function(e,el)
6205     {
6206         var cell = Roo.get(el);
6207         
6208         if(!cell || (!this.cellSelection && !this.rowSelection)){
6209             return;
6210         }
6211         
6212         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213             cell = cell.findParent('td', false, true);
6214         }
6215         
6216         if(!cell || typeof(cell) == 'undefined'){
6217             return;
6218         }
6219         
6220         var row = cell.findParent('tr', false, true);
6221         
6222         if(!row || typeof(row) == 'undefined'){
6223             return;
6224         }
6225         
6226         var cellIndex = cell.dom.cellIndex;
6227         var rowIndex = this.getRowIndex(row);
6228         
6229         if(this.cellSelection){
6230             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6231         }
6232         
6233         if(this.rowSelection){
6234             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6235         }
6236     },
6237     
6238     sort : function(e,el)
6239     {
6240         var col = Roo.get(el);
6241         
6242         if(!col.hasClass('sortable')){
6243             return;
6244         }
6245         
6246         var sort = col.attr('sort');
6247         var dir = 'ASC';
6248         
6249         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6250             dir = 'DESC';
6251         }
6252         
6253         this.store.sortInfo = {field : sort, direction : dir};
6254         
6255         if (this.footer) {
6256             Roo.log("calling footer first");
6257             this.footer.onClick('first');
6258         } else {
6259         
6260             this.store.load({ params : { start : 0 } });
6261         }
6262     },
6263     
6264     renderHeader : function()
6265     {
6266         var header = {
6267             tag: 'thead',
6268             cn : []
6269         };
6270         
6271         var cm = this.cm;
6272         this.totalWidth = 0;
6273         
6274         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6275             
6276             var config = cm.config[i];
6277             
6278             var c = {
6279                 tag: 'th',
6280                 style : '',
6281                 html: cm.getColumnHeader(i)
6282             };
6283             
6284             var hh = '';
6285             
6286             if(typeof(config.sortable) != 'undefined' && config.sortable){
6287                 c.cls = 'sortable';
6288                 c.html = '<i class="glyphicon"></i>' + c.html;
6289             }
6290             
6291             if(typeof(config.lgHeader) != 'undefined'){
6292                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6293             }
6294             
6295             if(typeof(config.mdHeader) != 'undefined'){
6296                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6297             }
6298             
6299             if(typeof(config.smHeader) != 'undefined'){
6300                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6301             }
6302             
6303             if(typeof(config.xsHeader) != 'undefined'){
6304                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6305             }
6306             
6307             if(hh.length){
6308                 c.html = hh;
6309             }
6310             
6311             if(typeof(config.tooltip) != 'undefined'){
6312                 c.tooltip = config.tooltip;
6313             }
6314             
6315             if(typeof(config.colspan) != 'undefined'){
6316                 c.colspan = config.colspan;
6317             }
6318             
6319             if(typeof(config.hidden) != 'undefined' && config.hidden){
6320                 c.style += ' display:none;';
6321             }
6322             
6323             if(typeof(config.dataIndex) != 'undefined'){
6324                 c.sort = config.dataIndex;
6325             }
6326             
6327            
6328             
6329             if(typeof(config.align) != 'undefined' && config.align.length){
6330                 c.style += ' text-align:' + config.align + ';';
6331             }
6332             
6333             if(typeof(config.width) != 'undefined'){
6334                 c.style += ' width:' + config.width + 'px;';
6335                 this.totalWidth += config.width;
6336             } else {
6337                 this.totalWidth += 100; // assume minimum of 100 per column?
6338             }
6339             
6340             if(typeof(config.cls) != 'undefined'){
6341                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6342             }
6343             
6344             ['xs','sm','md','lg'].map(function(size){
6345                 
6346                 if(typeof(config[size]) == 'undefined'){
6347                     return;
6348                 }
6349                 
6350                 if (!config[size]) { // 0 = hidden
6351                     c.cls += ' hidden-' + size;
6352                     return;
6353                 }
6354                 
6355                 c.cls += ' col-' + size + '-' + config[size];
6356
6357             });
6358             
6359             header.cn.push(c)
6360         }
6361         
6362         return header;
6363     },
6364     
6365     renderBody : function()
6366     {
6367         var body = {
6368             tag: 'tbody',
6369             cn : [
6370                 {
6371                     tag: 'tr',
6372                     cn : [
6373                         {
6374                             tag : 'td',
6375                             colspan :  this.cm.getColumnCount()
6376                         }
6377                     ]
6378                 }
6379             ]
6380         };
6381         
6382         return body;
6383     },
6384     
6385     renderFooter : function()
6386     {
6387         var footer = {
6388             tag: 'tfoot',
6389             cn : [
6390                 {
6391                     tag: 'tr',
6392                     cn : [
6393                         {
6394                             tag : 'td',
6395                             colspan :  this.cm.getColumnCount()
6396                         }
6397                     ]
6398                 }
6399             ]
6400         };
6401         
6402         return footer;
6403     },
6404     
6405     
6406     
6407     onLoad : function()
6408     {
6409 //        Roo.log('ds onload');
6410         this.clear();
6411         
6412         var _this = this;
6413         var cm = this.cm;
6414         var ds = this.store;
6415         
6416         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6418             if (_this.store.sortInfo) {
6419                     
6420                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6421                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6422                 }
6423                 
6424                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6425                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6426                 }
6427             }
6428         });
6429         
6430         var tbody =  this.mainBody;
6431               
6432         if(ds.getCount() > 0){
6433             ds.data.each(function(d,rowIndex){
6434                 var row =  this.renderRow(cm, ds, rowIndex);
6435                 
6436                 tbody.createChild(row);
6437                 
6438                 var _this = this;
6439                 
6440                 if(row.cellObjects.length){
6441                     Roo.each(row.cellObjects, function(r){
6442                         _this.renderCellObject(r);
6443                     })
6444                 }
6445                 
6446             }, this);
6447         }
6448         
6449         Roo.each(this.el.select('tbody td', true).elements, function(e){
6450             e.on('mouseover', _this.onMouseover, _this);
6451         });
6452         
6453         Roo.each(this.el.select('tbody td', true).elements, function(e){
6454             e.on('mouseout', _this.onMouseout, _this);
6455         });
6456         this.fireEvent('rowsrendered', this);
6457         //if(this.loadMask){
6458         //    this.maskEl.hide();
6459         //}
6460         
6461         this.autoSize();
6462     },
6463     
6464     
6465     onUpdate : function(ds,record)
6466     {
6467         this.refreshRow(record);
6468         this.autoSize();
6469     },
6470     
6471     onRemove : function(ds, record, index, isUpdate){
6472         if(isUpdate !== true){
6473             this.fireEvent("beforerowremoved", this, index, record);
6474         }
6475         var bt = this.mainBody.dom;
6476         
6477         var rows = this.el.select('tbody > tr', true).elements;
6478         
6479         if(typeof(rows[index]) != 'undefined'){
6480             bt.removeChild(rows[index].dom);
6481         }
6482         
6483 //        if(bt.rows[index]){
6484 //            bt.removeChild(bt.rows[index]);
6485 //        }
6486         
6487         if(isUpdate !== true){
6488             //this.stripeRows(index);
6489             //this.syncRowHeights(index, index);
6490             //this.layout();
6491             this.fireEvent("rowremoved", this, index, record);
6492         }
6493     },
6494     
6495     onAdd : function(ds, records, rowIndex)
6496     {
6497         //Roo.log('on Add called');
6498         // - note this does not handle multiple adding very well..
6499         var bt = this.mainBody.dom;
6500         for (var i =0 ; i < records.length;i++) {
6501             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6502             //Roo.log(records[i]);
6503             //Roo.log(this.store.getAt(rowIndex+i));
6504             this.insertRow(this.store, rowIndex + i, false);
6505             return;
6506         }
6507         
6508     },
6509     
6510     
6511     refreshRow : function(record){
6512         var ds = this.store, index;
6513         if(typeof record == 'number'){
6514             index = record;
6515             record = ds.getAt(index);
6516         }else{
6517             index = ds.indexOf(record);
6518         }
6519         this.insertRow(ds, index, true);
6520         this.autoSize();
6521         this.onRemove(ds, record, index+1, true);
6522         this.autoSize();
6523         //this.syncRowHeights(index, index);
6524         //this.layout();
6525         this.fireEvent("rowupdated", this, index, record);
6526     },
6527     
6528     insertRow : function(dm, rowIndex, isUpdate){
6529         
6530         if(!isUpdate){
6531             this.fireEvent("beforerowsinserted", this, rowIndex);
6532         }
6533             //var s = this.getScrollState();
6534         var row = this.renderRow(this.cm, this.store, rowIndex);
6535         // insert before rowIndex..
6536         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6537         
6538         var _this = this;
6539                 
6540         if(row.cellObjects.length){
6541             Roo.each(row.cellObjects, function(r){
6542                 _this.renderCellObject(r);
6543             })
6544         }
6545             
6546         if(!isUpdate){
6547             this.fireEvent("rowsinserted", this, rowIndex);
6548             //this.syncRowHeights(firstRow, lastRow);
6549             //this.stripeRows(firstRow);
6550             //this.layout();
6551         }
6552         
6553     },
6554     
6555     
6556     getRowDom : function(rowIndex)
6557     {
6558         var rows = this.el.select('tbody > tr', true).elements;
6559         
6560         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6561         
6562     },
6563     // returns the object tree for a tr..
6564   
6565     
6566     renderRow : function(cm, ds, rowIndex) 
6567     {
6568         
6569         var d = ds.getAt(rowIndex);
6570         
6571         var row = {
6572             tag : 'tr',
6573             cn : []
6574         };
6575             
6576         var cellObjects = [];
6577         
6578         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6579             var config = cm.config[i];
6580             
6581             var renderer = cm.getRenderer(i);
6582             var value = '';
6583             var id = false;
6584             
6585             if(typeof(renderer) !== 'undefined'){
6586                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6587             }
6588             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6589             // and are rendered into the cells after the row is rendered - using the id for the element.
6590             
6591             if(typeof(value) === 'object'){
6592                 id = Roo.id();
6593                 cellObjects.push({
6594                     container : id,
6595                     cfg : value 
6596                 })
6597             }
6598             
6599             var rowcfg = {
6600                 record: d,
6601                 rowIndex : rowIndex,
6602                 colIndex : i,
6603                 rowClass : ''
6604             };
6605
6606             this.fireEvent('rowclass', this, rowcfg);
6607             
6608             var td = {
6609                 tag: 'td',
6610                 cls : rowcfg.rowClass,
6611                 style: '',
6612                 html: (typeof(value) === 'object') ? '' : value
6613             };
6614             
6615             if (id) {
6616                 td.id = id;
6617             }
6618             
6619             if(typeof(config.colspan) != 'undefined'){
6620                 td.colspan = config.colspan;
6621             }
6622             
6623             if(typeof(config.hidden) != 'undefined' && config.hidden){
6624                 td.style += ' display:none;';
6625             }
6626             
6627             if(typeof(config.align) != 'undefined' && config.align.length){
6628                 td.style += ' text-align:' + config.align + ';';
6629             }
6630             
6631             if(typeof(config.width) != 'undefined'){
6632                 td.style += ' width:' +  config.width + 'px;';
6633             }
6634             
6635             if(typeof(config.cursor) != 'undefined'){
6636                 td.style += ' cursor:' +  config.cursor + ';';
6637             }
6638             
6639             if(typeof(config.cls) != 'undefined'){
6640                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6641             }
6642             
6643             ['xs','sm','md','lg'].map(function(size){
6644                 
6645                 if(typeof(config[size]) == 'undefined'){
6646                     return;
6647                 }
6648                 
6649                 if (!config[size]) { // 0 = hidden
6650                     td.cls += ' hidden-' + size;
6651                     return;
6652                 }
6653                 
6654                 td.cls += ' col-' + size + '-' + config[size];
6655
6656             });
6657              
6658             row.cn.push(td);
6659            
6660         }
6661         
6662         row.cellObjects = cellObjects;
6663         
6664         return row;
6665           
6666     },
6667     
6668     
6669     
6670     onBeforeLoad : function()
6671     {
6672         //Roo.log('ds onBeforeLoad');
6673         
6674         //this.clear();
6675         
6676         //if(this.loadMask){
6677         //    this.maskEl.show();
6678         //}
6679     },
6680      /**
6681      * Remove all rows
6682      */
6683     clear : function()
6684     {
6685         this.el.select('tbody', true).first().dom.innerHTML = '';
6686     },
6687     /**
6688      * Show or hide a row.
6689      * @param {Number} rowIndex to show or hide
6690      * @param {Boolean} state hide
6691      */
6692     setRowVisibility : function(rowIndex, state)
6693     {
6694         var bt = this.mainBody.dom;
6695         
6696         var rows = this.el.select('tbody > tr', true).elements;
6697         
6698         if(typeof(rows[rowIndex]) == 'undefined'){
6699             return;
6700         }
6701         rows[rowIndex].dom.style.display = state ? '' : 'none';
6702     },
6703     
6704     
6705     getSelectionModel : function(){
6706         if(!this.selModel){
6707             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6708         }
6709         return this.selModel;
6710     },
6711     /*
6712      * Render the Roo.bootstrap object from renderder
6713      */
6714     renderCellObject : function(r)
6715     {
6716         var _this = this;
6717         
6718         var t = r.cfg.render(r.container);
6719         
6720         if(r.cfg.cn){
6721             Roo.each(r.cfg.cn, function(c){
6722                 var child = {
6723                     container: t.getChildContainer(),
6724                     cfg: c
6725                 };
6726                 _this.renderCellObject(child);
6727             })
6728         }
6729     },
6730     
6731     getRowIndex : function(row)
6732     {
6733         var rowIndex = -1;
6734         
6735         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6736             if(el != row){
6737                 return;
6738             }
6739             
6740             rowIndex = index;
6741         });
6742         
6743         return rowIndex;
6744     },
6745      /**
6746      * Returns the grid's underlying element = used by panel.Grid
6747      * @return {Element} The element
6748      */
6749     getGridEl : function(){
6750         return this.el;
6751     },
6752      /**
6753      * Forces a resize - used by panel.Grid
6754      * @return {Element} The element
6755      */
6756     autoSize : function()
6757     {
6758         //var ctr = Roo.get(this.container.dom.parentElement);
6759         var ctr = Roo.get(this.el.dom);
6760         
6761         var thd = this.getGridEl().select('thead',true).first();
6762         var tbd = this.getGridEl().select('tbody', true).first();
6763         var tfd = this.getGridEl().select('tfoot', true).first();
6764         
6765         var cw = ctr.getWidth();
6766         
6767         if (tbd) {
6768             
6769             tbd.setSize(ctr.getWidth(),
6770                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6771             );
6772             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6773             cw -= barsize;
6774         }
6775         cw = Math.max(cw, this.totalWidth);
6776         this.getGridEl().select('tr',true).setWidth(cw);
6777         // resize 'expandable coloumn?
6778         
6779         return; // we doe not have a view in this design..
6780         
6781     },
6782     onBodyScroll: function()
6783     {
6784         
6785         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6786         this.mainHead.setStyle({
6787                     'position' : 'relative',
6788                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6789         });
6790         
6791         
6792     }
6793 });
6794
6795  
6796
6797  /*
6798  * - LGPL
6799  *
6800  * table cell
6801  * 
6802  */
6803
6804 /**
6805  * @class Roo.bootstrap.TableCell
6806  * @extends Roo.bootstrap.Component
6807  * Bootstrap TableCell class
6808  * @cfg {String} html cell contain text
6809  * @cfg {String} cls cell class
6810  * @cfg {String} tag cell tag (td|th) default td
6811  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6812  * @cfg {String} align Aligns the content in a cell
6813  * @cfg {String} axis Categorizes cells
6814  * @cfg {String} bgcolor Specifies the background color of a cell
6815  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6816  * @cfg {Number} colspan Specifies the number of columns a cell should span
6817  * @cfg {String} headers Specifies one or more header cells a cell is related to
6818  * @cfg {Number} height Sets the height of a cell
6819  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6820  * @cfg {Number} rowspan Sets the number of rows a cell should span
6821  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6822  * @cfg {String} valign Vertical aligns the content in a cell
6823  * @cfg {Number} width Specifies the width of a cell
6824  * 
6825  * @constructor
6826  * Create a new TableCell
6827  * @param {Object} config The config object
6828  */
6829
6830 Roo.bootstrap.TableCell = function(config){
6831     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6832 };
6833
6834 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6835     
6836     html: false,
6837     cls: false,
6838     tag: false,
6839     abbr: false,
6840     align: false,
6841     axis: false,
6842     bgcolor: false,
6843     charoff: false,
6844     colspan: false,
6845     headers: false,
6846     height: false,
6847     nowrap: false,
6848     rowspan: false,
6849     scope: false,
6850     valign: false,
6851     width: false,
6852     
6853     
6854     getAutoCreate : function(){
6855         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6856         
6857         cfg = {
6858             tag: 'td'
6859         };
6860         
6861         if(this.tag){
6862             cfg.tag = this.tag;
6863         }
6864         
6865         if (this.html) {
6866             cfg.html=this.html
6867         }
6868         if (this.cls) {
6869             cfg.cls=this.cls
6870         }
6871         if (this.abbr) {
6872             cfg.abbr=this.abbr
6873         }
6874         if (this.align) {
6875             cfg.align=this.align
6876         }
6877         if (this.axis) {
6878             cfg.axis=this.axis
6879         }
6880         if (this.bgcolor) {
6881             cfg.bgcolor=this.bgcolor
6882         }
6883         if (this.charoff) {
6884             cfg.charoff=this.charoff
6885         }
6886         if (this.colspan) {
6887             cfg.colspan=this.colspan
6888         }
6889         if (this.headers) {
6890             cfg.headers=this.headers
6891         }
6892         if (this.height) {
6893             cfg.height=this.height
6894         }
6895         if (this.nowrap) {
6896             cfg.nowrap=this.nowrap
6897         }
6898         if (this.rowspan) {
6899             cfg.rowspan=this.rowspan
6900         }
6901         if (this.scope) {
6902             cfg.scope=this.scope
6903         }
6904         if (this.valign) {
6905             cfg.valign=this.valign
6906         }
6907         if (this.width) {
6908             cfg.width=this.width
6909         }
6910         
6911         
6912         return cfg;
6913     }
6914    
6915 });
6916
6917  
6918
6919  /*
6920  * - LGPL
6921  *
6922  * table row
6923  * 
6924  */
6925
6926 /**
6927  * @class Roo.bootstrap.TableRow
6928  * @extends Roo.bootstrap.Component
6929  * Bootstrap TableRow class
6930  * @cfg {String} cls row class
6931  * @cfg {String} align Aligns the content in a table row
6932  * @cfg {String} bgcolor Specifies a background color for a table row
6933  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6934  * @cfg {String} valign Vertical aligns the content in a table row
6935  * 
6936  * @constructor
6937  * Create a new TableRow
6938  * @param {Object} config The config object
6939  */
6940
6941 Roo.bootstrap.TableRow = function(config){
6942     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6943 };
6944
6945 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6946     
6947     cls: false,
6948     align: false,
6949     bgcolor: false,
6950     charoff: false,
6951     valign: false,
6952     
6953     getAutoCreate : function(){
6954         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6955         
6956         cfg = {
6957             tag: 'tr'
6958         };
6959             
6960         if(this.cls){
6961             cfg.cls = this.cls;
6962         }
6963         if(this.align){
6964             cfg.align = this.align;
6965         }
6966         if(this.bgcolor){
6967             cfg.bgcolor = this.bgcolor;
6968         }
6969         if(this.charoff){
6970             cfg.charoff = this.charoff;
6971         }
6972         if(this.valign){
6973             cfg.valign = this.valign;
6974         }
6975         
6976         return cfg;
6977     }
6978    
6979 });
6980
6981  
6982
6983  /*
6984  * - LGPL
6985  *
6986  * table body
6987  * 
6988  */
6989
6990 /**
6991  * @class Roo.bootstrap.TableBody
6992  * @extends Roo.bootstrap.Component
6993  * Bootstrap TableBody class
6994  * @cfg {String} cls element class
6995  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6996  * @cfg {String} align Aligns the content inside the element
6997  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6998  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6999  * 
7000  * @constructor
7001  * Create a new TableBody
7002  * @param {Object} config The config object
7003  */
7004
7005 Roo.bootstrap.TableBody = function(config){
7006     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7007 };
7008
7009 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7010     
7011     cls: false,
7012     tag: false,
7013     align: false,
7014     charoff: false,
7015     valign: false,
7016     
7017     getAutoCreate : function(){
7018         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7019         
7020         cfg = {
7021             tag: 'tbody'
7022         };
7023             
7024         if (this.cls) {
7025             cfg.cls=this.cls
7026         }
7027         if(this.tag){
7028             cfg.tag = this.tag;
7029         }
7030         
7031         if(this.align){
7032             cfg.align = this.align;
7033         }
7034         if(this.charoff){
7035             cfg.charoff = this.charoff;
7036         }
7037         if(this.valign){
7038             cfg.valign = this.valign;
7039         }
7040         
7041         return cfg;
7042     }
7043     
7044     
7045 //    initEvents : function()
7046 //    {
7047 //        
7048 //        if(!this.store){
7049 //            return;
7050 //        }
7051 //        
7052 //        this.store = Roo.factory(this.store, Roo.data);
7053 //        this.store.on('load', this.onLoad, this);
7054 //        
7055 //        this.store.load();
7056 //        
7057 //    },
7058 //    
7059 //    onLoad: function () 
7060 //    {   
7061 //        this.fireEvent('load', this);
7062 //    }
7063 //    
7064 //   
7065 });
7066
7067  
7068
7069  /*
7070  * Based on:
7071  * Ext JS Library 1.1.1
7072  * Copyright(c) 2006-2007, Ext JS, LLC.
7073  *
7074  * Originally Released Under LGPL - original licence link has changed is not relivant.
7075  *
7076  * Fork - LGPL
7077  * <script type="text/javascript">
7078  */
7079
7080 // as we use this in bootstrap.
7081 Roo.namespace('Roo.form');
7082  /**
7083  * @class Roo.form.Action
7084  * Internal Class used to handle form actions
7085  * @constructor
7086  * @param {Roo.form.BasicForm} el The form element or its id
7087  * @param {Object} config Configuration options
7088  */
7089
7090  
7091  
7092 // define the action interface
7093 Roo.form.Action = function(form, options){
7094     this.form = form;
7095     this.options = options || {};
7096 };
7097 /**
7098  * Client Validation Failed
7099  * @const 
7100  */
7101 Roo.form.Action.CLIENT_INVALID = 'client';
7102 /**
7103  * Server Validation Failed
7104  * @const 
7105  */
7106 Roo.form.Action.SERVER_INVALID = 'server';
7107  /**
7108  * Connect to Server Failed
7109  * @const 
7110  */
7111 Roo.form.Action.CONNECT_FAILURE = 'connect';
7112 /**
7113  * Reading Data from Server Failed
7114  * @const 
7115  */
7116 Roo.form.Action.LOAD_FAILURE = 'load';
7117
7118 Roo.form.Action.prototype = {
7119     type : 'default',
7120     failureType : undefined,
7121     response : undefined,
7122     result : undefined,
7123
7124     // interface method
7125     run : function(options){
7126
7127     },
7128
7129     // interface method
7130     success : function(response){
7131
7132     },
7133
7134     // interface method
7135     handleResponse : function(response){
7136
7137     },
7138
7139     // default connection failure
7140     failure : function(response){
7141         
7142         this.response = response;
7143         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7144         this.form.afterAction(this, false);
7145     },
7146
7147     processResponse : function(response){
7148         this.response = response;
7149         if(!response.responseText){
7150             return true;
7151         }
7152         this.result = this.handleResponse(response);
7153         return this.result;
7154     },
7155
7156     // utility functions used internally
7157     getUrl : function(appendParams){
7158         var url = this.options.url || this.form.url || this.form.el.dom.action;
7159         if(appendParams){
7160             var p = this.getParams();
7161             if(p){
7162                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7163             }
7164         }
7165         return url;
7166     },
7167
7168     getMethod : function(){
7169         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7170     },
7171
7172     getParams : function(){
7173         var bp = this.form.baseParams;
7174         var p = this.options.params;
7175         if(p){
7176             if(typeof p == "object"){
7177                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7178             }else if(typeof p == 'string' && bp){
7179                 p += '&' + Roo.urlEncode(bp);
7180             }
7181         }else if(bp){
7182             p = Roo.urlEncode(bp);
7183         }
7184         return p;
7185     },
7186
7187     createCallback : function(){
7188         return {
7189             success: this.success,
7190             failure: this.failure,
7191             scope: this,
7192             timeout: (this.form.timeout*1000),
7193             upload: this.form.fileUpload ? this.success : undefined
7194         };
7195     }
7196 };
7197
7198 Roo.form.Action.Submit = function(form, options){
7199     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7200 };
7201
7202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7203     type : 'submit',
7204
7205     haveProgress : false,
7206     uploadComplete : false,
7207     
7208     // uploadProgress indicator.
7209     uploadProgress : function()
7210     {
7211         if (!this.form.progressUrl) {
7212             return;
7213         }
7214         
7215         if (!this.haveProgress) {
7216             Roo.MessageBox.progress("Uploading", "Uploading");
7217         }
7218         if (this.uploadComplete) {
7219            Roo.MessageBox.hide();
7220            return;
7221         }
7222         
7223         this.haveProgress = true;
7224    
7225         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7226         
7227         var c = new Roo.data.Connection();
7228         c.request({
7229             url : this.form.progressUrl,
7230             params: {
7231                 id : uid
7232             },
7233             method: 'GET',
7234             success : function(req){
7235                //console.log(data);
7236                 var rdata = false;
7237                 var edata;
7238                 try  {
7239                    rdata = Roo.decode(req.responseText)
7240                 } catch (e) {
7241                     Roo.log("Invalid data from server..");
7242                     Roo.log(edata);
7243                     return;
7244                 }
7245                 if (!rdata || !rdata.success) {
7246                     Roo.log(rdata);
7247                     Roo.MessageBox.alert(Roo.encode(rdata));
7248                     return;
7249                 }
7250                 var data = rdata.data;
7251                 
7252                 if (this.uploadComplete) {
7253                    Roo.MessageBox.hide();
7254                    return;
7255                 }
7256                    
7257                 if (data){
7258                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7259                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7260                     );
7261                 }
7262                 this.uploadProgress.defer(2000,this);
7263             },
7264        
7265             failure: function(data) {
7266                 Roo.log('progress url failed ');
7267                 Roo.log(data);
7268             },
7269             scope : this
7270         });
7271            
7272     },
7273     
7274     
7275     run : function()
7276     {
7277         // run get Values on the form, so it syncs any secondary forms.
7278         this.form.getValues();
7279         
7280         var o = this.options;
7281         var method = this.getMethod();
7282         var isPost = method == 'POST';
7283         if(o.clientValidation === false || this.form.isValid()){
7284             
7285             if (this.form.progressUrl) {
7286                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7287                     (new Date() * 1) + '' + Math.random());
7288                     
7289             } 
7290             
7291             
7292             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7293                 form:this.form.el.dom,
7294                 url:this.getUrl(!isPost),
7295                 method: method,
7296                 params:isPost ? this.getParams() : null,
7297                 isUpload: this.form.fileUpload
7298             }));
7299             
7300             this.uploadProgress();
7301
7302         }else if (o.clientValidation !== false){ // client validation failed
7303             this.failureType = Roo.form.Action.CLIENT_INVALID;
7304             this.form.afterAction(this, false);
7305         }
7306     },
7307
7308     success : function(response)
7309     {
7310         this.uploadComplete= true;
7311         if (this.haveProgress) {
7312             Roo.MessageBox.hide();
7313         }
7314         
7315         
7316         var result = this.processResponse(response);
7317         if(result === true || result.success){
7318             this.form.afterAction(this, true);
7319             return;
7320         }
7321         if(result.errors){
7322             this.form.markInvalid(result.errors);
7323             this.failureType = Roo.form.Action.SERVER_INVALID;
7324         }
7325         this.form.afterAction(this, false);
7326     },
7327     failure : function(response)
7328     {
7329         this.uploadComplete= true;
7330         if (this.haveProgress) {
7331             Roo.MessageBox.hide();
7332         }
7333         
7334         this.response = response;
7335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336         this.form.afterAction(this, false);
7337     },
7338     
7339     handleResponse : function(response){
7340         if(this.form.errorReader){
7341             var rs = this.form.errorReader.read(response);
7342             var errors = [];
7343             if(rs.records){
7344                 for(var i = 0, len = rs.records.length; i < len; i++) {
7345                     var r = rs.records[i];
7346                     errors[i] = r.data;
7347                 }
7348             }
7349             if(errors.length < 1){
7350                 errors = null;
7351             }
7352             return {
7353                 success : rs.success,
7354                 errors : errors
7355             };
7356         }
7357         var ret = false;
7358         try {
7359             ret = Roo.decode(response.responseText);
7360         } catch (e) {
7361             ret = {
7362                 success: false,
7363                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7364                 errors : []
7365             };
7366         }
7367         return ret;
7368         
7369     }
7370 });
7371
7372
7373 Roo.form.Action.Load = function(form, options){
7374     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7375     this.reader = this.form.reader;
7376 };
7377
7378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7379     type : 'load',
7380
7381     run : function(){
7382         
7383         Roo.Ajax.request(Roo.apply(
7384                 this.createCallback(), {
7385                     method:this.getMethod(),
7386                     url:this.getUrl(false),
7387                     params:this.getParams()
7388         }));
7389     },
7390
7391     success : function(response){
7392         
7393         var result = this.processResponse(response);
7394         if(result === true || !result.success || !result.data){
7395             this.failureType = Roo.form.Action.LOAD_FAILURE;
7396             this.form.afterAction(this, false);
7397             return;
7398         }
7399         this.form.clearInvalid();
7400         this.form.setValues(result.data);
7401         this.form.afterAction(this, true);
7402     },
7403
7404     handleResponse : function(response){
7405         if(this.form.reader){
7406             var rs = this.form.reader.read(response);
7407             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7408             return {
7409                 success : rs.success,
7410                 data : data
7411             };
7412         }
7413         return Roo.decode(response.responseText);
7414     }
7415 });
7416
7417 Roo.form.Action.ACTION_TYPES = {
7418     'load' : Roo.form.Action.Load,
7419     'submit' : Roo.form.Action.Submit
7420 };/*
7421  * - LGPL
7422  *
7423  * form
7424  *
7425  */
7426
7427 /**
7428  * @class Roo.bootstrap.Form
7429  * @extends Roo.bootstrap.Component
7430  * Bootstrap Form class
7431  * @cfg {String} method  GET | POST (default POST)
7432  * @cfg {String} labelAlign top | left (default top)
7433  * @cfg {String} align left  | right - for navbars
7434  * @cfg {Boolean} loadMask load mask when submit (default true)
7435
7436  *
7437  * @constructor
7438  * Create a new Form
7439  * @param {Object} config The config object
7440  */
7441
7442
7443 Roo.bootstrap.Form = function(config){
7444     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7445     this.addEvents({
7446         /**
7447          * @event clientvalidation
7448          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7449          * @param {Form} this
7450          * @param {Boolean} valid true if the form has passed client-side validation
7451          */
7452         clientvalidation: true,
7453         /**
7454          * @event beforeaction
7455          * Fires before any action is performed. Return false to cancel the action.
7456          * @param {Form} this
7457          * @param {Action} action The action to be performed
7458          */
7459         beforeaction: true,
7460         /**
7461          * @event actionfailed
7462          * Fires when an action fails.
7463          * @param {Form} this
7464          * @param {Action} action The action that failed
7465          */
7466         actionfailed : true,
7467         /**
7468          * @event actioncomplete
7469          * Fires when an action is completed.
7470          * @param {Form} this
7471          * @param {Action} action The action that completed
7472          */
7473         actioncomplete : true
7474     });
7475
7476 };
7477
7478 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7479
7480      /**
7481      * @cfg {String} method
7482      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7483      */
7484     method : 'POST',
7485     /**
7486      * @cfg {String} url
7487      * The URL to use for form actions if one isn't supplied in the action options.
7488      */
7489     /**
7490      * @cfg {Boolean} fileUpload
7491      * Set to true if this form is a file upload.
7492      */
7493
7494     /**
7495      * @cfg {Object} baseParams
7496      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7497      */
7498
7499     /**
7500      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7501      */
7502     timeout: 30,
7503     /**
7504      * @cfg {Sting} align (left|right) for navbar forms
7505      */
7506     align : 'left',
7507
7508     // private
7509     activeAction : null,
7510
7511     /**
7512      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7513      * element by passing it or its id or mask the form itself by passing in true.
7514      * @type Mixed
7515      */
7516     waitMsgTarget : false,
7517
7518     loadMask : true,
7519     
7520     /**
7521      * @cfg {Boolean} errPopover (true|false) default false
7522      */
7523     errPopover : false,
7524
7525     getAutoCreate : function(){
7526
7527         var cfg = {
7528             tag: 'form',
7529             method : this.method || 'POST',
7530             id : this.id || Roo.id(),
7531             cls : ''
7532         };
7533         if (this.parent().xtype.match(/^Nav/)) {
7534             cfg.cls = 'navbar-form navbar-' + this.align;
7535
7536         }
7537
7538         if (this.labelAlign == 'left' ) {
7539             cfg.cls += ' form-horizontal';
7540         }
7541
7542
7543         return cfg;
7544     },
7545     initEvents : function()
7546     {
7547         this.el.on('submit', this.onSubmit, this);
7548         // this was added as random key presses on the form where triggering form submit.
7549         this.el.on('keypress', function(e) {
7550             if (e.getCharCode() != 13) {
7551                 return true;
7552             }
7553             // we might need to allow it for textareas.. and some other items.
7554             // check e.getTarget().
7555
7556             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7557                 return true;
7558             }
7559
7560             Roo.log("keypress blocked");
7561
7562             e.preventDefault();
7563             return false;
7564         });
7565
7566     },
7567     // private
7568     onSubmit : function(e){
7569         e.stopEvent();
7570     },
7571
7572      /**
7573      * Returns true if client-side validation on the form is successful.
7574      * @return Boolean
7575      */
7576     isValid : function(){
7577         var items = this.getItems();
7578         var valid = true;
7579         var target = false;
7580         items.each(function(f){
7581            if(!f.validate()){
7582                valid = false;
7583                
7584                if(!target){
7585                    target = f;
7586                }
7587            }
7588         });
7589         
7590         if(this.errPopover && !valid){
7591             this.showErrPopover(target);
7592         }
7593         
7594         return valid;
7595     },
7596     
7597     showErrPopover : function(target)
7598     {
7599         if(!this.errPopover){
7600             return;
7601         }
7602         
7603 //        Roo.log(target.el);
7604 //        Roo.log(target.inputEl());
7605 //        
7606 //        target.inputEl().focus();
7607 //    
7608 //        Roo.get(document.body).mask();
7609 //        
7610 //        var m = this.el.createChild({
7611 //            tag : 'div'
7612 //        }, false, true);
7613 //        
7614 //        Roo.log(Roo.get(m));
7615         
7616     },
7617     
7618     /**
7619      * Returns true if any fields in this form have changed since their original load.
7620      * @return Boolean
7621      */
7622     isDirty : function(){
7623         var dirty = false;
7624         var items = this.getItems();
7625         items.each(function(f){
7626            if(f.isDirty()){
7627                dirty = true;
7628                return false;
7629            }
7630            return true;
7631         });
7632         return dirty;
7633     },
7634      /**
7635      * Performs a predefined action (submit or load) or custom actions you define on this form.
7636      * @param {String} actionName The name of the action type
7637      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7638      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7639      * accept other config options):
7640      * <pre>
7641 Property          Type             Description
7642 ----------------  ---------------  ----------------------------------------------------------------------------------
7643 url               String           The url for the action (defaults to the form's url)
7644 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7645 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7646 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7647                                    validate the form on the client (defaults to false)
7648      * </pre>
7649      * @return {BasicForm} this
7650      */
7651     doAction : function(action, options){
7652         if(typeof action == 'string'){
7653             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7654         }
7655         if(this.fireEvent('beforeaction', this, action) !== false){
7656             this.beforeAction(action);
7657             action.run.defer(100, action);
7658         }
7659         return this;
7660     },
7661
7662     // private
7663     beforeAction : function(action){
7664         var o = action.options;
7665
7666         if(this.loadMask){
7667             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7668         }
7669         // not really supported yet.. ??
7670
7671         //if(this.waitMsgTarget === true){
7672         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7673         //}else if(this.waitMsgTarget){
7674         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7675         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7676         //}else {
7677         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7678        // }
7679
7680     },
7681
7682     // private
7683     afterAction : function(action, success){
7684         this.activeAction = null;
7685         var o = action.options;
7686
7687         //if(this.waitMsgTarget === true){
7688             this.el.unmask();
7689         //}else if(this.waitMsgTarget){
7690         //    this.waitMsgTarget.unmask();
7691         //}else{
7692         //    Roo.MessageBox.updateProgress(1);
7693         //    Roo.MessageBox.hide();
7694        // }
7695         //
7696         if(success){
7697             if(o.reset){
7698                 this.reset();
7699             }
7700             Roo.callback(o.success, o.scope, [this, action]);
7701             this.fireEvent('actioncomplete', this, action);
7702
7703         }else{
7704
7705             // failure condition..
7706             // we have a scenario where updates need confirming.
7707             // eg. if a locking scenario exists..
7708             // we look for { errors : { needs_confirm : true }} in the response.
7709             if (
7710                 (typeof(action.result) != 'undefined')  &&
7711                 (typeof(action.result.errors) != 'undefined')  &&
7712                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7713            ){
7714                 var _t = this;
7715                 Roo.log("not supported yet");
7716                  /*
7717
7718                 Roo.MessageBox.confirm(
7719                     "Change requires confirmation",
7720                     action.result.errorMsg,
7721                     function(r) {
7722                         if (r != 'yes') {
7723                             return;
7724                         }
7725                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7726                     }
7727
7728                 );
7729                 */
7730
7731
7732                 return;
7733             }
7734
7735             Roo.callback(o.failure, o.scope, [this, action]);
7736             // show an error message if no failed handler is set..
7737             if (!this.hasListener('actionfailed')) {
7738                 Roo.log("need to add dialog support");
7739                 /*
7740                 Roo.MessageBox.alert("Error",
7741                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7742                         action.result.errorMsg :
7743                         "Saving Failed, please check your entries or try again"
7744                 );
7745                 */
7746             }
7747
7748             this.fireEvent('actionfailed', this, action);
7749         }
7750
7751     },
7752     /**
7753      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7754      * @param {String} id The value to search for
7755      * @return Field
7756      */
7757     findField : function(id){
7758         var items = this.getItems();
7759         var field = items.get(id);
7760         if(!field){
7761              items.each(function(f){
7762                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7763                     field = f;
7764                     return false;
7765                 }
7766                 return true;
7767             });
7768         }
7769         return field || null;
7770     },
7771      /**
7772      * Mark fields in this form invalid in bulk.
7773      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7774      * @return {BasicForm} this
7775      */
7776     markInvalid : function(errors){
7777         if(errors instanceof Array){
7778             for(var i = 0, len = errors.length; i < len; i++){
7779                 var fieldError = errors[i];
7780                 var f = this.findField(fieldError.id);
7781                 if(f){
7782                     f.markInvalid(fieldError.msg);
7783                 }
7784             }
7785         }else{
7786             var field, id;
7787             for(id in errors){
7788                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7789                     field.markInvalid(errors[id]);
7790                 }
7791             }
7792         }
7793         //Roo.each(this.childForms || [], function (f) {
7794         //    f.markInvalid(errors);
7795         //});
7796
7797         return this;
7798     },
7799
7800     /**
7801      * Set values for fields in this form in bulk.
7802      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7803      * @return {BasicForm} this
7804      */
7805     setValues : function(values){
7806         if(values instanceof Array){ // array of objects
7807             for(var i = 0, len = values.length; i < len; i++){
7808                 var v = values[i];
7809                 var f = this.findField(v.id);
7810                 if(f){
7811                     f.setValue(v.value);
7812                     if(this.trackResetOnLoad){
7813                         f.originalValue = f.getValue();
7814                     }
7815                 }
7816             }
7817         }else{ // object hash
7818             var field, id;
7819             for(id in values){
7820                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7821
7822                     if (field.setFromData &&
7823                         field.valueField &&
7824                         field.displayField &&
7825                         // combos' with local stores can
7826                         // be queried via setValue()
7827                         // to set their value..
7828                         (field.store && !field.store.isLocal)
7829                         ) {
7830                         // it's a combo
7831                         var sd = { };
7832                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7833                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7834                         field.setFromData(sd);
7835
7836                     } else {
7837                         field.setValue(values[id]);
7838                     }
7839
7840
7841                     if(this.trackResetOnLoad){
7842                         field.originalValue = field.getValue();
7843                     }
7844                 }
7845             }
7846         }
7847
7848         //Roo.each(this.childForms || [], function (f) {
7849         //    f.setValues(values);
7850         //});
7851
7852         return this;
7853     },
7854
7855     /**
7856      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7857      * they are returned as an array.
7858      * @param {Boolean} asString
7859      * @return {Object}
7860      */
7861     getValues : function(asString){
7862         //if (this.childForms) {
7863             // copy values from the child forms
7864         //    Roo.each(this.childForms, function (f) {
7865         //        this.setValues(f.getValues());
7866         //    }, this);
7867         //}
7868
7869
7870
7871         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7872         if(asString === true){
7873             return fs;
7874         }
7875         return Roo.urlDecode(fs);
7876     },
7877
7878     /**
7879      * Returns the fields in this form as an object with key/value pairs.
7880      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7881      * @return {Object}
7882      */
7883     getFieldValues : function(with_hidden)
7884     {
7885         var items = this.getItems();
7886         var ret = {};
7887         items.each(function(f){
7888             if (!f.getName()) {
7889                 return;
7890             }
7891             var v = f.getValue();
7892             if (f.inputType =='radio') {
7893                 if (typeof(ret[f.getName()]) == 'undefined') {
7894                     ret[f.getName()] = ''; // empty..
7895                 }
7896
7897                 if (!f.el.dom.checked) {
7898                     return;
7899
7900                 }
7901                 v = f.el.dom.value;
7902
7903             }
7904
7905             // not sure if this supported any more..
7906             if ((typeof(v) == 'object') && f.getRawValue) {
7907                 v = f.getRawValue() ; // dates..
7908             }
7909             // combo boxes where name != hiddenName...
7910             if (f.name != f.getName()) {
7911                 ret[f.name] = f.getRawValue();
7912             }
7913             ret[f.getName()] = v;
7914         });
7915
7916         return ret;
7917     },
7918
7919     /**
7920      * Clears all invalid messages in this form.
7921      * @return {BasicForm} this
7922      */
7923     clearInvalid : function(){
7924         var items = this.getItems();
7925
7926         items.each(function(f){
7927            f.clearInvalid();
7928         });
7929
7930
7931
7932         return this;
7933     },
7934
7935     /**
7936      * Resets this form.
7937      * @return {BasicForm} this
7938      */
7939     reset : function(){
7940         var items = this.getItems();
7941         items.each(function(f){
7942             f.reset();
7943         });
7944
7945         Roo.each(this.childForms || [], function (f) {
7946             f.reset();
7947         });
7948
7949
7950         return this;
7951     },
7952     getItems : function()
7953     {
7954         var r=new Roo.util.MixedCollection(false, function(o){
7955             return o.id || (o.id = Roo.id());
7956         });
7957         var iter = function(el) {
7958             if (el.inputEl) {
7959                 r.add(el);
7960             }
7961             if (!el.items) {
7962                 return;
7963             }
7964             Roo.each(el.items,function(e) {
7965                 iter(e);
7966             });
7967
7968
7969         };
7970
7971         iter(this);
7972         return r;
7973
7974
7975
7976
7977     }
7978
7979 });
7980 /*
7981  * Based on:
7982  * Ext JS Library 1.1.1
7983  * Copyright(c) 2006-2007, Ext JS, LLC.
7984  *
7985  * Originally Released Under LGPL - original licence link has changed is not relivant.
7986  *
7987  * Fork - LGPL
7988  * <script type="text/javascript">
7989  */
7990 /**
7991  * @class Roo.form.VTypes
7992  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7993  * @singleton
7994  */
7995 Roo.form.VTypes = function(){
7996     // closure these in so they are only created once.
7997     var alpha = /^[a-zA-Z_]+$/;
7998     var alphanum = /^[a-zA-Z0-9_]+$/;
7999     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8000     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8001
8002     // All these messages and functions are configurable
8003     return {
8004         /**
8005          * The function used to validate email addresses
8006          * @param {String} value The email address
8007          */
8008         'email' : function(v){
8009             return email.test(v);
8010         },
8011         /**
8012          * The error text to display when the email validation function returns false
8013          * @type String
8014          */
8015         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8016         /**
8017          * The keystroke filter mask to be applied on email input
8018          * @type RegExp
8019          */
8020         'emailMask' : /[a-z0-9_\.\-@]/i,
8021
8022         /**
8023          * The function used to validate URLs
8024          * @param {String} value The URL
8025          */
8026         'url' : function(v){
8027             return url.test(v);
8028         },
8029         /**
8030          * The error text to display when the url validation function returns false
8031          * @type String
8032          */
8033         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8034         
8035         /**
8036          * The function used to validate alpha values
8037          * @param {String} value The value
8038          */
8039         'alpha' : function(v){
8040             return alpha.test(v);
8041         },
8042         /**
8043          * The error text to display when the alpha validation function returns false
8044          * @type String
8045          */
8046         'alphaText' : 'This field should only contain letters and _',
8047         /**
8048          * The keystroke filter mask to be applied on alpha input
8049          * @type RegExp
8050          */
8051         'alphaMask' : /[a-z_]/i,
8052
8053         /**
8054          * The function used to validate alphanumeric values
8055          * @param {String} value The value
8056          */
8057         'alphanum' : function(v){
8058             return alphanum.test(v);
8059         },
8060         /**
8061          * The error text to display when the alphanumeric validation function returns false
8062          * @type String
8063          */
8064         'alphanumText' : 'This field should only contain letters, numbers and _',
8065         /**
8066          * The keystroke filter mask to be applied on alphanumeric input
8067          * @type RegExp
8068          */
8069         'alphanumMask' : /[a-z0-9_]/i
8070     };
8071 }();/*
8072  * - LGPL
8073  *
8074  * Input
8075  * 
8076  */
8077
8078 /**
8079  * @class Roo.bootstrap.Input
8080  * @extends Roo.bootstrap.Component
8081  * Bootstrap Input class
8082  * @cfg {Boolean} disabled is it disabled
8083  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8084  * @cfg {String} name name of the input
8085  * @cfg {string} fieldLabel - the label associated
8086  * @cfg {string} placeholder - placeholder to put in text.
8087  * @cfg {string}  before - input group add on before
8088  * @cfg {string} after - input group add on after
8089  * @cfg {string} size - (lg|sm) or leave empty..
8090  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8091  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8092  * @cfg {Number} md colspan out of 12 for computer-sized screens
8093  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8094  * @cfg {string} value default value of the input
8095  * @cfg {Number} labelWidth set the width of label (0-12)
8096  * @cfg {String} labelAlign (top|left)
8097  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8098  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8099  * @cfg {String} indicatorpos (left|right) default left
8100
8101  * @cfg {String} align (left|center|right) Default left
8102  * @cfg {Boolean} forceFeedback (true|false) Default false
8103  * 
8104  * 
8105  * 
8106  * 
8107  * @constructor
8108  * Create a new Input
8109  * @param {Object} config The config object
8110  */
8111
8112 Roo.bootstrap.Input = function(config){
8113     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8114    
8115         this.addEvents({
8116             /**
8117              * @event focus
8118              * Fires when this field receives input focus.
8119              * @param {Roo.form.Field} this
8120              */
8121             focus : true,
8122             /**
8123              * @event blur
8124              * Fires when this field loses input focus.
8125              * @param {Roo.form.Field} this
8126              */
8127             blur : true,
8128             /**
8129              * @event specialkey
8130              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8131              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8132              * @param {Roo.form.Field} this
8133              * @param {Roo.EventObject} e The event object
8134              */
8135             specialkey : true,
8136             /**
8137              * @event change
8138              * Fires just before the field blurs if the field value has changed.
8139              * @param {Roo.form.Field} this
8140              * @param {Mixed} newValue The new value
8141              * @param {Mixed} oldValue The original value
8142              */
8143             change : true,
8144             /**
8145              * @event invalid
8146              * Fires after the field has been marked as invalid.
8147              * @param {Roo.form.Field} this
8148              * @param {String} msg The validation message
8149              */
8150             invalid : true,
8151             /**
8152              * @event valid
8153              * Fires after the field has been validated with no errors.
8154              * @param {Roo.form.Field} this
8155              */
8156             valid : true,
8157              /**
8158              * @event keyup
8159              * Fires after the key up
8160              * @param {Roo.form.Field} this
8161              * @param {Roo.EventObject}  e The event Object
8162              */
8163             keyup : true
8164         });
8165 };
8166
8167 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8168      /**
8169      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8170       automatic validation (defaults to "keyup").
8171      */
8172     validationEvent : "keyup",
8173      /**
8174      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8175      */
8176     validateOnBlur : true,
8177     /**
8178      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8179      */
8180     validationDelay : 250,
8181      /**
8182      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8183      */
8184     focusClass : "x-form-focus",  // not needed???
8185     
8186        
8187     /**
8188      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8189      */
8190     invalidClass : "has-warning",
8191     
8192     /**
8193      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8194      */
8195     validClass : "has-success",
8196     
8197     /**
8198      * @cfg {Boolean} hasFeedback (true|false) default true
8199      */
8200     hasFeedback : true,
8201     
8202     /**
8203      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8204      */
8205     invalidFeedbackClass : "glyphicon-warning-sign",
8206     
8207     /**
8208      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8209      */
8210     validFeedbackClass : "glyphicon-ok",
8211     
8212     /**
8213      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8214      */
8215     selectOnFocus : false,
8216     
8217      /**
8218      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8219      */
8220     maskRe : null,
8221        /**
8222      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8223      */
8224     vtype : null,
8225     
8226       /**
8227      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8228      */
8229     disableKeyFilter : false,
8230     
8231        /**
8232      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8233      */
8234     disabled : false,
8235      /**
8236      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8237      */
8238     allowBlank : true,
8239     /**
8240      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8241      */
8242     blankText : "This field is required",
8243     
8244      /**
8245      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8246      */
8247     minLength : 0,
8248     /**
8249      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8250      */
8251     maxLength : Number.MAX_VALUE,
8252     /**
8253      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8254      */
8255     minLengthText : "The minimum length for this field is {0}",
8256     /**
8257      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8258      */
8259     maxLengthText : "The maximum length for this field is {0}",
8260   
8261     
8262     /**
8263      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8264      * If available, this function will be called only after the basic validators all return true, and will be passed the
8265      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8266      */
8267     validator : null,
8268     /**
8269      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8270      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8271      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8272      */
8273     regex : null,
8274     /**
8275      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8276      */
8277     regexText : "",
8278     
8279     autocomplete: false,
8280     
8281     
8282     fieldLabel : '',
8283     inputType : 'text',
8284     
8285     name : false,
8286     placeholder: false,
8287     before : false,
8288     after : false,
8289     size : false,
8290     hasFocus : false,
8291     preventMark: false,
8292     isFormField : true,
8293     value : '',
8294     labelWidth : 2,
8295     labelAlign : false,
8296     readOnly : false,
8297     align : false,
8298     formatedValue : false,
8299     forceFeedback : false,
8300     
8301     indicatorpos : 'left',
8302     
8303     parentLabelAlign : function()
8304     {
8305         var parent = this;
8306         while (parent.parent()) {
8307             parent = parent.parent();
8308             if (typeof(parent.labelAlign) !='undefined') {
8309                 return parent.labelAlign;
8310             }
8311         }
8312         return 'left';
8313         
8314     },
8315     
8316     getAutoCreate : function()
8317     {
8318         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8319         
8320         var id = Roo.id();
8321         
8322         var cfg = {};
8323         
8324         if(this.inputType != 'hidden'){
8325             cfg.cls = 'form-group' //input-group
8326         }
8327         
8328         var input =  {
8329             tag: 'input',
8330             id : id,
8331             type : this.inputType,
8332             value : this.value,
8333             cls : 'form-control',
8334             placeholder : this.placeholder || '',
8335             autocomplete : this.autocomplete || 'new-password'
8336         };
8337         
8338         if(this.align){
8339             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8340         }
8341         
8342         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8343             input.maxLength = this.maxLength;
8344         }
8345         
8346         if (this.disabled) {
8347             input.disabled=true;
8348         }
8349         
8350         if (this.readOnly) {
8351             input.readonly=true;
8352         }
8353         
8354         if (this.name) {
8355             input.name = this.name;
8356         }
8357         
8358         if (this.size) {
8359             input.cls += ' input-' + this.size;
8360         }
8361         
8362         var settings=this;
8363         ['xs','sm','md','lg'].map(function(size){
8364             if (settings[size]) {
8365                 cfg.cls += ' col-' + size + '-' + settings[size];
8366             }
8367         });
8368         
8369         var inputblock = input;
8370         
8371         var feedback = {
8372             tag: 'span',
8373             cls: 'glyphicon form-control-feedback'
8374         };
8375             
8376         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8377             
8378             inputblock = {
8379                 cls : 'has-feedback',
8380                 cn :  [
8381                     input,
8382                     feedback
8383                 ] 
8384             };  
8385         }
8386         
8387         if (this.before || this.after) {
8388             
8389             inputblock = {
8390                 cls : 'input-group',
8391                 cn :  [] 
8392             };
8393             
8394             if (this.before && typeof(this.before) == 'string') {
8395                 
8396                 inputblock.cn.push({
8397                     tag :'span',
8398                     cls : 'roo-input-before input-group-addon',
8399                     html : this.before
8400                 });
8401             }
8402             if (this.before && typeof(this.before) == 'object') {
8403                 this.before = Roo.factory(this.before);
8404                 
8405                 inputblock.cn.push({
8406                     tag :'span',
8407                     cls : 'roo-input-before input-group-' +
8408                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8409                 });
8410             }
8411             
8412             inputblock.cn.push(input);
8413             
8414             if (this.after && typeof(this.after) == 'string') {
8415                 inputblock.cn.push({
8416                     tag :'span',
8417                     cls : 'roo-input-after input-group-addon',
8418                     html : this.after
8419                 });
8420             }
8421             if (this.after && typeof(this.after) == 'object') {
8422                 this.after = Roo.factory(this.after);
8423                 
8424                 inputblock.cn.push({
8425                     tag :'span',
8426                     cls : 'roo-input-after input-group-' +
8427                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8428                 });
8429             }
8430             
8431             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8432                 inputblock.cls += ' has-feedback';
8433                 inputblock.cn.push(feedback);
8434             }
8435         };
8436         
8437         if (align ==='left' && this.fieldLabel.length) {
8438             
8439             cfg.cn = [
8440                 {
8441                     tag : 'i',
8442                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8443                     tooltip : 'This field is required'
8444                 },
8445                 {
8446                     tag: 'label',
8447                     'for' :  id,
8448                     cls : 'control-label col-sm-' + this.labelWidth,
8449                     html : this.fieldLabel
8450
8451                 },
8452                 {
8453                     cls : "col-sm-" + (12 - this.labelWidth), 
8454                     cn: [
8455                         inputblock
8456                     ]
8457                 }
8458
8459             ];
8460             
8461             if(this.indicatorpos == 'right'){
8462                 cfg.cn = [
8463                     {
8464                         tag: 'label',
8465                         'for' :  id,
8466                         cls : 'control-label col-sm-' + this.labelWidth,
8467                         html : this.fieldLabel
8468
8469                     },
8470                     {
8471                         tag : 'i',
8472                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8473                         tooltip : 'This field is required'
8474                     },
8475                     {
8476                         cls : "col-sm-" + (12 - this.labelWidth), 
8477                         cn: [
8478                             inputblock
8479                         ]
8480                     }
8481
8482                 ];
8483             }
8484             
8485         } else if ( this.fieldLabel.length) {
8486                 
8487             cfg.cn = [
8488                 {
8489                     tag : 'i',
8490                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8491                     tooltip : 'This field is required'
8492                 },
8493                 {
8494                     tag: 'label',
8495                    //cls : 'input-group-addon',
8496                     html : this.fieldLabel
8497
8498                 },
8499
8500                inputblock
8501
8502            ];
8503            
8504            if(this.indicatorpos == 'right'){
8505                 
8506                 cfg.cn = [
8507                     {
8508                         tag: 'label',
8509                        //cls : 'input-group-addon',
8510                         html : this.fieldLabel
8511
8512                     },
8513                     {
8514                         tag : 'i',
8515                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8516                         tooltip : 'This field is required'
8517                     },
8518
8519                    inputblock
8520
8521                ];
8522
8523             }
8524
8525         } else {
8526             
8527             cfg.cn = [
8528
8529                     inputblock
8530
8531             ];
8532                 
8533                 
8534         };
8535         
8536         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8537            cfg.cls += ' navbar-form';
8538         }
8539         
8540         if (this.parentType === 'NavGroup') {
8541            cfg.cls += ' navbar-form';
8542            cfg.tag = 'li';
8543         }
8544         
8545         return cfg;
8546         
8547     },
8548     /**
8549      * return the real input element.
8550      */
8551     inputEl: function ()
8552     {
8553         return this.el.select('input.form-control',true).first();
8554     },
8555     
8556     tooltipEl : function()
8557     {
8558         return this.inputEl();
8559     },
8560     
8561     indicatorEl : function()
8562     {
8563         var indicator = this.el.select('i.roo-required-indicator',true).first();
8564         
8565         if(!indicator){
8566             return false;
8567         }
8568         
8569         return indicator;
8570         
8571     },
8572     
8573     setDisabled : function(v)
8574     {
8575         var i  = this.inputEl().dom;
8576         if (!v) {
8577             i.removeAttribute('disabled');
8578             return;
8579             
8580         }
8581         i.setAttribute('disabled','true');
8582     },
8583     initEvents : function()
8584     {
8585           
8586         this.inputEl().on("keydown" , this.fireKey,  this);
8587         this.inputEl().on("focus", this.onFocus,  this);
8588         this.inputEl().on("blur", this.onBlur,  this);
8589         
8590         this.inputEl().relayEvent('keyup', this);
8591         
8592         this.indicator = this.indicatorEl();
8593         
8594         if(this.indicator){
8595             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8596             this.indicator.hide();
8597         }
8598  
8599         // reference to original value for reset
8600         this.originalValue = this.getValue();
8601         //Roo.form.TextField.superclass.initEvents.call(this);
8602         if(this.validationEvent == 'keyup'){
8603             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8604             this.inputEl().on('keyup', this.filterValidation, this);
8605         }
8606         else if(this.validationEvent !== false){
8607             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8608         }
8609         
8610         if(this.selectOnFocus){
8611             this.on("focus", this.preFocus, this);
8612             
8613         }
8614         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8615             this.inputEl().on("keypress", this.filterKeys, this);
8616         } else {
8617             this.inputEl().relayEvent('keypress', this);
8618         }
8619        /* if(this.grow){
8620             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8621             this.el.on("click", this.autoSize,  this);
8622         }
8623         */
8624         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8625             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8626         }
8627         
8628         if (typeof(this.before) == 'object') {
8629             this.before.render(this.el.select('.roo-input-before',true).first());
8630         }
8631         if (typeof(this.after) == 'object') {
8632             this.after.render(this.el.select('.roo-input-after',true).first());
8633         }
8634         
8635         
8636     },
8637     filterValidation : function(e){
8638         if(!e.isNavKeyPress()){
8639             this.validationTask.delay(this.validationDelay);
8640         }
8641     },
8642      /**
8643      * Validates the field value
8644      * @return {Boolean} True if the value is valid, else false
8645      */
8646     validate : function(){
8647         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8648         if(this.disabled || this.validateValue(this.getRawValue())){
8649             this.markValid();
8650             return true;
8651         }
8652         
8653         this.markInvalid();
8654         return false;
8655     },
8656     
8657     
8658     /**
8659      * Validates a value according to the field's validation rules and marks the field as invalid
8660      * if the validation fails
8661      * @param {Mixed} value The value to validate
8662      * @return {Boolean} True if the value is valid, else false
8663      */
8664     validateValue : function(value){
8665         if(value.length < 1)  { // if it's blank
8666             if(this.allowBlank){
8667                 return true;
8668             }
8669             return false;
8670         }
8671         
8672         if(value.length < this.minLength){
8673             return false;
8674         }
8675         if(value.length > this.maxLength){
8676             return false;
8677         }
8678         if(this.vtype){
8679             var vt = Roo.form.VTypes;
8680             if(!vt[this.vtype](value, this)){
8681                 return false;
8682             }
8683         }
8684         if(typeof this.validator == "function"){
8685             var msg = this.validator(value);
8686             if(msg !== true){
8687                 return false;
8688             }
8689         }
8690         
8691         if(this.regex && !this.regex.test(value)){
8692             return false;
8693         }
8694         
8695         return true;
8696     },
8697
8698     
8699     
8700      // private
8701     fireKey : function(e){
8702         //Roo.log('field ' + e.getKey());
8703         if(e.isNavKeyPress()){
8704             this.fireEvent("specialkey", this, e);
8705         }
8706     },
8707     focus : function (selectText){
8708         if(this.rendered){
8709             this.inputEl().focus();
8710             if(selectText === true){
8711                 this.inputEl().dom.select();
8712             }
8713         }
8714         return this;
8715     } ,
8716     
8717     onFocus : function(){
8718         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8719            // this.el.addClass(this.focusClass);
8720         }
8721         if(!this.hasFocus){
8722             this.hasFocus = true;
8723             this.startValue = this.getValue();
8724             this.fireEvent("focus", this);
8725         }
8726     },
8727     
8728     beforeBlur : Roo.emptyFn,
8729
8730     
8731     // private
8732     onBlur : function(){
8733         this.beforeBlur();
8734         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8735             //this.el.removeClass(this.focusClass);
8736         }
8737         this.hasFocus = false;
8738         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8739             this.validate();
8740         }
8741         var v = this.getValue();
8742         if(String(v) !== String(this.startValue)){
8743             this.fireEvent('change', this, v, this.startValue);
8744         }
8745         this.fireEvent("blur", this);
8746     },
8747     
8748     /**
8749      * Resets the current field value to the originally loaded value and clears any validation messages
8750      */
8751     reset : function(){
8752         this.setValue(this.originalValue);
8753         this.validate();
8754     },
8755      /**
8756      * Returns the name of the field
8757      * @return {Mixed} name The name field
8758      */
8759     getName: function(){
8760         return this.name;
8761     },
8762      /**
8763      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8764      * @return {Mixed} value The field value
8765      */
8766     getValue : function(){
8767         
8768         var v = this.inputEl().getValue();
8769         
8770         return v;
8771     },
8772     /**
8773      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8774      * @return {Mixed} value The field value
8775      */
8776     getRawValue : function(){
8777         var v = this.inputEl().getValue();
8778         
8779         return v;
8780     },
8781     
8782     /**
8783      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8784      * @param {Mixed} value The value to set
8785      */
8786     setRawValue : function(v){
8787         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8788     },
8789     
8790     selectText : function(start, end){
8791         var v = this.getRawValue();
8792         if(v.length > 0){
8793             start = start === undefined ? 0 : start;
8794             end = end === undefined ? v.length : end;
8795             var d = this.inputEl().dom;
8796             if(d.setSelectionRange){
8797                 d.setSelectionRange(start, end);
8798             }else if(d.createTextRange){
8799                 var range = d.createTextRange();
8800                 range.moveStart("character", start);
8801                 range.moveEnd("character", v.length-end);
8802                 range.select();
8803             }
8804         }
8805     },
8806     
8807     /**
8808      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8809      * @param {Mixed} value The value to set
8810      */
8811     setValue : function(v){
8812         this.value = v;
8813         if(this.rendered){
8814             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8815             this.validate();
8816         }
8817     },
8818     
8819     /*
8820     processValue : function(value){
8821         if(this.stripCharsRe){
8822             var newValue = value.replace(this.stripCharsRe, '');
8823             if(newValue !== value){
8824                 this.setRawValue(newValue);
8825                 return newValue;
8826             }
8827         }
8828         return value;
8829     },
8830   */
8831     preFocus : function(){
8832         
8833         if(this.selectOnFocus){
8834             this.inputEl().dom.select();
8835         }
8836     },
8837     filterKeys : function(e){
8838         var k = e.getKey();
8839         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8840             return;
8841         }
8842         var c = e.getCharCode(), cc = String.fromCharCode(c);
8843         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8844             return;
8845         }
8846         if(!this.maskRe.test(cc)){
8847             e.stopEvent();
8848         }
8849     },
8850      /**
8851      * Clear any invalid styles/messages for this field
8852      */
8853     clearInvalid : function(){
8854         
8855         if(!this.el || this.preventMark){ // not rendered
8856             return;
8857         }
8858         
8859         if(this.indicator){
8860             this.indicator.hide();
8861         }
8862         
8863         this.el.removeClass(this.invalidClass);
8864         
8865         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8866             
8867             var feedback = this.el.select('.form-control-feedback', true).first();
8868             
8869             if(feedback){
8870                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8871             }
8872             
8873         }
8874         
8875         this.fireEvent('valid', this);
8876     },
8877     
8878      /**
8879      * Mark this field as valid
8880      */
8881     markValid : function()
8882     {
8883         if(!this.el  || this.preventMark){ // not rendered
8884             return;
8885         }
8886         
8887         this.el.removeClass([this.invalidClass, this.validClass]);
8888         
8889         var feedback = this.el.select('.form-control-feedback', true).first();
8890             
8891         if(feedback){
8892             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8893         }
8894
8895         if(this.disabled){
8896             return;
8897         }
8898         
8899         if(this.allowBlank && !this.getRawValue().length){
8900             return;
8901         }
8902         
8903         if(this.indicator){
8904             this.indicator.hide();
8905         }
8906         
8907         this.el.addClass(this.validClass);
8908         
8909         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8910             
8911             var feedback = this.el.select('.form-control-feedback', true).first();
8912             
8913             if(feedback){
8914                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8915                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8916             }
8917             
8918         }
8919         
8920         this.fireEvent('valid', this);
8921     },
8922     
8923      /**
8924      * Mark this field as invalid
8925      * @param {String} msg The validation message
8926      */
8927     markInvalid : function(msg)
8928     {
8929         if(!this.el  || this.preventMark){ // not rendered
8930             return;
8931         }
8932         
8933         this.el.removeClass([this.invalidClass, this.validClass]);
8934         
8935         var feedback = this.el.select('.form-control-feedback', true).first();
8936             
8937         if(feedback){
8938             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8939         }
8940
8941         if(this.disabled){
8942             return;
8943         }
8944         
8945         if(this.allowBlank && !this.getRawValue().length){
8946             return;
8947         }
8948         
8949         if(this.indicator){
8950             this.indicator.show();
8951         }
8952         
8953         this.el.addClass(this.invalidClass);
8954         
8955         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8956             
8957             var feedback = this.el.select('.form-control-feedback', true).first();
8958             
8959             if(feedback){
8960                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8961                 
8962                 if(this.getValue().length || this.forceFeedback){
8963                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8964                 }
8965                 
8966             }
8967             
8968         }
8969         
8970         this.fireEvent('invalid', this, msg);
8971     },
8972     // private
8973     SafariOnKeyDown : function(event)
8974     {
8975         // this is a workaround for a password hang bug on chrome/ webkit.
8976         if (this.inputEl().dom.type != 'password') {
8977             return;
8978         }
8979         
8980         var isSelectAll = false;
8981         
8982         if(this.inputEl().dom.selectionEnd > 0){
8983             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8984         }
8985         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8986             event.preventDefault();
8987             this.setValue('');
8988             return;
8989         }
8990         
8991         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8992             
8993             event.preventDefault();
8994             // this is very hacky as keydown always get's upper case.
8995             //
8996             var cc = String.fromCharCode(event.getCharCode());
8997             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8998             
8999         }
9000     },
9001     adjustWidth : function(tag, w){
9002         tag = tag.toLowerCase();
9003         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9004             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9005                 if(tag == 'input'){
9006                     return w + 2;
9007                 }
9008                 if(tag == 'textarea'){
9009                     return w-2;
9010                 }
9011             }else if(Roo.isOpera){
9012                 if(tag == 'input'){
9013                     return w + 2;
9014                 }
9015                 if(tag == 'textarea'){
9016                     return w-2;
9017                 }
9018             }
9019         }
9020         return w;
9021     }
9022     
9023 });
9024
9025  
9026 /*
9027  * - LGPL
9028  *
9029  * Input
9030  * 
9031  */
9032
9033 /**
9034  * @class Roo.bootstrap.TextArea
9035  * @extends Roo.bootstrap.Input
9036  * Bootstrap TextArea class
9037  * @cfg {Number} cols Specifies the visible width of a text area
9038  * @cfg {Number} rows Specifies the visible number of lines in a text area
9039  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9040  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9041  * @cfg {string} html text
9042  * 
9043  * @constructor
9044  * Create a new TextArea
9045  * @param {Object} config The config object
9046  */
9047
9048 Roo.bootstrap.TextArea = function(config){
9049     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9050    
9051 };
9052
9053 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9054      
9055     cols : false,
9056     rows : 5,
9057     readOnly : false,
9058     warp : 'soft',
9059     resize : false,
9060     value: false,
9061     html: false,
9062     
9063     getAutoCreate : function(){
9064         
9065         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9066         
9067         var id = Roo.id();
9068         
9069         var cfg = {};
9070         
9071         var input =  {
9072             tag: 'textarea',
9073             id : id,
9074             warp : this.warp,
9075             rows : this.rows,
9076             value : this.value || '',
9077             html: this.html || '',
9078             cls : 'form-control',
9079             placeholder : this.placeholder || '' 
9080             
9081         };
9082         
9083         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9084             input.maxLength = this.maxLength;
9085         }
9086         
9087         if(this.resize){
9088             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9089         }
9090         
9091         if(this.cols){
9092             input.cols = this.cols;
9093         }
9094         
9095         if (this.readOnly) {
9096             input.readonly = true;
9097         }
9098         
9099         if (this.name) {
9100             input.name = this.name;
9101         }
9102         
9103         if (this.size) {
9104             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9105         }
9106         
9107         var settings=this;
9108         ['xs','sm','md','lg'].map(function(size){
9109             if (settings[size]) {
9110                 cfg.cls += ' col-' + size + '-' + settings[size];
9111             }
9112         });
9113         
9114         var inputblock = input;
9115         
9116         if(this.hasFeedback && !this.allowBlank){
9117             
9118             var feedback = {
9119                 tag: 'span',
9120                 cls: 'glyphicon form-control-feedback'
9121             };
9122
9123             inputblock = {
9124                 cls : 'has-feedback',
9125                 cn :  [
9126                     input,
9127                     feedback
9128                 ] 
9129             };  
9130         }
9131         
9132         
9133         if (this.before || this.after) {
9134             
9135             inputblock = {
9136                 cls : 'input-group',
9137                 cn :  [] 
9138             };
9139             if (this.before) {
9140                 inputblock.cn.push({
9141                     tag :'span',
9142                     cls : 'input-group-addon',
9143                     html : this.before
9144                 });
9145             }
9146             
9147             inputblock.cn.push(input);
9148             
9149             if(this.hasFeedback && !this.allowBlank){
9150                 inputblock.cls += ' has-feedback';
9151                 inputblock.cn.push(feedback);
9152             }
9153             
9154             if (this.after) {
9155                 inputblock.cn.push({
9156                     tag :'span',
9157                     cls : 'input-group-addon',
9158                     html : this.after
9159                 });
9160             }
9161             
9162         }
9163         
9164         if (align ==='left' && this.fieldLabel.length) {
9165 //                Roo.log("left and has label");
9166                 cfg.cn = [
9167                     
9168                     {
9169                         tag: 'label',
9170                         'for' :  id,
9171                         cls : 'control-label col-sm-' + this.labelWidth,
9172                         html : this.fieldLabel
9173                         
9174                     },
9175                     {
9176                         cls : "col-sm-" + (12 - this.labelWidth), 
9177                         cn: [
9178                             inputblock
9179                         ]
9180                     }
9181                     
9182                 ];
9183         } else if ( this.fieldLabel.length) {
9184 //                Roo.log(" label");
9185                  cfg.cn = [
9186                    
9187                     {
9188                         tag: 'label',
9189                         //cls : 'input-group-addon',
9190                         html : this.fieldLabel
9191                         
9192                     },
9193                     
9194                     inputblock
9195                     
9196                 ];
9197
9198         } else {
9199             
9200 //                   Roo.log(" no label && no align");
9201                 cfg.cn = [
9202                     
9203                         inputblock
9204                     
9205                 ];
9206                 
9207                 
9208         }
9209         
9210         if (this.disabled) {
9211             input.disabled=true;
9212         }
9213         
9214         return cfg;
9215         
9216     },
9217     /**
9218      * return the real textarea element.
9219      */
9220     inputEl: function ()
9221     {
9222         return this.el.select('textarea.form-control',true).first();
9223     },
9224     
9225     /**
9226      * Clear any invalid styles/messages for this field
9227      */
9228     clearInvalid : function()
9229     {
9230         
9231         if(!this.el || this.preventMark){ // not rendered
9232             return;
9233         }
9234         
9235         var label = this.el.select('label', true).first();
9236         var icon = this.el.select('i.fa-star', true).first();
9237         
9238         if(label && icon){
9239             icon.remove();
9240         }
9241         
9242         this.el.removeClass(this.invalidClass);
9243         
9244         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9245             
9246             var feedback = this.el.select('.form-control-feedback', true).first();
9247             
9248             if(feedback){
9249                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9250             }
9251             
9252         }
9253         
9254         this.fireEvent('valid', this);
9255     },
9256     
9257      /**
9258      * Mark this field as valid
9259      */
9260     markValid : function()
9261     {
9262         if(!this.el  || this.preventMark){ // not rendered
9263             return;
9264         }
9265         
9266         this.el.removeClass([this.invalidClass, this.validClass]);
9267         
9268         var feedback = this.el.select('.form-control-feedback', true).first();
9269             
9270         if(feedback){
9271             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9272         }
9273
9274         if(this.disabled || this.allowBlank){
9275             return;
9276         }
9277         
9278         var label = this.el.select('label', true).first();
9279         var icon = this.el.select('i.fa-star', true).first();
9280         
9281         if(label && icon){
9282             icon.remove();
9283         }
9284         
9285         this.el.addClass(this.validClass);
9286         
9287         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9288             
9289             var feedback = this.el.select('.form-control-feedback', true).first();
9290             
9291             if(feedback){
9292                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9293                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9294             }
9295             
9296         }
9297         
9298         this.fireEvent('valid', this);
9299     },
9300     
9301      /**
9302      * Mark this field as invalid
9303      * @param {String} msg The validation message
9304      */
9305     markInvalid : function(msg)
9306     {
9307         if(!this.el  || this.preventMark){ // not rendered
9308             return;
9309         }
9310         
9311         this.el.removeClass([this.invalidClass, this.validClass]);
9312         
9313         var feedback = this.el.select('.form-control-feedback', true).first();
9314             
9315         if(feedback){
9316             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9317         }
9318
9319         if(this.disabled || this.allowBlank){
9320             return;
9321         }
9322         
9323         var label = this.el.select('label', true).first();
9324         var icon = this.el.select('i.fa-star', true).first();
9325         
9326         if(!this.getValue().length && label && !icon){
9327             this.el.createChild({
9328                 tag : 'i',
9329                 cls : 'text-danger fa fa-lg fa-star',
9330                 tooltip : 'This field is required',
9331                 style : 'margin-right:5px;'
9332             }, label, true);
9333         }
9334
9335         this.el.addClass(this.invalidClass);
9336         
9337         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9338             
9339             var feedback = this.el.select('.form-control-feedback', true).first();
9340             
9341             if(feedback){
9342                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9343                 
9344                 if(this.getValue().length || this.forceFeedback){
9345                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9346                 }
9347                 
9348             }
9349             
9350         }
9351         
9352         this.fireEvent('invalid', this, msg);
9353     }
9354 });
9355
9356  
9357 /*
9358  * - LGPL
9359  *
9360  * trigger field - base class for combo..
9361  * 
9362  */
9363  
9364 /**
9365  * @class Roo.bootstrap.TriggerField
9366  * @extends Roo.bootstrap.Input
9367  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9368  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9369  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9370  * for which you can provide a custom implementation.  For example:
9371  * <pre><code>
9372 var trigger = new Roo.bootstrap.TriggerField();
9373 trigger.onTriggerClick = myTriggerFn;
9374 trigger.applyTo('my-field');
9375 </code></pre>
9376  *
9377  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9378  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9379  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9380  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9381  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9382
9383  * @constructor
9384  * Create a new TriggerField.
9385  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9386  * to the base TextField)
9387  */
9388 Roo.bootstrap.TriggerField = function(config){
9389     this.mimicing = false;
9390     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9391 };
9392
9393 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9394     /**
9395      * @cfg {String} triggerClass A CSS class to apply to the trigger
9396      */
9397      /**
9398      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9399      */
9400     hideTrigger:false,
9401
9402     /**
9403      * @cfg {Boolean} removable (true|false) special filter default false
9404      */
9405     removable : false,
9406     
9407     /** @cfg {Boolean} grow @hide */
9408     /** @cfg {Number} growMin @hide */
9409     /** @cfg {Number} growMax @hide */
9410
9411     /**
9412      * @hide 
9413      * @method
9414      */
9415     autoSize: Roo.emptyFn,
9416     // private
9417     monitorTab : true,
9418     // private
9419     deferHeight : true,
9420
9421     
9422     actionMode : 'wrap',
9423     
9424     caret : false,
9425     
9426     
9427     getAutoCreate : function(){
9428        
9429         var align = this.labelAlign || this.parentLabelAlign();
9430         
9431         var id = Roo.id();
9432         
9433         var cfg = {
9434             cls: 'form-group' //input-group
9435         };
9436         
9437         
9438         var input =  {
9439             tag: 'input',
9440             id : id,
9441             type : this.inputType,
9442             cls : 'form-control',
9443             autocomplete: 'new-password',
9444             placeholder : this.placeholder || '' 
9445             
9446         };
9447         if (this.name) {
9448             input.name = this.name;
9449         }
9450         if (this.size) {
9451             input.cls += ' input-' + this.size;
9452         }
9453         
9454         if (this.disabled) {
9455             input.disabled=true;
9456         }
9457         
9458         var inputblock = input;
9459         
9460         if(this.hasFeedback && !this.allowBlank){
9461             
9462             var feedback = {
9463                 tag: 'span',
9464                 cls: 'glyphicon form-control-feedback'
9465             };
9466             
9467             if(this.removable && !this.editable && !this.tickable){
9468                 inputblock = {
9469                     cls : 'has-feedback',
9470                     cn :  [
9471                         inputblock,
9472                         {
9473                             tag: 'button',
9474                             html : 'x',
9475                             cls : 'roo-combo-removable-btn close'
9476                         },
9477                         feedback
9478                     ] 
9479                 };
9480             } else {
9481                 inputblock = {
9482                     cls : 'has-feedback',
9483                     cn :  [
9484                         inputblock,
9485                         feedback
9486                     ] 
9487                 };
9488             }
9489
9490         } else {
9491             if(this.removable && !this.editable && !this.tickable){
9492                 inputblock = {
9493                     cls : 'roo-removable',
9494                     cn :  [
9495                         inputblock,
9496                         {
9497                             tag: 'button',
9498                             html : 'x',
9499                             cls : 'roo-combo-removable-btn close'
9500                         }
9501                     ] 
9502                 };
9503             }
9504         }
9505         
9506         if (this.before || this.after) {
9507             
9508             inputblock = {
9509                 cls : 'input-group',
9510                 cn :  [] 
9511             };
9512             if (this.before) {
9513                 inputblock.cn.push({
9514                     tag :'span',
9515                     cls : 'input-group-addon',
9516                     html : this.before
9517                 });
9518             }
9519             
9520             inputblock.cn.push(input);
9521             
9522             if(this.hasFeedback && !this.allowBlank){
9523                 inputblock.cls += ' has-feedback';
9524                 inputblock.cn.push(feedback);
9525             }
9526             
9527             if (this.after) {
9528                 inputblock.cn.push({
9529                     tag :'span',
9530                     cls : 'input-group-addon',
9531                     html : this.after
9532                 });
9533             }
9534             
9535         };
9536         
9537         var box = {
9538             tag: 'div',
9539             cn: [
9540                 {
9541                     tag: 'input',
9542                     type : 'hidden',
9543                     cls: 'form-hidden-field'
9544                 },
9545                 inputblock
9546             ]
9547             
9548         };
9549         
9550         if(this.multiple){
9551             box = {
9552                 tag: 'div',
9553                 cn: [
9554                     {
9555                         tag: 'input',
9556                         type : 'hidden',
9557                         cls: 'form-hidden-field'
9558                     },
9559                     {
9560                         tag: 'ul',
9561                         cls: 'roo-select2-choices',
9562                         cn:[
9563                             {
9564                                 tag: 'li',
9565                                 cls: 'roo-select2-search-field',
9566                                 cn: [
9567
9568                                     inputblock
9569                                 ]
9570                             }
9571                         ]
9572                     }
9573                 ]
9574             }
9575         };
9576         
9577         var combobox = {
9578             cls: 'roo-select2-container input-group',
9579             cn: [
9580                 box
9581 //                {
9582 //                    tag: 'ul',
9583 //                    cls: 'typeahead typeahead-long dropdown-menu',
9584 //                    style: 'display:none'
9585 //                }
9586             ]
9587         };
9588         
9589         if(!this.multiple && this.showToggleBtn){
9590             
9591             var caret = {
9592                         tag: 'span',
9593                         cls: 'caret'
9594              };
9595             if (this.caret != false) {
9596                 caret = {
9597                      tag: 'i',
9598                      cls: 'fa fa-' + this.caret
9599                 };
9600                 
9601             }
9602             
9603             combobox.cn.push({
9604                 tag :'span',
9605                 cls : 'input-group-addon btn dropdown-toggle',
9606                 cn : [
9607                     caret,
9608                     {
9609                         tag: 'span',
9610                         cls: 'combobox-clear',
9611                         cn  : [
9612                             {
9613                                 tag : 'i',
9614                                 cls: 'icon-remove'
9615                             }
9616                         ]
9617                     }
9618                 ]
9619
9620             })
9621         }
9622         
9623         if(this.multiple){
9624             combobox.cls += ' roo-select2-container-multi';
9625         }
9626         
9627         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9628             
9629 //                Roo.log("left and has label");
9630             cfg.cn = [
9631                 {
9632                     tag : 'i',
9633                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9634                     tooltip : 'This field is required'
9635                 },
9636                 {
9637                     tag: 'label',
9638                     'for' :  id,
9639                     cls : 'control-label col-sm-' + this.labelWidth,
9640                     html : this.fieldLabel
9641
9642                 },
9643                 {
9644                     cls : "col-sm-" + (12 - this.labelWidth), 
9645                     cn: [
9646                         combobox
9647                     ]
9648                 }
9649
9650             ];
9651             
9652             if(this.indicatorpos == 'right'){
9653                 cfg.cn = [
9654                     {
9655                         tag: 'label',
9656                         'for' :  id,
9657                         cls : 'control-label col-sm-' + this.labelWidth,
9658                         html : this.fieldLabel
9659
9660                     },
9661                     {
9662                         tag : 'i',
9663                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9664                         tooltip : 'This field is required'
9665                     },
9666                     {
9667                         cls : "col-sm-" + (12 - this.labelWidth), 
9668                         cn: [
9669                             combobox
9670                         ]
9671                     }
9672
9673                 ];
9674             }
9675             
9676         } else if ( this.fieldLabel.length) {
9677 //                Roo.log(" label");
9678             cfg.cn = [
9679                 {
9680                    tag : 'i',
9681                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9682                    tooltip : 'This field is required'
9683                },
9684                {
9685                    tag: 'label',
9686                    //cls : 'input-group-addon',
9687                    html : this.fieldLabel
9688
9689                },
9690
9691                combobox
9692
9693             ];
9694             
9695             if(this.indicatorpos == 'right'){
9696                 
9697                 cfg.cn = [
9698                     {
9699                        tag: 'label',
9700                        //cls : 'input-group-addon',
9701                        html : this.fieldLabel
9702
9703                     },
9704                     {
9705                        tag : 'i',
9706                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9707                        tooltip : 'This field is required'
9708                     },
9709                     
9710                     combobox
9711
9712                 ];
9713
9714             }
9715
9716         } else {
9717             
9718 //                Roo.log(" no label && no align");
9719                 cfg = combobox
9720                      
9721                 
9722         }
9723          
9724         var settings=this;
9725         ['xs','sm','md','lg'].map(function(size){
9726             if (settings[size]) {
9727                 cfg.cls += ' col-' + size + '-' + settings[size];
9728             }
9729         });
9730         
9731         return cfg;
9732         
9733     },
9734     
9735     
9736     
9737     // private
9738     onResize : function(w, h){
9739 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9740 //        if(typeof w == 'number'){
9741 //            var x = w - this.trigger.getWidth();
9742 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9743 //            this.trigger.setStyle('left', x+'px');
9744 //        }
9745     },
9746
9747     // private
9748     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9749
9750     // private
9751     getResizeEl : function(){
9752         return this.inputEl();
9753     },
9754
9755     // private
9756     getPositionEl : function(){
9757         return this.inputEl();
9758     },
9759
9760     // private
9761     alignErrorIcon : function(){
9762         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9763     },
9764
9765     // private
9766     initEvents : function(){
9767         
9768         this.createList();
9769         
9770         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9771         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9772         if(!this.multiple && this.showToggleBtn){
9773             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9774             if(this.hideTrigger){
9775                 this.trigger.setDisplayed(false);
9776             }
9777             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9778         }
9779         
9780         if(this.multiple){
9781             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9782         }
9783         
9784         if(this.removable && !this.editable && !this.tickable){
9785             var close = this.closeTriggerEl();
9786             
9787             if(close){
9788                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9789                 close.on('click', this.removeBtnClick, this, close);
9790             }
9791         }
9792         
9793         //this.trigger.addClassOnOver('x-form-trigger-over');
9794         //this.trigger.addClassOnClick('x-form-trigger-click');
9795         
9796         //if(!this.width){
9797         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9798         //}
9799     },
9800     
9801     closeTriggerEl : function()
9802     {
9803         var close = this.el.select('.roo-combo-removable-btn', true).first();
9804         return close ? close : false;
9805     },
9806     
9807     removeBtnClick : function(e, h, el)
9808     {
9809         e.preventDefault();
9810         
9811         if(this.fireEvent("remove", this) !== false){
9812             this.reset();
9813             this.fireEvent("afterremove", this)
9814         }
9815     },
9816     
9817     createList : function()
9818     {
9819         this.list = Roo.get(document.body).createChild({
9820             tag: 'ul',
9821             cls: 'typeahead typeahead-long dropdown-menu',
9822             style: 'display:none'
9823         });
9824         
9825         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9826         
9827     },
9828
9829     // private
9830     initTrigger : function(){
9831        
9832     },
9833
9834     // private
9835     onDestroy : function(){
9836         if(this.trigger){
9837             this.trigger.removeAllListeners();
9838           //  this.trigger.remove();
9839         }
9840         //if(this.wrap){
9841         //    this.wrap.remove();
9842         //}
9843         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9844     },
9845
9846     // private
9847     onFocus : function(){
9848         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9849         /*
9850         if(!this.mimicing){
9851             this.wrap.addClass('x-trigger-wrap-focus');
9852             this.mimicing = true;
9853             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9854             if(this.monitorTab){
9855                 this.el.on("keydown", this.checkTab, this);
9856             }
9857         }
9858         */
9859     },
9860
9861     // private
9862     checkTab : function(e){
9863         if(e.getKey() == e.TAB){
9864             this.triggerBlur();
9865         }
9866     },
9867
9868     // private
9869     onBlur : function(){
9870         // do nothing
9871     },
9872
9873     // private
9874     mimicBlur : function(e, t){
9875         /*
9876         if(!this.wrap.contains(t) && this.validateBlur()){
9877             this.triggerBlur();
9878         }
9879         */
9880     },
9881
9882     // private
9883     triggerBlur : function(){
9884         this.mimicing = false;
9885         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9886         if(this.monitorTab){
9887             this.el.un("keydown", this.checkTab, this);
9888         }
9889         //this.wrap.removeClass('x-trigger-wrap-focus');
9890         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9891     },
9892
9893     // private
9894     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9895     validateBlur : function(e, t){
9896         return true;
9897     },
9898
9899     // private
9900     onDisable : function(){
9901         this.inputEl().dom.disabled = true;
9902         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9903         //if(this.wrap){
9904         //    this.wrap.addClass('x-item-disabled');
9905         //}
9906     },
9907
9908     // private
9909     onEnable : function(){
9910         this.inputEl().dom.disabled = false;
9911         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9912         //if(this.wrap){
9913         //    this.el.removeClass('x-item-disabled');
9914         //}
9915     },
9916
9917     // private
9918     onShow : function(){
9919         var ae = this.getActionEl();
9920         
9921         if(ae){
9922             ae.dom.style.display = '';
9923             ae.dom.style.visibility = 'visible';
9924         }
9925     },
9926
9927     // private
9928     
9929     onHide : function(){
9930         var ae = this.getActionEl();
9931         ae.dom.style.display = 'none';
9932     },
9933
9934     /**
9935      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9936      * by an implementing function.
9937      * @method
9938      * @param {EventObject} e
9939      */
9940     onTriggerClick : Roo.emptyFn
9941 });
9942  /*
9943  * Based on:
9944  * Ext JS Library 1.1.1
9945  * Copyright(c) 2006-2007, Ext JS, LLC.
9946  *
9947  * Originally Released Under LGPL - original licence link has changed is not relivant.
9948  *
9949  * Fork - LGPL
9950  * <script type="text/javascript">
9951  */
9952
9953
9954 /**
9955  * @class Roo.data.SortTypes
9956  * @singleton
9957  * Defines the default sorting (casting?) comparison functions used when sorting data.
9958  */
9959 Roo.data.SortTypes = {
9960     /**
9961      * Default sort that does nothing
9962      * @param {Mixed} s The value being converted
9963      * @return {Mixed} The comparison value
9964      */
9965     none : function(s){
9966         return s;
9967     },
9968     
9969     /**
9970      * The regular expression used to strip tags
9971      * @type {RegExp}
9972      * @property
9973      */
9974     stripTagsRE : /<\/?[^>]+>/gi,
9975     
9976     /**
9977      * Strips all HTML tags to sort on text only
9978      * @param {Mixed} s The value being converted
9979      * @return {String} The comparison value
9980      */
9981     asText : function(s){
9982         return String(s).replace(this.stripTagsRE, "");
9983     },
9984     
9985     /**
9986      * Strips all HTML tags to sort on text only - Case insensitive
9987      * @param {Mixed} s The value being converted
9988      * @return {String} The comparison value
9989      */
9990     asUCText : function(s){
9991         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9992     },
9993     
9994     /**
9995      * Case insensitive string
9996      * @param {Mixed} s The value being converted
9997      * @return {String} The comparison value
9998      */
9999     asUCString : function(s) {
10000         return String(s).toUpperCase();
10001     },
10002     
10003     /**
10004      * Date sorting
10005      * @param {Mixed} s The value being converted
10006      * @return {Number} The comparison value
10007      */
10008     asDate : function(s) {
10009         if(!s){
10010             return 0;
10011         }
10012         if(s instanceof Date){
10013             return s.getTime();
10014         }
10015         return Date.parse(String(s));
10016     },
10017     
10018     /**
10019      * Float sorting
10020      * @param {Mixed} s The value being converted
10021      * @return {Float} The comparison value
10022      */
10023     asFloat : function(s) {
10024         var val = parseFloat(String(s).replace(/,/g, ""));
10025         if(isNaN(val)) {
10026             val = 0;
10027         }
10028         return val;
10029     },
10030     
10031     /**
10032      * Integer sorting
10033      * @param {Mixed} s The value being converted
10034      * @return {Number} The comparison value
10035      */
10036     asInt : function(s) {
10037         var val = parseInt(String(s).replace(/,/g, ""));
10038         if(isNaN(val)) {
10039             val = 0;
10040         }
10041         return val;
10042     }
10043 };/*
10044  * Based on:
10045  * Ext JS Library 1.1.1
10046  * Copyright(c) 2006-2007, Ext JS, LLC.
10047  *
10048  * Originally Released Under LGPL - original licence link has changed is not relivant.
10049  *
10050  * Fork - LGPL
10051  * <script type="text/javascript">
10052  */
10053
10054 /**
10055 * @class Roo.data.Record
10056  * Instances of this class encapsulate both record <em>definition</em> information, and record
10057  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10058  * to access Records cached in an {@link Roo.data.Store} object.<br>
10059  * <p>
10060  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10061  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10062  * objects.<br>
10063  * <p>
10064  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10065  * @constructor
10066  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10067  * {@link #create}. The parameters are the same.
10068  * @param {Array} data An associative Array of data values keyed by the field name.
10069  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10070  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10071  * not specified an integer id is generated.
10072  */
10073 Roo.data.Record = function(data, id){
10074     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10075     this.data = data;
10076 };
10077
10078 /**
10079  * Generate a constructor for a specific record layout.
10080  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10081  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10082  * Each field definition object may contain the following properties: <ul>
10083  * <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,
10084  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10085  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10086  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10087  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10088  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10089  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10090  * this may be omitted.</p></li>
10091  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10092  * <ul><li>auto (Default, implies no conversion)</li>
10093  * <li>string</li>
10094  * <li>int</li>
10095  * <li>float</li>
10096  * <li>boolean</li>
10097  * <li>date</li></ul></p></li>
10098  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10099  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10100  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10101  * by the Reader into an object that will be stored in the Record. It is passed the
10102  * following parameters:<ul>
10103  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10104  * </ul></p></li>
10105  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10106  * </ul>
10107  * <br>usage:<br><pre><code>
10108 var TopicRecord = Roo.data.Record.create(
10109     {name: 'title', mapping: 'topic_title'},
10110     {name: 'author', mapping: 'username'},
10111     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10112     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10113     {name: 'lastPoster', mapping: 'user2'},
10114     {name: 'excerpt', mapping: 'post_text'}
10115 );
10116
10117 var myNewRecord = new TopicRecord({
10118     title: 'Do my job please',
10119     author: 'noobie',
10120     totalPosts: 1,
10121     lastPost: new Date(),
10122     lastPoster: 'Animal',
10123     excerpt: 'No way dude!'
10124 });
10125 myStore.add(myNewRecord);
10126 </code></pre>
10127  * @method create
10128  * @static
10129  */
10130 Roo.data.Record.create = function(o){
10131     var f = function(){
10132         f.superclass.constructor.apply(this, arguments);
10133     };
10134     Roo.extend(f, Roo.data.Record);
10135     var p = f.prototype;
10136     p.fields = new Roo.util.MixedCollection(false, function(field){
10137         return field.name;
10138     });
10139     for(var i = 0, len = o.length; i < len; i++){
10140         p.fields.add(new Roo.data.Field(o[i]));
10141     }
10142     f.getField = function(name){
10143         return p.fields.get(name);  
10144     };
10145     return f;
10146 };
10147
10148 Roo.data.Record.AUTO_ID = 1000;
10149 Roo.data.Record.EDIT = 'edit';
10150 Roo.data.Record.REJECT = 'reject';
10151 Roo.data.Record.COMMIT = 'commit';
10152
10153 Roo.data.Record.prototype = {
10154     /**
10155      * Readonly flag - true if this record has been modified.
10156      * @type Boolean
10157      */
10158     dirty : false,
10159     editing : false,
10160     error: null,
10161     modified: null,
10162
10163     // private
10164     join : function(store){
10165         this.store = store;
10166     },
10167
10168     /**
10169      * Set the named field to the specified value.
10170      * @param {String} name The name of the field to set.
10171      * @param {Object} value The value to set the field to.
10172      */
10173     set : function(name, value){
10174         if(this.data[name] == value){
10175             return;
10176         }
10177         this.dirty = true;
10178         if(!this.modified){
10179             this.modified = {};
10180         }
10181         if(typeof this.modified[name] == 'undefined'){
10182             this.modified[name] = this.data[name];
10183         }
10184         this.data[name] = value;
10185         if(!this.editing && this.store){
10186             this.store.afterEdit(this);
10187         }       
10188     },
10189
10190     /**
10191      * Get the value of the named field.
10192      * @param {String} name The name of the field to get the value of.
10193      * @return {Object} The value of the field.
10194      */
10195     get : function(name){
10196         return this.data[name]; 
10197     },
10198
10199     // private
10200     beginEdit : function(){
10201         this.editing = true;
10202         this.modified = {}; 
10203     },
10204
10205     // private
10206     cancelEdit : function(){
10207         this.editing = false;
10208         delete this.modified;
10209     },
10210
10211     // private
10212     endEdit : function(){
10213         this.editing = false;
10214         if(this.dirty && this.store){
10215             this.store.afterEdit(this);
10216         }
10217     },
10218
10219     /**
10220      * Usually called by the {@link Roo.data.Store} which owns the Record.
10221      * Rejects all changes made to the Record since either creation, or the last commit operation.
10222      * Modified fields are reverted to their original values.
10223      * <p>
10224      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10225      * of reject operations.
10226      */
10227     reject : function(){
10228         var m = this.modified;
10229         for(var n in m){
10230             if(typeof m[n] != "function"){
10231                 this.data[n] = m[n];
10232             }
10233         }
10234         this.dirty = false;
10235         delete this.modified;
10236         this.editing = false;
10237         if(this.store){
10238             this.store.afterReject(this);
10239         }
10240     },
10241
10242     /**
10243      * Usually called by the {@link Roo.data.Store} which owns the Record.
10244      * Commits all changes made to the Record since either creation, or the last commit operation.
10245      * <p>
10246      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10247      * of commit operations.
10248      */
10249     commit : function(){
10250         this.dirty = false;
10251         delete this.modified;
10252         this.editing = false;
10253         if(this.store){
10254             this.store.afterCommit(this);
10255         }
10256     },
10257
10258     // private
10259     hasError : function(){
10260         return this.error != null;
10261     },
10262
10263     // private
10264     clearError : function(){
10265         this.error = null;
10266     },
10267
10268     /**
10269      * Creates a copy of this record.
10270      * @param {String} id (optional) A new record id if you don't want to use this record's id
10271      * @return {Record}
10272      */
10273     copy : function(newId) {
10274         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10275     }
10276 };/*
10277  * Based on:
10278  * Ext JS Library 1.1.1
10279  * Copyright(c) 2006-2007, Ext JS, LLC.
10280  *
10281  * Originally Released Under LGPL - original licence link has changed is not relivant.
10282  *
10283  * Fork - LGPL
10284  * <script type="text/javascript">
10285  */
10286
10287
10288
10289 /**
10290  * @class Roo.data.Store
10291  * @extends Roo.util.Observable
10292  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10293  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10294  * <p>
10295  * 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
10296  * has no knowledge of the format of the data returned by the Proxy.<br>
10297  * <p>
10298  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10299  * instances from the data object. These records are cached and made available through accessor functions.
10300  * @constructor
10301  * Creates a new Store.
10302  * @param {Object} config A config object containing the objects needed for the Store to access data,
10303  * and read the data into Records.
10304  */
10305 Roo.data.Store = function(config){
10306     this.data = new Roo.util.MixedCollection(false);
10307     this.data.getKey = function(o){
10308         return o.id;
10309     };
10310     this.baseParams = {};
10311     // private
10312     this.paramNames = {
10313         "start" : "start",
10314         "limit" : "limit",
10315         "sort" : "sort",
10316         "dir" : "dir",
10317         "multisort" : "_multisort"
10318     };
10319
10320     if(config && config.data){
10321         this.inlineData = config.data;
10322         delete config.data;
10323     }
10324
10325     Roo.apply(this, config);
10326     
10327     if(this.reader){ // reader passed
10328         this.reader = Roo.factory(this.reader, Roo.data);
10329         this.reader.xmodule = this.xmodule || false;
10330         if(!this.recordType){
10331             this.recordType = this.reader.recordType;
10332         }
10333         if(this.reader.onMetaChange){
10334             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10335         }
10336     }
10337
10338     if(this.recordType){
10339         this.fields = this.recordType.prototype.fields;
10340     }
10341     this.modified = [];
10342
10343     this.addEvents({
10344         /**
10345          * @event datachanged
10346          * Fires when the data cache has changed, and a widget which is using this Store
10347          * as a Record cache should refresh its view.
10348          * @param {Store} this
10349          */
10350         datachanged : true,
10351         /**
10352          * @event metachange
10353          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10354          * @param {Store} this
10355          * @param {Object} meta The JSON metadata
10356          */
10357         metachange : true,
10358         /**
10359          * @event add
10360          * Fires when Records have been added to the Store
10361          * @param {Store} this
10362          * @param {Roo.data.Record[]} records The array of Records added
10363          * @param {Number} index The index at which the record(s) were added
10364          */
10365         add : true,
10366         /**
10367          * @event remove
10368          * Fires when a Record has been removed from the Store
10369          * @param {Store} this
10370          * @param {Roo.data.Record} record The Record that was removed
10371          * @param {Number} index The index at which the record was removed
10372          */
10373         remove : true,
10374         /**
10375          * @event update
10376          * Fires when a Record has been updated
10377          * @param {Store} this
10378          * @param {Roo.data.Record} record The Record that was updated
10379          * @param {String} operation The update operation being performed.  Value may be one of:
10380          * <pre><code>
10381  Roo.data.Record.EDIT
10382  Roo.data.Record.REJECT
10383  Roo.data.Record.COMMIT
10384          * </code></pre>
10385          */
10386         update : true,
10387         /**
10388          * @event clear
10389          * Fires when the data cache has been cleared.
10390          * @param {Store} this
10391          */
10392         clear : true,
10393         /**
10394          * @event beforeload
10395          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10396          * the load action will be canceled.
10397          * @param {Store} this
10398          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10399          */
10400         beforeload : true,
10401         /**
10402          * @event beforeloadadd
10403          * Fires after a new set of Records has been loaded.
10404          * @param {Store} this
10405          * @param {Roo.data.Record[]} records The Records that were loaded
10406          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10407          */
10408         beforeloadadd : true,
10409         /**
10410          * @event load
10411          * Fires after a new set of Records has been loaded, before they are added to the store.
10412          * @param {Store} this
10413          * @param {Roo.data.Record[]} records The Records that were loaded
10414          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10415          * @params {Object} return from reader
10416          */
10417         load : true,
10418         /**
10419          * @event loadexception
10420          * Fires if an exception occurs in the Proxy during loading.
10421          * Called with the signature of the Proxy's "loadexception" event.
10422          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10423          * 
10424          * @param {Proxy} 
10425          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10426          * @param {Object} load options 
10427          * @param {Object} jsonData from your request (normally this contains the Exception)
10428          */
10429         loadexception : true
10430     });
10431     
10432     if(this.proxy){
10433         this.proxy = Roo.factory(this.proxy, Roo.data);
10434         this.proxy.xmodule = this.xmodule || false;
10435         this.relayEvents(this.proxy,  ["loadexception"]);
10436     }
10437     this.sortToggle = {};
10438     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10439
10440     Roo.data.Store.superclass.constructor.call(this);
10441
10442     if(this.inlineData){
10443         this.loadData(this.inlineData);
10444         delete this.inlineData;
10445     }
10446 };
10447
10448 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10449      /**
10450     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10451     * without a remote query - used by combo/forms at present.
10452     */
10453     
10454     /**
10455     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10456     */
10457     /**
10458     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10459     */
10460     /**
10461     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10462     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10463     */
10464     /**
10465     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10466     * on any HTTP request
10467     */
10468     /**
10469     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10470     */
10471     /**
10472     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10473     */
10474     multiSort: false,
10475     /**
10476     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10477     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10478     */
10479     remoteSort : false,
10480
10481     /**
10482     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10483      * loaded or when a record is removed. (defaults to false).
10484     */
10485     pruneModifiedRecords : false,
10486
10487     // private
10488     lastOptions : null,
10489
10490     /**
10491      * Add Records to the Store and fires the add event.
10492      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10493      */
10494     add : function(records){
10495         records = [].concat(records);
10496         for(var i = 0, len = records.length; i < len; i++){
10497             records[i].join(this);
10498         }
10499         var index = this.data.length;
10500         this.data.addAll(records);
10501         this.fireEvent("add", this, records, index);
10502     },
10503
10504     /**
10505      * Remove a Record from the Store and fires the remove event.
10506      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10507      */
10508     remove : function(record){
10509         var index = this.data.indexOf(record);
10510         this.data.removeAt(index);
10511         if(this.pruneModifiedRecords){
10512             this.modified.remove(record);
10513         }
10514         this.fireEvent("remove", this, record, index);
10515     },
10516
10517     /**
10518      * Remove all Records from the Store and fires the clear event.
10519      */
10520     removeAll : function(){
10521         this.data.clear();
10522         if(this.pruneModifiedRecords){
10523             this.modified = [];
10524         }
10525         this.fireEvent("clear", this);
10526     },
10527
10528     /**
10529      * Inserts Records to the Store at the given index and fires the add event.
10530      * @param {Number} index The start index at which to insert the passed Records.
10531      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10532      */
10533     insert : function(index, records){
10534         records = [].concat(records);
10535         for(var i = 0, len = records.length; i < len; i++){
10536             this.data.insert(index, records[i]);
10537             records[i].join(this);
10538         }
10539         this.fireEvent("add", this, records, index);
10540     },
10541
10542     /**
10543      * Get the index within the cache of the passed Record.
10544      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10545      * @return {Number} The index of the passed Record. Returns -1 if not found.
10546      */
10547     indexOf : function(record){
10548         return this.data.indexOf(record);
10549     },
10550
10551     /**
10552      * Get the index within the cache of the Record with the passed id.
10553      * @param {String} id The id of the Record to find.
10554      * @return {Number} The index of the Record. Returns -1 if not found.
10555      */
10556     indexOfId : function(id){
10557         return this.data.indexOfKey(id);
10558     },
10559
10560     /**
10561      * Get the Record with the specified id.
10562      * @param {String} id The id of the Record to find.
10563      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10564      */
10565     getById : function(id){
10566         return this.data.key(id);
10567     },
10568
10569     /**
10570      * Get the Record at the specified index.
10571      * @param {Number} index The index of the Record to find.
10572      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10573      */
10574     getAt : function(index){
10575         return this.data.itemAt(index);
10576     },
10577
10578     /**
10579      * Returns a range of Records between specified indices.
10580      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10581      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10582      * @return {Roo.data.Record[]} An array of Records
10583      */
10584     getRange : function(start, end){
10585         return this.data.getRange(start, end);
10586     },
10587
10588     // private
10589     storeOptions : function(o){
10590         o = Roo.apply({}, o);
10591         delete o.callback;
10592         delete o.scope;
10593         this.lastOptions = o;
10594     },
10595
10596     /**
10597      * Loads the Record cache from the configured Proxy using the configured Reader.
10598      * <p>
10599      * If using remote paging, then the first load call must specify the <em>start</em>
10600      * and <em>limit</em> properties in the options.params property to establish the initial
10601      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10602      * <p>
10603      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10604      * and this call will return before the new data has been loaded. Perform any post-processing
10605      * in a callback function, or in a "load" event handler.</strong>
10606      * <p>
10607      * @param {Object} options An object containing properties which control loading options:<ul>
10608      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10609      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10610      * passed the following arguments:<ul>
10611      * <li>r : Roo.data.Record[]</li>
10612      * <li>options: Options object from the load call</li>
10613      * <li>success: Boolean success indicator</li></ul></li>
10614      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10615      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10616      * </ul>
10617      */
10618     load : function(options){
10619         options = options || {};
10620         if(this.fireEvent("beforeload", this, options) !== false){
10621             this.storeOptions(options);
10622             var p = Roo.apply(options.params || {}, this.baseParams);
10623             // if meta was not loaded from remote source.. try requesting it.
10624             if (!this.reader.metaFromRemote) {
10625                 p._requestMeta = 1;
10626             }
10627             if(this.sortInfo && this.remoteSort){
10628                 var pn = this.paramNames;
10629                 p[pn["sort"]] = this.sortInfo.field;
10630                 p[pn["dir"]] = this.sortInfo.direction;
10631             }
10632             if (this.multiSort) {
10633                 var pn = this.paramNames;
10634                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10635             }
10636             
10637             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10638         }
10639     },
10640
10641     /**
10642      * Reloads the Record cache from the configured Proxy using the configured Reader and
10643      * the options from the last load operation performed.
10644      * @param {Object} options (optional) An object containing properties which may override the options
10645      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10646      * the most recently used options are reused).
10647      */
10648     reload : function(options){
10649         this.load(Roo.applyIf(options||{}, this.lastOptions));
10650     },
10651
10652     // private
10653     // Called as a callback by the Reader during a load operation.
10654     loadRecords : function(o, options, success){
10655         if(!o || success === false){
10656             if(success !== false){
10657                 this.fireEvent("load", this, [], options, o);
10658             }
10659             if(options.callback){
10660                 options.callback.call(options.scope || this, [], options, false);
10661             }
10662             return;
10663         }
10664         // if data returned failure - throw an exception.
10665         if (o.success === false) {
10666             // show a message if no listener is registered.
10667             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10668                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10669             }
10670             // loadmask wil be hooked into this..
10671             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10672             return;
10673         }
10674         var r = o.records, t = o.totalRecords || r.length;
10675         
10676         this.fireEvent("beforeloadadd", this, r, options, o);
10677         
10678         if(!options || options.add !== true){
10679             if(this.pruneModifiedRecords){
10680                 this.modified = [];
10681             }
10682             for(var i = 0, len = r.length; i < len; i++){
10683                 r[i].join(this);
10684             }
10685             if(this.snapshot){
10686                 this.data = this.snapshot;
10687                 delete this.snapshot;
10688             }
10689             this.data.clear();
10690             this.data.addAll(r);
10691             this.totalLength = t;
10692             this.applySort();
10693             this.fireEvent("datachanged", this);
10694         }else{
10695             this.totalLength = Math.max(t, this.data.length+r.length);
10696             this.add(r);
10697         }
10698         this.fireEvent("load", this, r, options, o);
10699         if(options.callback){
10700             options.callback.call(options.scope || this, r, options, true);
10701         }
10702     },
10703
10704
10705     /**
10706      * Loads data from a passed data block. A Reader which understands the format of the data
10707      * must have been configured in the constructor.
10708      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10709      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10710      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10711      */
10712     loadData : function(o, append){
10713         var r = this.reader.readRecords(o);
10714         this.loadRecords(r, {add: append}, true);
10715     },
10716
10717     /**
10718      * Gets the number of cached records.
10719      * <p>
10720      * <em>If using paging, this may not be the total size of the dataset. If the data object
10721      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10722      * the data set size</em>
10723      */
10724     getCount : function(){
10725         return this.data.length || 0;
10726     },
10727
10728     /**
10729      * Gets the total number of records in the dataset as returned by the server.
10730      * <p>
10731      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10732      * the dataset size</em>
10733      */
10734     getTotalCount : function(){
10735         return this.totalLength || 0;
10736     },
10737
10738     /**
10739      * Returns the sort state of the Store as an object with two properties:
10740      * <pre><code>
10741  field {String} The name of the field by which the Records are sorted
10742  direction {String} The sort order, "ASC" or "DESC"
10743      * </code></pre>
10744      */
10745     getSortState : function(){
10746         return this.sortInfo;
10747     },
10748
10749     // private
10750     applySort : function(){
10751         if(this.sortInfo && !this.remoteSort){
10752             var s = this.sortInfo, f = s.field;
10753             var st = this.fields.get(f).sortType;
10754             var fn = function(r1, r2){
10755                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10756                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10757             };
10758             this.data.sort(s.direction, fn);
10759             if(this.snapshot && this.snapshot != this.data){
10760                 this.snapshot.sort(s.direction, fn);
10761             }
10762         }
10763     },
10764
10765     /**
10766      * Sets the default sort column and order to be used by the next load operation.
10767      * @param {String} fieldName The name of the field to sort by.
10768      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10769      */
10770     setDefaultSort : function(field, dir){
10771         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10772     },
10773
10774     /**
10775      * Sort the Records.
10776      * If remote sorting is used, the sort is performed on the server, and the cache is
10777      * reloaded. If local sorting is used, the cache is sorted internally.
10778      * @param {String} fieldName The name of the field to sort by.
10779      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10780      */
10781     sort : function(fieldName, dir){
10782         var f = this.fields.get(fieldName);
10783         if(!dir){
10784             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10785             
10786             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10787                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10788             }else{
10789                 dir = f.sortDir;
10790             }
10791         }
10792         this.sortToggle[f.name] = dir;
10793         this.sortInfo = {field: f.name, direction: dir};
10794         if(!this.remoteSort){
10795             this.applySort();
10796             this.fireEvent("datachanged", this);
10797         }else{
10798             this.load(this.lastOptions);
10799         }
10800     },
10801
10802     /**
10803      * Calls the specified function for each of the Records in the cache.
10804      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10805      * Returning <em>false</em> aborts and exits the iteration.
10806      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10807      */
10808     each : function(fn, scope){
10809         this.data.each(fn, scope);
10810     },
10811
10812     /**
10813      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10814      * (e.g., during paging).
10815      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10816      */
10817     getModifiedRecords : function(){
10818         return this.modified;
10819     },
10820
10821     // private
10822     createFilterFn : function(property, value, anyMatch){
10823         if(!value.exec){ // not a regex
10824             value = String(value);
10825             if(value.length == 0){
10826                 return false;
10827             }
10828             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10829         }
10830         return function(r){
10831             return value.test(r.data[property]);
10832         };
10833     },
10834
10835     /**
10836      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10837      * @param {String} property A field on your records
10838      * @param {Number} start The record index to start at (defaults to 0)
10839      * @param {Number} end The last record index to include (defaults to length - 1)
10840      * @return {Number} The sum
10841      */
10842     sum : function(property, start, end){
10843         var rs = this.data.items, v = 0;
10844         start = start || 0;
10845         end = (end || end === 0) ? end : rs.length-1;
10846
10847         for(var i = start; i <= end; i++){
10848             v += (rs[i].data[property] || 0);
10849         }
10850         return v;
10851     },
10852
10853     /**
10854      * Filter the records by a specified property.
10855      * @param {String} field A field on your records
10856      * @param {String/RegExp} value Either a string that the field
10857      * should start with or a RegExp to test against the field
10858      * @param {Boolean} anyMatch True to match any part not just the beginning
10859      */
10860     filter : function(property, value, anyMatch){
10861         var fn = this.createFilterFn(property, value, anyMatch);
10862         return fn ? this.filterBy(fn) : this.clearFilter();
10863     },
10864
10865     /**
10866      * Filter by a function. The specified function will be called with each
10867      * record in this data source. If the function returns true the record is included,
10868      * otherwise it is filtered.
10869      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10870      * @param {Object} scope (optional) The scope of the function (defaults to this)
10871      */
10872     filterBy : function(fn, scope){
10873         this.snapshot = this.snapshot || this.data;
10874         this.data = this.queryBy(fn, scope||this);
10875         this.fireEvent("datachanged", this);
10876     },
10877
10878     /**
10879      * Query the records by a specified property.
10880      * @param {String} field A field on your records
10881      * @param {String/RegExp} value Either a string that the field
10882      * should start with or a RegExp to test against the field
10883      * @param {Boolean} anyMatch True to match any part not just the beginning
10884      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10885      */
10886     query : function(property, value, anyMatch){
10887         var fn = this.createFilterFn(property, value, anyMatch);
10888         return fn ? this.queryBy(fn) : this.data.clone();
10889     },
10890
10891     /**
10892      * Query by a function. The specified function will be called with each
10893      * record in this data source. If the function returns true the record is included
10894      * in the results.
10895      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10896      * @param {Object} scope (optional) The scope of the function (defaults to this)
10897       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10898      **/
10899     queryBy : function(fn, scope){
10900         var data = this.snapshot || this.data;
10901         return data.filterBy(fn, scope||this);
10902     },
10903
10904     /**
10905      * Collects unique values for a particular dataIndex from this store.
10906      * @param {String} dataIndex The property to collect
10907      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10908      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10909      * @return {Array} An array of the unique values
10910      **/
10911     collect : function(dataIndex, allowNull, bypassFilter){
10912         var d = (bypassFilter === true && this.snapshot) ?
10913                 this.snapshot.items : this.data.items;
10914         var v, sv, r = [], l = {};
10915         for(var i = 0, len = d.length; i < len; i++){
10916             v = d[i].data[dataIndex];
10917             sv = String(v);
10918             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10919                 l[sv] = true;
10920                 r[r.length] = v;
10921             }
10922         }
10923         return r;
10924     },
10925
10926     /**
10927      * Revert to a view of the Record cache with no filtering applied.
10928      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10929      */
10930     clearFilter : function(suppressEvent){
10931         if(this.snapshot && this.snapshot != this.data){
10932             this.data = this.snapshot;
10933             delete this.snapshot;
10934             if(suppressEvent !== true){
10935                 this.fireEvent("datachanged", this);
10936             }
10937         }
10938     },
10939
10940     // private
10941     afterEdit : function(record){
10942         if(this.modified.indexOf(record) == -1){
10943             this.modified.push(record);
10944         }
10945         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10946     },
10947     
10948     // private
10949     afterReject : function(record){
10950         this.modified.remove(record);
10951         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10952     },
10953
10954     // private
10955     afterCommit : function(record){
10956         this.modified.remove(record);
10957         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10958     },
10959
10960     /**
10961      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10962      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10963      */
10964     commitChanges : function(){
10965         var m = this.modified.slice(0);
10966         this.modified = [];
10967         for(var i = 0, len = m.length; i < len; i++){
10968             m[i].commit();
10969         }
10970     },
10971
10972     /**
10973      * Cancel outstanding changes on all changed records.
10974      */
10975     rejectChanges : function(){
10976         var m = this.modified.slice(0);
10977         this.modified = [];
10978         for(var i = 0, len = m.length; i < len; i++){
10979             m[i].reject();
10980         }
10981     },
10982
10983     onMetaChange : function(meta, rtype, o){
10984         this.recordType = rtype;
10985         this.fields = rtype.prototype.fields;
10986         delete this.snapshot;
10987         this.sortInfo = meta.sortInfo || this.sortInfo;
10988         this.modified = [];
10989         this.fireEvent('metachange', this, this.reader.meta);
10990     },
10991     
10992     moveIndex : function(data, type)
10993     {
10994         var index = this.indexOf(data);
10995         
10996         var newIndex = index + type;
10997         
10998         this.remove(data);
10999         
11000         this.insert(newIndex, data);
11001         
11002     }
11003 });/*
11004  * Based on:
11005  * Ext JS Library 1.1.1
11006  * Copyright(c) 2006-2007, Ext JS, LLC.
11007  *
11008  * Originally Released Under LGPL - original licence link has changed is not relivant.
11009  *
11010  * Fork - LGPL
11011  * <script type="text/javascript">
11012  */
11013
11014 /**
11015  * @class Roo.data.SimpleStore
11016  * @extends Roo.data.Store
11017  * Small helper class to make creating Stores from Array data easier.
11018  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11019  * @cfg {Array} fields An array of field definition objects, or field name strings.
11020  * @cfg {Array} data The multi-dimensional array of data
11021  * @constructor
11022  * @param {Object} config
11023  */
11024 Roo.data.SimpleStore = function(config){
11025     Roo.data.SimpleStore.superclass.constructor.call(this, {
11026         isLocal : true,
11027         reader: new Roo.data.ArrayReader({
11028                 id: config.id
11029             },
11030             Roo.data.Record.create(config.fields)
11031         ),
11032         proxy : new Roo.data.MemoryProxy(config.data)
11033     });
11034     this.load();
11035 };
11036 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11037  * Based on:
11038  * Ext JS Library 1.1.1
11039  * Copyright(c) 2006-2007, Ext JS, LLC.
11040  *
11041  * Originally Released Under LGPL - original licence link has changed is not relivant.
11042  *
11043  * Fork - LGPL
11044  * <script type="text/javascript">
11045  */
11046
11047 /**
11048 /**
11049  * @extends Roo.data.Store
11050  * @class Roo.data.JsonStore
11051  * Small helper class to make creating Stores for JSON data easier. <br/>
11052 <pre><code>
11053 var store = new Roo.data.JsonStore({
11054     url: 'get-images.php',
11055     root: 'images',
11056     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11057 });
11058 </code></pre>
11059  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11060  * JsonReader and HttpProxy (unless inline data is provided).</b>
11061  * @cfg {Array} fields An array of field definition objects, or field name strings.
11062  * @constructor
11063  * @param {Object} config
11064  */
11065 Roo.data.JsonStore = function(c){
11066     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11067         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11068         reader: new Roo.data.JsonReader(c, c.fields)
11069     }));
11070 };
11071 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11072  * Based on:
11073  * Ext JS Library 1.1.1
11074  * Copyright(c) 2006-2007, Ext JS, LLC.
11075  *
11076  * Originally Released Under LGPL - original licence link has changed is not relivant.
11077  *
11078  * Fork - LGPL
11079  * <script type="text/javascript">
11080  */
11081
11082  
11083 Roo.data.Field = function(config){
11084     if(typeof config == "string"){
11085         config = {name: config};
11086     }
11087     Roo.apply(this, config);
11088     
11089     if(!this.type){
11090         this.type = "auto";
11091     }
11092     
11093     var st = Roo.data.SortTypes;
11094     // named sortTypes are supported, here we look them up
11095     if(typeof this.sortType == "string"){
11096         this.sortType = st[this.sortType];
11097     }
11098     
11099     // set default sortType for strings and dates
11100     if(!this.sortType){
11101         switch(this.type){
11102             case "string":
11103                 this.sortType = st.asUCString;
11104                 break;
11105             case "date":
11106                 this.sortType = st.asDate;
11107                 break;
11108             default:
11109                 this.sortType = st.none;
11110         }
11111     }
11112
11113     // define once
11114     var stripRe = /[\$,%]/g;
11115
11116     // prebuilt conversion function for this field, instead of
11117     // switching every time we're reading a value
11118     if(!this.convert){
11119         var cv, dateFormat = this.dateFormat;
11120         switch(this.type){
11121             case "":
11122             case "auto":
11123             case undefined:
11124                 cv = function(v){ return v; };
11125                 break;
11126             case "string":
11127                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11128                 break;
11129             case "int":
11130                 cv = function(v){
11131                     return v !== undefined && v !== null && v !== '' ?
11132                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11133                     };
11134                 break;
11135             case "float":
11136                 cv = function(v){
11137                     return v !== undefined && v !== null && v !== '' ?
11138                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11139                     };
11140                 break;
11141             case "bool":
11142             case "boolean":
11143                 cv = function(v){ return v === true || v === "true" || v == 1; };
11144                 break;
11145             case "date":
11146                 cv = function(v){
11147                     if(!v){
11148                         return '';
11149                     }
11150                     if(v instanceof Date){
11151                         return v;
11152                     }
11153                     if(dateFormat){
11154                         if(dateFormat == "timestamp"){
11155                             return new Date(v*1000);
11156                         }
11157                         return Date.parseDate(v, dateFormat);
11158                     }
11159                     var parsed = Date.parse(v);
11160                     return parsed ? new Date(parsed) : null;
11161                 };
11162              break;
11163             
11164         }
11165         this.convert = cv;
11166     }
11167 };
11168
11169 Roo.data.Field.prototype = {
11170     dateFormat: null,
11171     defaultValue: "",
11172     mapping: null,
11173     sortType : null,
11174     sortDir : "ASC"
11175 };/*
11176  * Based on:
11177  * Ext JS Library 1.1.1
11178  * Copyright(c) 2006-2007, Ext JS, LLC.
11179  *
11180  * Originally Released Under LGPL - original licence link has changed is not relivant.
11181  *
11182  * Fork - LGPL
11183  * <script type="text/javascript">
11184  */
11185  
11186 // Base class for reading structured data from a data source.  This class is intended to be
11187 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11188
11189 /**
11190  * @class Roo.data.DataReader
11191  * Base class for reading structured data from a data source.  This class is intended to be
11192  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11193  */
11194
11195 Roo.data.DataReader = function(meta, recordType){
11196     
11197     this.meta = meta;
11198     
11199     this.recordType = recordType instanceof Array ? 
11200         Roo.data.Record.create(recordType) : recordType;
11201 };
11202
11203 Roo.data.DataReader.prototype = {
11204      /**
11205      * Create an empty record
11206      * @param {Object} data (optional) - overlay some values
11207      * @return {Roo.data.Record} record created.
11208      */
11209     newRow :  function(d) {
11210         var da =  {};
11211         this.recordType.prototype.fields.each(function(c) {
11212             switch( c.type) {
11213                 case 'int' : da[c.name] = 0; break;
11214                 case 'date' : da[c.name] = new Date(); break;
11215                 case 'float' : da[c.name] = 0.0; break;
11216                 case 'boolean' : da[c.name] = false; break;
11217                 default : da[c.name] = ""; break;
11218             }
11219             
11220         });
11221         return new this.recordType(Roo.apply(da, d));
11222     }
11223     
11224 };/*
11225  * Based on:
11226  * Ext JS Library 1.1.1
11227  * Copyright(c) 2006-2007, Ext JS, LLC.
11228  *
11229  * Originally Released Under LGPL - original licence link has changed is not relivant.
11230  *
11231  * Fork - LGPL
11232  * <script type="text/javascript">
11233  */
11234
11235 /**
11236  * @class Roo.data.DataProxy
11237  * @extends Roo.data.Observable
11238  * This class is an abstract base class for implementations which provide retrieval of
11239  * unformatted data objects.<br>
11240  * <p>
11241  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11242  * (of the appropriate type which knows how to parse the data object) to provide a block of
11243  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11244  * <p>
11245  * Custom implementations must implement the load method as described in
11246  * {@link Roo.data.HttpProxy#load}.
11247  */
11248 Roo.data.DataProxy = function(){
11249     this.addEvents({
11250         /**
11251          * @event beforeload
11252          * Fires before a network request is made to retrieve a data object.
11253          * @param {Object} This DataProxy object.
11254          * @param {Object} params The params parameter to the load function.
11255          */
11256         beforeload : true,
11257         /**
11258          * @event load
11259          * Fires before the load method's callback is called.
11260          * @param {Object} This DataProxy object.
11261          * @param {Object} o The data object.
11262          * @param {Object} arg The callback argument object passed to the load function.
11263          */
11264         load : true,
11265         /**
11266          * @event loadexception
11267          * Fires if an Exception occurs during data retrieval.
11268          * @param {Object} This DataProxy object.
11269          * @param {Object} o The data object.
11270          * @param {Object} arg The callback argument object passed to the load function.
11271          * @param {Object} e The Exception.
11272          */
11273         loadexception : true
11274     });
11275     Roo.data.DataProxy.superclass.constructor.call(this);
11276 };
11277
11278 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11279
11280     /**
11281      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11282      */
11283 /*
11284  * Based on:
11285  * Ext JS Library 1.1.1
11286  * Copyright(c) 2006-2007, Ext JS, LLC.
11287  *
11288  * Originally Released Under LGPL - original licence link has changed is not relivant.
11289  *
11290  * Fork - LGPL
11291  * <script type="text/javascript">
11292  */
11293 /**
11294  * @class Roo.data.MemoryProxy
11295  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11296  * to the Reader when its load method is called.
11297  * @constructor
11298  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11299  */
11300 Roo.data.MemoryProxy = function(data){
11301     if (data.data) {
11302         data = data.data;
11303     }
11304     Roo.data.MemoryProxy.superclass.constructor.call(this);
11305     this.data = data;
11306 };
11307
11308 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11309     
11310     /**
11311      * Load data from the requested source (in this case an in-memory
11312      * data object passed to the constructor), read the data object into
11313      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11314      * process that block using the passed callback.
11315      * @param {Object} params This parameter is not used by the MemoryProxy class.
11316      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11317      * object into a block of Roo.data.Records.
11318      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11319      * The function must be passed <ul>
11320      * <li>The Record block object</li>
11321      * <li>The "arg" argument from the load function</li>
11322      * <li>A boolean success indicator</li>
11323      * </ul>
11324      * @param {Object} scope The scope in which to call the callback
11325      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11326      */
11327     load : function(params, reader, callback, scope, arg){
11328         params = params || {};
11329         var result;
11330         try {
11331             result = reader.readRecords(this.data);
11332         }catch(e){
11333             this.fireEvent("loadexception", this, arg, null, e);
11334             callback.call(scope, null, arg, false);
11335             return;
11336         }
11337         callback.call(scope, result, arg, true);
11338     },
11339     
11340     // private
11341     update : function(params, records){
11342         
11343     }
11344 });/*
11345  * Based on:
11346  * Ext JS Library 1.1.1
11347  * Copyright(c) 2006-2007, Ext JS, LLC.
11348  *
11349  * Originally Released Under LGPL - original licence link has changed is not relivant.
11350  *
11351  * Fork - LGPL
11352  * <script type="text/javascript">
11353  */
11354 /**
11355  * @class Roo.data.HttpProxy
11356  * @extends Roo.data.DataProxy
11357  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11358  * configured to reference a certain URL.<br><br>
11359  * <p>
11360  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11361  * from which the running page was served.<br><br>
11362  * <p>
11363  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11364  * <p>
11365  * Be aware that to enable the browser to parse an XML document, the server must set
11366  * the Content-Type header in the HTTP response to "text/xml".
11367  * @constructor
11368  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11369  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11370  * will be used to make the request.
11371  */
11372 Roo.data.HttpProxy = function(conn){
11373     Roo.data.HttpProxy.superclass.constructor.call(this);
11374     // is conn a conn config or a real conn?
11375     this.conn = conn;
11376     this.useAjax = !conn || !conn.events;
11377   
11378 };
11379
11380 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11381     // thse are take from connection...
11382     
11383     /**
11384      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11385      */
11386     /**
11387      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11388      * extra parameters to each request made by this object. (defaults to undefined)
11389      */
11390     /**
11391      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11392      *  to each request made by this object. (defaults to undefined)
11393      */
11394     /**
11395      * @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)
11396      */
11397     /**
11398      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11399      */
11400      /**
11401      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11402      * @type Boolean
11403      */
11404   
11405
11406     /**
11407      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11408      * @type Boolean
11409      */
11410     /**
11411      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11412      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11413      * a finer-grained basis than the DataProxy events.
11414      */
11415     getConnection : function(){
11416         return this.useAjax ? Roo.Ajax : this.conn;
11417     },
11418
11419     /**
11420      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11421      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11422      * process that block using the passed callback.
11423      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11424      * for the request to the remote server.
11425      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11426      * object into a block of Roo.data.Records.
11427      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11428      * The function must be passed <ul>
11429      * <li>The Record block object</li>
11430      * <li>The "arg" argument from the load function</li>
11431      * <li>A boolean success indicator</li>
11432      * </ul>
11433      * @param {Object} scope The scope in which to call the callback
11434      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11435      */
11436     load : function(params, reader, callback, scope, arg){
11437         if(this.fireEvent("beforeload", this, params) !== false){
11438             var  o = {
11439                 params : params || {},
11440                 request: {
11441                     callback : callback,
11442                     scope : scope,
11443                     arg : arg
11444                 },
11445                 reader: reader,
11446                 callback : this.loadResponse,
11447                 scope: this
11448             };
11449             if(this.useAjax){
11450                 Roo.applyIf(o, this.conn);
11451                 if(this.activeRequest){
11452                     Roo.Ajax.abort(this.activeRequest);
11453                 }
11454                 this.activeRequest = Roo.Ajax.request(o);
11455             }else{
11456                 this.conn.request(o);
11457             }
11458         }else{
11459             callback.call(scope||this, null, arg, false);
11460         }
11461     },
11462
11463     // private
11464     loadResponse : function(o, success, response){
11465         delete this.activeRequest;
11466         if(!success){
11467             this.fireEvent("loadexception", this, o, response);
11468             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11469             return;
11470         }
11471         var result;
11472         try {
11473             result = o.reader.read(response);
11474         }catch(e){
11475             this.fireEvent("loadexception", this, o, response, e);
11476             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11477             return;
11478         }
11479         
11480         this.fireEvent("load", this, o, o.request.arg);
11481         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11482     },
11483
11484     // private
11485     update : function(dataSet){
11486
11487     },
11488
11489     // private
11490     updateResponse : function(dataSet){
11491
11492     }
11493 });/*
11494  * Based on:
11495  * Ext JS Library 1.1.1
11496  * Copyright(c) 2006-2007, Ext JS, LLC.
11497  *
11498  * Originally Released Under LGPL - original licence link has changed is not relivant.
11499  *
11500  * Fork - LGPL
11501  * <script type="text/javascript">
11502  */
11503
11504 /**
11505  * @class Roo.data.ScriptTagProxy
11506  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11507  * other than the originating domain of the running page.<br><br>
11508  * <p>
11509  * <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
11510  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11511  * <p>
11512  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11513  * source code that is used as the source inside a &lt;script> tag.<br><br>
11514  * <p>
11515  * In order for the browser to process the returned data, the server must wrap the data object
11516  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11517  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11518  * depending on whether the callback name was passed:
11519  * <p>
11520  * <pre><code>
11521 boolean scriptTag = false;
11522 String cb = request.getParameter("callback");
11523 if (cb != null) {
11524     scriptTag = true;
11525     response.setContentType("text/javascript");
11526 } else {
11527     response.setContentType("application/x-json");
11528 }
11529 Writer out = response.getWriter();
11530 if (scriptTag) {
11531     out.write(cb + "(");
11532 }
11533 out.print(dataBlock.toJsonString());
11534 if (scriptTag) {
11535     out.write(");");
11536 }
11537 </pre></code>
11538  *
11539  * @constructor
11540  * @param {Object} config A configuration object.
11541  */
11542 Roo.data.ScriptTagProxy = function(config){
11543     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11544     Roo.apply(this, config);
11545     this.head = document.getElementsByTagName("head")[0];
11546 };
11547
11548 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11549
11550 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11551     /**
11552      * @cfg {String} url The URL from which to request the data object.
11553      */
11554     /**
11555      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11556      */
11557     timeout : 30000,
11558     /**
11559      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11560      * the server the name of the callback function set up by the load call to process the returned data object.
11561      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11562      * javascript output which calls this named function passing the data object as its only parameter.
11563      */
11564     callbackParam : "callback",
11565     /**
11566      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11567      * name to the request.
11568      */
11569     nocache : true,
11570
11571     /**
11572      * Load data from the configured URL, read the data object into
11573      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11574      * process that block using the passed callback.
11575      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11576      * for the request to the remote server.
11577      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11578      * object into a block of Roo.data.Records.
11579      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11580      * The function must be passed <ul>
11581      * <li>The Record block object</li>
11582      * <li>The "arg" argument from the load function</li>
11583      * <li>A boolean success indicator</li>
11584      * </ul>
11585      * @param {Object} scope The scope in which to call the callback
11586      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11587      */
11588     load : function(params, reader, callback, scope, arg){
11589         if(this.fireEvent("beforeload", this, params) !== false){
11590
11591             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11592
11593             var url = this.url;
11594             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11595             if(this.nocache){
11596                 url += "&_dc=" + (new Date().getTime());
11597             }
11598             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11599             var trans = {
11600                 id : transId,
11601                 cb : "stcCallback"+transId,
11602                 scriptId : "stcScript"+transId,
11603                 params : params,
11604                 arg : arg,
11605                 url : url,
11606                 callback : callback,
11607                 scope : scope,
11608                 reader : reader
11609             };
11610             var conn = this;
11611
11612             window[trans.cb] = function(o){
11613                 conn.handleResponse(o, trans);
11614             };
11615
11616             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11617
11618             if(this.autoAbort !== false){
11619                 this.abort();
11620             }
11621
11622             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11623
11624             var script = document.createElement("script");
11625             script.setAttribute("src", url);
11626             script.setAttribute("type", "text/javascript");
11627             script.setAttribute("id", trans.scriptId);
11628             this.head.appendChild(script);
11629
11630             this.trans = trans;
11631         }else{
11632             callback.call(scope||this, null, arg, false);
11633         }
11634     },
11635
11636     // private
11637     isLoading : function(){
11638         return this.trans ? true : false;
11639     },
11640
11641     /**
11642      * Abort the current server request.
11643      */
11644     abort : function(){
11645         if(this.isLoading()){
11646             this.destroyTrans(this.trans);
11647         }
11648     },
11649
11650     // private
11651     destroyTrans : function(trans, isLoaded){
11652         this.head.removeChild(document.getElementById(trans.scriptId));
11653         clearTimeout(trans.timeoutId);
11654         if(isLoaded){
11655             window[trans.cb] = undefined;
11656             try{
11657                 delete window[trans.cb];
11658             }catch(e){}
11659         }else{
11660             // if hasn't been loaded, wait for load to remove it to prevent script error
11661             window[trans.cb] = function(){
11662                 window[trans.cb] = undefined;
11663                 try{
11664                     delete window[trans.cb];
11665                 }catch(e){}
11666             };
11667         }
11668     },
11669
11670     // private
11671     handleResponse : function(o, trans){
11672         this.trans = false;
11673         this.destroyTrans(trans, true);
11674         var result;
11675         try {
11676             result = trans.reader.readRecords(o);
11677         }catch(e){
11678             this.fireEvent("loadexception", this, o, trans.arg, e);
11679             trans.callback.call(trans.scope||window, null, trans.arg, false);
11680             return;
11681         }
11682         this.fireEvent("load", this, o, trans.arg);
11683         trans.callback.call(trans.scope||window, result, trans.arg, true);
11684     },
11685
11686     // private
11687     handleFailure : function(trans){
11688         this.trans = false;
11689         this.destroyTrans(trans, false);
11690         this.fireEvent("loadexception", this, null, trans.arg);
11691         trans.callback.call(trans.scope||window, null, trans.arg, false);
11692     }
11693 });/*
11694  * Based on:
11695  * Ext JS Library 1.1.1
11696  * Copyright(c) 2006-2007, Ext JS, LLC.
11697  *
11698  * Originally Released Under LGPL - original licence link has changed is not relivant.
11699  *
11700  * Fork - LGPL
11701  * <script type="text/javascript">
11702  */
11703
11704 /**
11705  * @class Roo.data.JsonReader
11706  * @extends Roo.data.DataReader
11707  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11708  * based on mappings in a provided Roo.data.Record constructor.
11709  * 
11710  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11711  * in the reply previously. 
11712  * 
11713  * <p>
11714  * Example code:
11715  * <pre><code>
11716 var RecordDef = Roo.data.Record.create([
11717     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11718     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11719 ]);
11720 var myReader = new Roo.data.JsonReader({
11721     totalProperty: "results",    // The property which contains the total dataset size (optional)
11722     root: "rows",                // The property which contains an Array of row objects
11723     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11724 }, RecordDef);
11725 </code></pre>
11726  * <p>
11727  * This would consume a JSON file like this:
11728  * <pre><code>
11729 { 'results': 2, 'rows': [
11730     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11731     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11732 }
11733 </code></pre>
11734  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11735  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11736  * paged from the remote server.
11737  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11738  * @cfg {String} root name of the property which contains the Array of row objects.
11739  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11740  * @cfg {Array} fields Array of field definition objects
11741  * @constructor
11742  * Create a new JsonReader
11743  * @param {Object} meta Metadata configuration options
11744  * @param {Object} recordType Either an Array of field definition objects,
11745  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11746  */
11747 Roo.data.JsonReader = function(meta, recordType){
11748     
11749     meta = meta || {};
11750     // set some defaults:
11751     Roo.applyIf(meta, {
11752         totalProperty: 'total',
11753         successProperty : 'success',
11754         root : 'data',
11755         id : 'id'
11756     });
11757     
11758     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11759 };
11760 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11761     
11762     /**
11763      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11764      * Used by Store query builder to append _requestMeta to params.
11765      * 
11766      */
11767     metaFromRemote : false,
11768     /**
11769      * This method is only used by a DataProxy which has retrieved data from a remote server.
11770      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11771      * @return {Object} data A data block which is used by an Roo.data.Store object as
11772      * a cache of Roo.data.Records.
11773      */
11774     read : function(response){
11775         var json = response.responseText;
11776        
11777         var o = /* eval:var:o */ eval("("+json+")");
11778         if(!o) {
11779             throw {message: "JsonReader.read: Json object not found"};
11780         }
11781         
11782         if(o.metaData){
11783             
11784             delete this.ef;
11785             this.metaFromRemote = true;
11786             this.meta = o.metaData;
11787             this.recordType = Roo.data.Record.create(o.metaData.fields);
11788             this.onMetaChange(this.meta, this.recordType, o);
11789         }
11790         return this.readRecords(o);
11791     },
11792
11793     // private function a store will implement
11794     onMetaChange : function(meta, recordType, o){
11795
11796     },
11797
11798     /**
11799          * @ignore
11800          */
11801     simpleAccess: function(obj, subsc) {
11802         return obj[subsc];
11803     },
11804
11805         /**
11806          * @ignore
11807          */
11808     getJsonAccessor: function(){
11809         var re = /[\[\.]/;
11810         return function(expr) {
11811             try {
11812                 return(re.test(expr))
11813                     ? new Function("obj", "return obj." + expr)
11814                     : function(obj){
11815                         return obj[expr];
11816                     };
11817             } catch(e){}
11818             return Roo.emptyFn;
11819         };
11820     }(),
11821
11822     /**
11823      * Create a data block containing Roo.data.Records from an XML document.
11824      * @param {Object} o An object which contains an Array of row objects in the property specified
11825      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11826      * which contains the total size of the dataset.
11827      * @return {Object} data A data block which is used by an Roo.data.Store object as
11828      * a cache of Roo.data.Records.
11829      */
11830     readRecords : function(o){
11831         /**
11832          * After any data loads, the raw JSON data is available for further custom processing.
11833          * @type Object
11834          */
11835         this.o = o;
11836         var s = this.meta, Record = this.recordType,
11837             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11838
11839 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11840         if (!this.ef) {
11841             if(s.totalProperty) {
11842                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11843                 }
11844                 if(s.successProperty) {
11845                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11846                 }
11847                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11848                 if (s.id) {
11849                         var g = this.getJsonAccessor(s.id);
11850                         this.getId = function(rec) {
11851                                 var r = g(rec);  
11852                                 return (r === undefined || r === "") ? null : r;
11853                         };
11854                 } else {
11855                         this.getId = function(){return null;};
11856                 }
11857             this.ef = [];
11858             for(var jj = 0; jj < fl; jj++){
11859                 f = fi[jj];
11860                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11861                 this.ef[jj] = this.getJsonAccessor(map);
11862             }
11863         }
11864
11865         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11866         if(s.totalProperty){
11867             var vt = parseInt(this.getTotal(o), 10);
11868             if(!isNaN(vt)){
11869                 totalRecords = vt;
11870             }
11871         }
11872         if(s.successProperty){
11873             var vs = this.getSuccess(o);
11874             if(vs === false || vs === 'false'){
11875                 success = false;
11876             }
11877         }
11878         var records = [];
11879         for(var i = 0; i < c; i++){
11880                 var n = root[i];
11881             var values = {};
11882             var id = this.getId(n);
11883             for(var j = 0; j < fl; j++){
11884                 f = fi[j];
11885             var v = this.ef[j](n);
11886             if (!f.convert) {
11887                 Roo.log('missing convert for ' + f.name);
11888                 Roo.log(f);
11889                 continue;
11890             }
11891             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11892             }
11893             var record = new Record(values, id);
11894             record.json = n;
11895             records[i] = record;
11896         }
11897         return {
11898             raw : o,
11899             success : success,
11900             records : records,
11901             totalRecords : totalRecords
11902         };
11903     }
11904 });/*
11905  * Based on:
11906  * Ext JS Library 1.1.1
11907  * Copyright(c) 2006-2007, Ext JS, LLC.
11908  *
11909  * Originally Released Under LGPL - original licence link has changed is not relivant.
11910  *
11911  * Fork - LGPL
11912  * <script type="text/javascript">
11913  */
11914
11915 /**
11916  * @class Roo.data.ArrayReader
11917  * @extends Roo.data.DataReader
11918  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11919  * Each element of that Array represents a row of data fields. The
11920  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11921  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11922  * <p>
11923  * Example code:.
11924  * <pre><code>
11925 var RecordDef = Roo.data.Record.create([
11926     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11927     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11928 ]);
11929 var myReader = new Roo.data.ArrayReader({
11930     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11931 }, RecordDef);
11932 </code></pre>
11933  * <p>
11934  * This would consume an Array like this:
11935  * <pre><code>
11936 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11937   </code></pre>
11938  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11939  * @constructor
11940  * Create a new JsonReader
11941  * @param {Object} meta Metadata configuration options.
11942  * @param {Object} recordType Either an Array of field definition objects
11943  * as specified to {@link Roo.data.Record#create},
11944  * or an {@link Roo.data.Record} object
11945  * created using {@link Roo.data.Record#create}.
11946  */
11947 Roo.data.ArrayReader = function(meta, recordType){
11948     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11949 };
11950
11951 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11952     /**
11953      * Create a data block containing Roo.data.Records from an XML document.
11954      * @param {Object} o An Array of row objects which represents the dataset.
11955      * @return {Object} data A data block which is used by an Roo.data.Store object as
11956      * a cache of Roo.data.Records.
11957      */
11958     readRecords : function(o){
11959         var sid = this.meta ? this.meta.id : null;
11960         var recordType = this.recordType, fields = recordType.prototype.fields;
11961         var records = [];
11962         var root = o;
11963             for(var i = 0; i < root.length; i++){
11964                     var n = root[i];
11965                 var values = {};
11966                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11967                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11968                 var f = fields.items[j];
11969                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11970                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11971                 v = f.convert(v);
11972                 values[f.name] = v;
11973             }
11974                 var record = new recordType(values, id);
11975                 record.json = n;
11976                 records[records.length] = record;
11977             }
11978             return {
11979                 records : records,
11980                 totalRecords : records.length
11981             };
11982     }
11983 });/*
11984  * - LGPL
11985  * * 
11986  */
11987
11988 /**
11989  * @class Roo.bootstrap.ComboBox
11990  * @extends Roo.bootstrap.TriggerField
11991  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11992  * @cfg {Boolean} append (true|false) default false
11993  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11994  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11995  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11996  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11997  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11998  * @cfg {Boolean} animate default true
11999  * @cfg {Boolean} emptyResultText only for touch device
12000  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12001  * @constructor
12002  * Create a new ComboBox.
12003  * @param {Object} config Configuration options
12004  */
12005 Roo.bootstrap.ComboBox = function(config){
12006     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12007     this.addEvents({
12008         /**
12009          * @event expand
12010          * Fires when the dropdown list is expanded
12011              * @param {Roo.bootstrap.ComboBox} combo This combo box
12012              */
12013         'expand' : true,
12014         /**
12015          * @event collapse
12016          * Fires when the dropdown list is collapsed
12017              * @param {Roo.bootstrap.ComboBox} combo This combo box
12018              */
12019         'collapse' : true,
12020         /**
12021          * @event beforeselect
12022          * Fires before a list item is selected. Return false to cancel the selection.
12023              * @param {Roo.bootstrap.ComboBox} combo This combo box
12024              * @param {Roo.data.Record} record The data record returned from the underlying store
12025              * @param {Number} index The index of the selected item in the dropdown list
12026              */
12027         'beforeselect' : true,
12028         /**
12029          * @event select
12030          * Fires when a list item is selected
12031              * @param {Roo.bootstrap.ComboBox} combo This combo box
12032              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12033              * @param {Number} index The index of the selected item in the dropdown list
12034              */
12035         'select' : true,
12036         /**
12037          * @event beforequery
12038          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12039          * The event object passed has these properties:
12040              * @param {Roo.bootstrap.ComboBox} combo This combo box
12041              * @param {String} query The query
12042              * @param {Boolean} forceAll true to force "all" query
12043              * @param {Boolean} cancel true to cancel the query
12044              * @param {Object} e The query event object
12045              */
12046         'beforequery': true,
12047          /**
12048          * @event add
12049          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12050              * @param {Roo.bootstrap.ComboBox} combo This combo box
12051              */
12052         'add' : true,
12053         /**
12054          * @event edit
12055          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12056              * @param {Roo.bootstrap.ComboBox} combo This combo box
12057              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12058              */
12059         'edit' : true,
12060         /**
12061          * @event remove
12062          * Fires when the remove value from the combobox array
12063              * @param {Roo.bootstrap.ComboBox} combo This combo box
12064              */
12065         'remove' : true,
12066         /**
12067          * @event afterremove
12068          * Fires when the remove value from the combobox array
12069              * @param {Roo.bootstrap.ComboBox} combo This combo box
12070              */
12071         'afterremove' : true,
12072         /**
12073          * @event specialfilter
12074          * Fires when specialfilter
12075             * @param {Roo.bootstrap.ComboBox} combo This combo box
12076             */
12077         'specialfilter' : true,
12078         /**
12079          * @event tick
12080          * Fires when tick the element
12081             * @param {Roo.bootstrap.ComboBox} combo This combo box
12082             */
12083         'tick' : true,
12084         /**
12085          * @event touchviewdisplay
12086          * Fires when touch view require special display (default is using displayField)
12087             * @param {Roo.bootstrap.ComboBox} combo This combo box
12088             * @param {Object} cfg set html .
12089             */
12090         'touchviewdisplay' : true
12091         
12092     });
12093     
12094     this.item = [];
12095     this.tickItems = [];
12096     
12097     this.selectedIndex = -1;
12098     if(this.mode == 'local'){
12099         if(config.queryDelay === undefined){
12100             this.queryDelay = 10;
12101         }
12102         if(config.minChars === undefined){
12103             this.minChars = 0;
12104         }
12105     }
12106 };
12107
12108 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12109      
12110     /**
12111      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12112      * rendering into an Roo.Editor, defaults to false)
12113      */
12114     /**
12115      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12116      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12117      */
12118     /**
12119      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12120      */
12121     /**
12122      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12123      * the dropdown list (defaults to undefined, with no header element)
12124      */
12125
12126      /**
12127      * @cfg {String/Roo.Template} tpl The template to use to render the output
12128      */
12129      
12130      /**
12131      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12132      */
12133     listWidth: undefined,
12134     /**
12135      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12136      * mode = 'remote' or 'text' if mode = 'local')
12137      */
12138     displayField: undefined,
12139     
12140     /**
12141      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12142      * mode = 'remote' or 'value' if mode = 'local'). 
12143      * Note: use of a valueField requires the user make a selection
12144      * in order for a value to be mapped.
12145      */
12146     valueField: undefined,
12147     /**
12148      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12149      */
12150     modalTitle : '',
12151     
12152     /**
12153      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12154      * field's data value (defaults to the underlying DOM element's name)
12155      */
12156     hiddenName: undefined,
12157     /**
12158      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12159      */
12160     listClass: '',
12161     /**
12162      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12163      */
12164     selectedClass: 'active',
12165     
12166     /**
12167      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12168      */
12169     shadow:'sides',
12170     /**
12171      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12172      * anchor positions (defaults to 'tl-bl')
12173      */
12174     listAlign: 'tl-bl?',
12175     /**
12176      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12177      */
12178     maxHeight: 300,
12179     /**
12180      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12181      * query specified by the allQuery config option (defaults to 'query')
12182      */
12183     triggerAction: 'query',
12184     /**
12185      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12186      * (defaults to 4, does not apply if editable = false)
12187      */
12188     minChars : 4,
12189     /**
12190      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12191      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12192      */
12193     typeAhead: false,
12194     /**
12195      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12196      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12197      */
12198     queryDelay: 500,
12199     /**
12200      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12201      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12202      */
12203     pageSize: 0,
12204     /**
12205      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12206      * when editable = true (defaults to false)
12207      */
12208     selectOnFocus:false,
12209     /**
12210      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12211      */
12212     queryParam: 'query',
12213     /**
12214      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12215      * when mode = 'remote' (defaults to 'Loading...')
12216      */
12217     loadingText: 'Loading...',
12218     /**
12219      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12220      */
12221     resizable: false,
12222     /**
12223      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12224      */
12225     handleHeight : 8,
12226     /**
12227      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12228      * traditional select (defaults to true)
12229      */
12230     editable: true,
12231     /**
12232      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12233      */
12234     allQuery: '',
12235     /**
12236      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12237      */
12238     mode: 'remote',
12239     /**
12240      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12241      * listWidth has a higher value)
12242      */
12243     minListWidth : 70,
12244     /**
12245      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12246      * allow the user to set arbitrary text into the field (defaults to false)
12247      */
12248     forceSelection:false,
12249     /**
12250      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12251      * if typeAhead = true (defaults to 250)
12252      */
12253     typeAheadDelay : 250,
12254     /**
12255      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12256      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12257      */
12258     valueNotFoundText : undefined,
12259     /**
12260      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12261      */
12262     blockFocus : false,
12263     
12264     /**
12265      * @cfg {Boolean} disableClear Disable showing of clear button.
12266      */
12267     disableClear : false,
12268     /**
12269      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12270      */
12271     alwaysQuery : false,
12272     
12273     /**
12274      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12275      */
12276     multiple : false,
12277     
12278     /**
12279      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12280      */
12281     invalidClass : "has-warning",
12282     
12283     /**
12284      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12285      */
12286     validClass : "has-success",
12287     
12288     /**
12289      * @cfg {Boolean} specialFilter (true|false) special filter default false
12290      */
12291     specialFilter : false,
12292     
12293     /**
12294      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12295      */
12296     mobileTouchView : true,
12297     
12298     /**
12299      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12300      */
12301     useNativeIOS : false,
12302     
12303     ios_options : false,
12304     
12305     //private
12306     addicon : false,
12307     editicon: false,
12308     
12309     page: 0,
12310     hasQuery: false,
12311     append: false,
12312     loadNext: false,
12313     autoFocus : true,
12314     tickable : false,
12315     btnPosition : 'right',
12316     triggerList : true,
12317     showToggleBtn : true,
12318     animate : true,
12319     emptyResultText: 'Empty',
12320     triggerText : 'Select',
12321     
12322     // element that contains real text value.. (when hidden is used..)
12323     
12324     getAutoCreate : function()
12325     {
12326         var cfg = false;
12327         
12328         /*
12329          * Render classic select for iso
12330          */
12331         
12332         if(Roo.isIOS && this.useNativeIOS){
12333             cfg = this.getAutoCreateNativeIOS();
12334             return cfg;
12335         }
12336         
12337         /*
12338          * Touch Devices
12339          */
12340         
12341         if(Roo.isTouch && this.mobileTouchView){
12342             cfg = this.getAutoCreateTouchView();
12343             return cfg;;
12344         }
12345         
12346         /*
12347          *  Normal ComboBox
12348          */
12349         if(!this.tickable){
12350             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12351             return cfg;
12352         }
12353         
12354         /*
12355          *  ComboBox with tickable selections
12356          */
12357              
12358         var align = this.labelAlign || this.parentLabelAlign();
12359         
12360         cfg = {
12361             cls : 'form-group roo-combobox-tickable' //input-group
12362         };
12363         
12364         var buttons = {
12365             tag : 'div',
12366             cls : 'tickable-buttons',
12367             cn : [
12368                 {
12369                     tag : 'button',
12370                     type : 'button',
12371                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12372                     html : this.triggerText
12373                 },
12374                 {
12375                     tag : 'button',
12376                     type : 'button',
12377                     name : 'ok',
12378                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12379                     html : 'Done'
12380                 },
12381                 {
12382                     tag : 'button',
12383                     type : 'button',
12384                     name : 'cancel',
12385                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12386                     html : 'Cancel'
12387                 }
12388             ]
12389         };
12390         
12391         if(this.editable){
12392             buttons.cn.unshift({
12393                 tag: 'input',
12394                 cls: 'roo-select2-search-field-input'
12395             });
12396         }
12397         
12398         var _this = this;
12399         
12400         Roo.each(buttons.cn, function(c){
12401             if (_this.size) {
12402                 c.cls += ' btn-' + _this.size;
12403             }
12404
12405             if (_this.disabled) {
12406                 c.disabled = true;
12407             }
12408         });
12409         
12410         var box = {
12411             tag: 'div',
12412             cn: [
12413                 {
12414                     tag: 'input',
12415                     type : 'hidden',
12416                     cls: 'form-hidden-field'
12417                 },
12418                 {
12419                     tag: 'ul',
12420                     cls: 'roo-select2-choices',
12421                     cn:[
12422                         {
12423                             tag: 'li',
12424                             cls: 'roo-select2-search-field',
12425                             cn: [
12426
12427                                 buttons
12428                             ]
12429                         }
12430                     ]
12431                 }
12432             ]
12433         };
12434         
12435         var combobox = {
12436             cls: 'roo-select2-container input-group roo-select2-container-multi',
12437             cn: [
12438                 box
12439 //                {
12440 //                    tag: 'ul',
12441 //                    cls: 'typeahead typeahead-long dropdown-menu',
12442 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12443 //                }
12444             ]
12445         };
12446         
12447         if(this.hasFeedback && !this.allowBlank){
12448             
12449             var feedback = {
12450                 tag: 'span',
12451                 cls: 'glyphicon form-control-feedback'
12452             };
12453
12454             combobox.cn.push(feedback);
12455         }
12456         
12457         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12458             
12459 //                Roo.log("left and has label");
12460             cfg.cn = [
12461                 {
12462                     tag : 'i',
12463                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12464                     tooltip : 'This field is required'
12465                 },
12466                 {
12467                     tag: 'label',
12468                     'for' :  id,
12469                     cls : 'control-label col-sm-' + this.labelWidth,
12470                     html : this.fieldLabel
12471
12472                 },
12473                 {
12474                     cls : "col-sm-" + (12 - this.labelWidth), 
12475                     cn: [
12476                         combobox
12477                     ]
12478                 }
12479
12480             ];
12481
12482             if(this.indicatorpos == 'right'){
12483                 
12484                 cfg.cn = [
12485                     {
12486                         tag: 'label',
12487                         'for' :  id,
12488                         cls : 'control-label col-sm-' + this.labelWidth,
12489                         html : this.fieldLabel
12490
12491                     },
12492                     {
12493                         tag : 'i',
12494                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12495                         tooltip : 'This field is required'
12496                     },
12497                     {
12498                         cls : "col-sm-" + (12 - this.labelWidth), 
12499                         cn: [
12500                             combobox
12501                         ]
12502                     }
12503
12504                 ];
12505             
12506             }
12507                 
12508                 
12509         } else if ( this.fieldLabel.length) {
12510 //                Roo.log(" label");
12511                  cfg.cn = [
12512                     {
12513                         tag : 'i',
12514                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12515                         tooltip : 'This field is required'
12516                     },
12517                     {
12518                         tag: 'label',
12519                         //cls : 'input-group-addon',
12520                         html : this.fieldLabel
12521                         
12522                     },
12523                     
12524                     combobox
12525                     
12526                 ];
12527                 
12528                 if(this.indicatorpos == 'right'){
12529                     
12530                     cfg.cn = [
12531                         {
12532                             tag: 'label',
12533                             //cls : 'input-group-addon',
12534                             html : this.fieldLabel
12535
12536                         },
12537                         
12538                         {
12539                             tag : 'i',
12540                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12541                             tooltip : 'This field is required'
12542                         },
12543                         
12544                         combobox
12545
12546                     ];
12547                 
12548                 }
12549
12550         } else {
12551             
12552 //                Roo.log(" no label && no align");
12553                 cfg = combobox
12554                      
12555                 
12556         }
12557          
12558         var settings=this;
12559         ['xs','sm','md','lg'].map(function(size){
12560             if (settings[size]) {
12561                 cfg.cls += ' col-' + size + '-' + settings[size];
12562             }
12563         });
12564         
12565         return cfg;
12566         
12567     },
12568     
12569     _initEventsCalled : false,
12570     
12571     // private
12572     initEvents: function()
12573     {   
12574         if (this._initEventsCalled) { // as we call render... prevent looping...
12575             return;
12576         }
12577         this._initEventsCalled = true;
12578         
12579         if (!this.store) {
12580             throw "can not find store for combo";
12581         }
12582         
12583         this.store = Roo.factory(this.store, Roo.data);
12584         
12585         // if we are building from html. then this element is so complex, that we can not really
12586         // use the rendered HTML.
12587         // so we have to trash and replace the previous code.
12588         if (Roo.XComponent.build_from_html) {
12589             
12590             // remove this element....
12591             var e = this.el.dom, k=0;
12592             while (e ) { e = e.previousSibling;  ++k;}
12593
12594             this.el.remove();
12595             
12596             this.el=false;
12597             this.rendered = false;
12598             
12599             this.render(this.parent().getChildContainer(true), k);
12600             
12601             
12602             
12603         }
12604         
12605         if(Roo.isIOS && this.useNativeIOS){
12606             this.initIOSView();
12607             return;
12608         }
12609         
12610         /*
12611          * Touch Devices
12612          */
12613         
12614         if(Roo.isTouch && this.mobileTouchView){
12615             this.initTouchView();
12616             return;
12617         }
12618         
12619         if(this.tickable){
12620             this.initTickableEvents();
12621             return;
12622         }
12623         
12624         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12625         
12626         if(this.hiddenName){
12627             
12628             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12629             
12630             this.hiddenField.dom.value =
12631                 this.hiddenValue !== undefined ? this.hiddenValue :
12632                 this.value !== undefined ? this.value : '';
12633
12634             // prevent input submission
12635             this.el.dom.removeAttribute('name');
12636             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12637              
12638              
12639         }
12640         //if(Roo.isGecko){
12641         //    this.el.dom.setAttribute('autocomplete', 'off');
12642         //}
12643         
12644         var cls = 'x-combo-list';
12645         
12646         //this.list = new Roo.Layer({
12647         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12648         //});
12649         
12650         var _this = this;
12651         
12652         (function(){
12653             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12654             _this.list.setWidth(lw);
12655         }).defer(100);
12656         
12657         this.list.on('mouseover', this.onViewOver, this);
12658         this.list.on('mousemove', this.onViewMove, this);
12659         
12660         this.list.on('scroll', this.onViewScroll, this);
12661         
12662         /*
12663         this.list.swallowEvent('mousewheel');
12664         this.assetHeight = 0;
12665
12666         if(this.title){
12667             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12668             this.assetHeight += this.header.getHeight();
12669         }
12670
12671         this.innerList = this.list.createChild({cls:cls+'-inner'});
12672         this.innerList.on('mouseover', this.onViewOver, this);
12673         this.innerList.on('mousemove', this.onViewMove, this);
12674         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12675         
12676         if(this.allowBlank && !this.pageSize && !this.disableClear){
12677             this.footer = this.list.createChild({cls:cls+'-ft'});
12678             this.pageTb = new Roo.Toolbar(this.footer);
12679            
12680         }
12681         if(this.pageSize){
12682             this.footer = this.list.createChild({cls:cls+'-ft'});
12683             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12684                     {pageSize: this.pageSize});
12685             
12686         }
12687         
12688         if (this.pageTb && this.allowBlank && !this.disableClear) {
12689             var _this = this;
12690             this.pageTb.add(new Roo.Toolbar.Fill(), {
12691                 cls: 'x-btn-icon x-btn-clear',
12692                 text: '&#160;',
12693                 handler: function()
12694                 {
12695                     _this.collapse();
12696                     _this.clearValue();
12697                     _this.onSelect(false, -1);
12698                 }
12699             });
12700         }
12701         if (this.footer) {
12702             this.assetHeight += this.footer.getHeight();
12703         }
12704         */
12705             
12706         if(!this.tpl){
12707             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12708         }
12709
12710         this.view = new Roo.View(this.list, this.tpl, {
12711             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12712         });
12713         //this.view.wrapEl.setDisplayed(false);
12714         this.view.on('click', this.onViewClick, this);
12715         
12716         
12717         
12718         this.store.on('beforeload', this.onBeforeLoad, this);
12719         this.store.on('load', this.onLoad, this);
12720         this.store.on('loadexception', this.onLoadException, this);
12721         /*
12722         if(this.resizable){
12723             this.resizer = new Roo.Resizable(this.list,  {
12724                pinned:true, handles:'se'
12725             });
12726             this.resizer.on('resize', function(r, w, h){
12727                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12728                 this.listWidth = w;
12729                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12730                 this.restrictHeight();
12731             }, this);
12732             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12733         }
12734         */
12735         if(!this.editable){
12736             this.editable = true;
12737             this.setEditable(false);
12738         }
12739         
12740         /*
12741         
12742         if (typeof(this.events.add.listeners) != 'undefined') {
12743             
12744             this.addicon = this.wrap.createChild(
12745                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12746        
12747             this.addicon.on('click', function(e) {
12748                 this.fireEvent('add', this);
12749             }, this);
12750         }
12751         if (typeof(this.events.edit.listeners) != 'undefined') {
12752             
12753             this.editicon = this.wrap.createChild(
12754                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12755             if (this.addicon) {
12756                 this.editicon.setStyle('margin-left', '40px');
12757             }
12758             this.editicon.on('click', function(e) {
12759                 
12760                 // we fire even  if inothing is selected..
12761                 this.fireEvent('edit', this, this.lastData );
12762                 
12763             }, this);
12764         }
12765         */
12766         
12767         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12768             "up" : function(e){
12769                 this.inKeyMode = true;
12770                 this.selectPrev();
12771             },
12772
12773             "down" : function(e){
12774                 if(!this.isExpanded()){
12775                     this.onTriggerClick();
12776                 }else{
12777                     this.inKeyMode = true;
12778                     this.selectNext();
12779                 }
12780             },
12781
12782             "enter" : function(e){
12783 //                this.onViewClick();
12784                 //return true;
12785                 this.collapse();
12786                 
12787                 if(this.fireEvent("specialkey", this, e)){
12788                     this.onViewClick(false);
12789                 }
12790                 
12791                 return true;
12792             },
12793
12794             "esc" : function(e){
12795                 this.collapse();
12796             },
12797
12798             "tab" : function(e){
12799                 this.collapse();
12800                 
12801                 if(this.fireEvent("specialkey", this, e)){
12802                     this.onViewClick(false);
12803                 }
12804                 
12805                 return true;
12806             },
12807
12808             scope : this,
12809
12810             doRelay : function(foo, bar, hname){
12811                 if(hname == 'down' || this.scope.isExpanded()){
12812                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12813                 }
12814                 return true;
12815             },
12816
12817             forceKeyDown: true
12818         });
12819         
12820         
12821         this.queryDelay = Math.max(this.queryDelay || 10,
12822                 this.mode == 'local' ? 10 : 250);
12823         
12824         
12825         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12826         
12827         if(this.typeAhead){
12828             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12829         }
12830         if(this.editable !== false){
12831             this.inputEl().on("keyup", this.onKeyUp, this);
12832         }
12833         if(this.forceSelection){
12834             this.inputEl().on('blur', this.doForce, this);
12835         }
12836         
12837         if(this.multiple){
12838             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12839             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12840         }
12841     },
12842     
12843     initTickableEvents: function()
12844     {   
12845         this.createList();
12846         
12847         if(this.hiddenName){
12848             
12849             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12850             
12851             this.hiddenField.dom.value =
12852                 this.hiddenValue !== undefined ? this.hiddenValue :
12853                 this.value !== undefined ? this.value : '';
12854
12855             // prevent input submission
12856             this.el.dom.removeAttribute('name');
12857             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12858              
12859              
12860         }
12861         
12862 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12863         
12864         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12865         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12866         if(this.triggerList){
12867             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12868         }
12869          
12870         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12871         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12872         
12873         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12874         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12875         
12876         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12877         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12878         
12879         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12880         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12881         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12882         
12883         this.okBtn.hide();
12884         this.cancelBtn.hide();
12885         
12886         var _this = this;
12887         
12888         (function(){
12889             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12890             _this.list.setWidth(lw);
12891         }).defer(100);
12892         
12893         this.list.on('mouseover', this.onViewOver, this);
12894         this.list.on('mousemove', this.onViewMove, this);
12895         
12896         this.list.on('scroll', this.onViewScroll, this);
12897         
12898         if(!this.tpl){
12899             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>';
12900         }
12901
12902         this.view = new Roo.View(this.list, this.tpl, {
12903             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12904         });
12905         
12906         //this.view.wrapEl.setDisplayed(false);
12907         this.view.on('click', this.onViewClick, this);
12908         
12909         
12910         
12911         this.store.on('beforeload', this.onBeforeLoad, this);
12912         this.store.on('load', this.onLoad, this);
12913         this.store.on('loadexception', this.onLoadException, this);
12914         
12915         if(this.editable){
12916             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12917                 "up" : function(e){
12918                     this.inKeyMode = true;
12919                     this.selectPrev();
12920                 },
12921
12922                 "down" : function(e){
12923                     this.inKeyMode = true;
12924                     this.selectNext();
12925                 },
12926
12927                 "enter" : function(e){
12928                     if(this.fireEvent("specialkey", this, e)){
12929                         this.onViewClick(false);
12930                     }
12931                     
12932                     return true;
12933                 },
12934
12935                 "esc" : function(e){
12936                     this.onTickableFooterButtonClick(e, false, false);
12937                 },
12938
12939                 "tab" : function(e){
12940                     this.fireEvent("specialkey", this, e);
12941                     
12942                     this.onTickableFooterButtonClick(e, false, false);
12943                     
12944                     return true;
12945                 },
12946
12947                 scope : this,
12948
12949                 doRelay : function(e, fn, key){
12950                     if(this.scope.isExpanded()){
12951                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12952                     }
12953                     return true;
12954                 },
12955
12956                 forceKeyDown: true
12957             });
12958         }
12959         
12960         this.queryDelay = Math.max(this.queryDelay || 10,
12961                 this.mode == 'local' ? 10 : 250);
12962         
12963         
12964         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12965         
12966         if(this.typeAhead){
12967             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12968         }
12969         
12970         if(this.editable !== false){
12971             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12972         }
12973         
12974     },
12975
12976     onDestroy : function(){
12977         if(this.view){
12978             this.view.setStore(null);
12979             this.view.el.removeAllListeners();
12980             this.view.el.remove();
12981             this.view.purgeListeners();
12982         }
12983         if(this.list){
12984             this.list.dom.innerHTML  = '';
12985         }
12986         
12987         if(this.store){
12988             this.store.un('beforeload', this.onBeforeLoad, this);
12989             this.store.un('load', this.onLoad, this);
12990             this.store.un('loadexception', this.onLoadException, this);
12991         }
12992         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12993     },
12994
12995     // private
12996     fireKey : function(e){
12997         if(e.isNavKeyPress() && !this.list.isVisible()){
12998             this.fireEvent("specialkey", this, e);
12999         }
13000     },
13001
13002     // private
13003     onResize: function(w, h){
13004 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13005 //        
13006 //        if(typeof w != 'number'){
13007 //            // we do not handle it!?!?
13008 //            return;
13009 //        }
13010 //        var tw = this.trigger.getWidth();
13011 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13012 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13013 //        var x = w - tw;
13014 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13015 //            
13016 //        //this.trigger.setStyle('left', x+'px');
13017 //        
13018 //        if(this.list && this.listWidth === undefined){
13019 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13020 //            this.list.setWidth(lw);
13021 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13022 //        }
13023         
13024     
13025         
13026     },
13027
13028     /**
13029      * Allow or prevent the user from directly editing the field text.  If false is passed,
13030      * the user will only be able to select from the items defined in the dropdown list.  This method
13031      * is the runtime equivalent of setting the 'editable' config option at config time.
13032      * @param {Boolean} value True to allow the user to directly edit the field text
13033      */
13034     setEditable : function(value){
13035         if(value == this.editable){
13036             return;
13037         }
13038         this.editable = value;
13039         if(!value){
13040             this.inputEl().dom.setAttribute('readOnly', true);
13041             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13042             this.inputEl().addClass('x-combo-noedit');
13043         }else{
13044             this.inputEl().dom.setAttribute('readOnly', false);
13045             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13046             this.inputEl().removeClass('x-combo-noedit');
13047         }
13048     },
13049
13050     // private
13051     
13052     onBeforeLoad : function(combo,opts){
13053         if(!this.hasFocus){
13054             return;
13055         }
13056          if (!opts.add) {
13057             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13058          }
13059         this.restrictHeight();
13060         this.selectedIndex = -1;
13061     },
13062
13063     // private
13064     onLoad : function(){
13065         
13066         this.hasQuery = false;
13067         
13068         if(!this.hasFocus){
13069             return;
13070         }
13071         
13072         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13073             this.loading.hide();
13074         }
13075              
13076         if(this.store.getCount() > 0){
13077             this.expand();
13078             this.restrictHeight();
13079             if(this.lastQuery == this.allQuery){
13080                 if(this.editable && !this.tickable){
13081                     this.inputEl().dom.select();
13082                 }
13083                 
13084                 if(
13085                     !this.selectByValue(this.value, true) &&
13086                     this.autoFocus && 
13087                     (
13088                         !this.store.lastOptions ||
13089                         typeof(this.store.lastOptions.add) == 'undefined' || 
13090                         this.store.lastOptions.add != true
13091                     )
13092                 ){
13093                     this.select(0, true);
13094                 }
13095             }else{
13096                 if(this.autoFocus){
13097                     this.selectNext();
13098                 }
13099                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13100                     this.taTask.delay(this.typeAheadDelay);
13101                 }
13102             }
13103         }else{
13104             this.onEmptyResults();
13105         }
13106         
13107         //this.el.focus();
13108     },
13109     // private
13110     onLoadException : function()
13111     {
13112         this.hasQuery = false;
13113         
13114         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13115             this.loading.hide();
13116         }
13117         
13118         if(this.tickable && this.editable){
13119             return;
13120         }
13121         
13122         this.collapse();
13123         // only causes errors at present
13124         //Roo.log(this.store.reader.jsonData);
13125         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13126             // fixme
13127             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13128         //}
13129         
13130         
13131     },
13132     // private
13133     onTypeAhead : function(){
13134         if(this.store.getCount() > 0){
13135             var r = this.store.getAt(0);
13136             var newValue = r.data[this.displayField];
13137             var len = newValue.length;
13138             var selStart = this.getRawValue().length;
13139             
13140             if(selStart != len){
13141                 this.setRawValue(newValue);
13142                 this.selectText(selStart, newValue.length);
13143             }
13144         }
13145     },
13146
13147     // private
13148     onSelect : function(record, index){
13149         
13150         if(this.fireEvent('beforeselect', this, record, index) !== false){
13151         
13152             this.setFromData(index > -1 ? record.data : false);
13153             
13154             this.collapse();
13155             this.fireEvent('select', this, record, index);
13156         }
13157     },
13158
13159     /**
13160      * Returns the currently selected field value or empty string if no value is set.
13161      * @return {String} value The selected value
13162      */
13163     getValue : function()
13164     {
13165         if(Roo.isIOS && this.useNativeIOS){
13166             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13167         }
13168         
13169         if(this.multiple){
13170             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13171         }
13172         
13173         if(this.valueField){
13174             return typeof this.value != 'undefined' ? this.value : '';
13175         }else{
13176             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13177         }
13178     },
13179     
13180     getRawValue : function()
13181     {
13182         if(Roo.isIOS && this.useNativeIOS){
13183             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13184         }
13185         
13186         var v = this.inputEl().getValue();
13187         
13188         return v;
13189     },
13190
13191     /**
13192      * Clears any text/value currently set in the field
13193      */
13194     clearValue : function(){
13195         
13196         if(this.hiddenField){
13197             this.hiddenField.dom.value = '';
13198         }
13199         this.value = '';
13200         this.setRawValue('');
13201         this.lastSelectionText = '';
13202         this.lastData = false;
13203         
13204         var close = this.closeTriggerEl();
13205         
13206         if(close){
13207             close.hide();
13208         }
13209         
13210         this.validate();
13211         
13212     },
13213
13214     /**
13215      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13216      * will be displayed in the field.  If the value does not match the data value of an existing item,
13217      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13218      * Otherwise the field will be blank (although the value will still be set).
13219      * @param {String} value The value to match
13220      */
13221     setValue : function(v)
13222     {
13223         if(Roo.isIOS && this.useNativeIOS){
13224             this.setIOSValue(v);
13225             return;
13226         }
13227         
13228         if(this.multiple){
13229             this.syncValue();
13230             return;
13231         }
13232         
13233         var text = v;
13234         if(this.valueField){
13235             var r = this.findRecord(this.valueField, v);
13236             if(r){
13237                 text = r.data[this.displayField];
13238             }else if(this.valueNotFoundText !== undefined){
13239                 text = this.valueNotFoundText;
13240             }
13241         }
13242         this.lastSelectionText = text;
13243         if(this.hiddenField){
13244             this.hiddenField.dom.value = v;
13245         }
13246         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13247         this.value = v;
13248         
13249         var close = this.closeTriggerEl();
13250         
13251         if(close){
13252             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13253         }
13254         
13255         this.validate();
13256     },
13257     /**
13258      * @property {Object} the last set data for the element
13259      */
13260     
13261     lastData : false,
13262     /**
13263      * Sets the value of the field based on a object which is related to the record format for the store.
13264      * @param {Object} value the value to set as. or false on reset?
13265      */
13266     setFromData : function(o){
13267         
13268         if(this.multiple){
13269             this.addItem(o);
13270             return;
13271         }
13272             
13273         var dv = ''; // display value
13274         var vv = ''; // value value..
13275         this.lastData = o;
13276         if (this.displayField) {
13277             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13278         } else {
13279             // this is an error condition!!!
13280             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13281         }
13282         
13283         if(this.valueField){
13284             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13285         }
13286         
13287         var close = this.closeTriggerEl();
13288         
13289         if(close){
13290             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13291         }
13292         
13293         if(this.hiddenField){
13294             this.hiddenField.dom.value = vv;
13295             
13296             this.lastSelectionText = dv;
13297             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13298             this.value = vv;
13299             return;
13300         }
13301         // no hidden field.. - we store the value in 'value', but still display
13302         // display field!!!!
13303         this.lastSelectionText = dv;
13304         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13305         this.value = vv;
13306         
13307         
13308         
13309     },
13310     // private
13311     reset : function(){
13312         // overridden so that last data is reset..
13313         
13314         if(this.multiple){
13315             this.clearItem();
13316             return;
13317         }
13318         
13319         this.setValue(this.originalValue);
13320         //this.clearInvalid();
13321         this.lastData = false;
13322         if (this.view) {
13323             this.view.clearSelections();
13324         }
13325         
13326         this.validate();
13327     },
13328     // private
13329     findRecord : function(prop, value){
13330         var record;
13331         if(this.store.getCount() > 0){
13332             this.store.each(function(r){
13333                 if(r.data[prop] == value){
13334                     record = r;
13335                     return false;
13336                 }
13337                 return true;
13338             });
13339         }
13340         return record;
13341     },
13342     
13343     getName: function()
13344     {
13345         // returns hidden if it's set..
13346         if (!this.rendered) {return ''};
13347         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13348         
13349     },
13350     // private
13351     onViewMove : function(e, t){
13352         this.inKeyMode = false;
13353     },
13354
13355     // private
13356     onViewOver : function(e, t){
13357         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13358             return;
13359         }
13360         var item = this.view.findItemFromChild(t);
13361         
13362         if(item){
13363             var index = this.view.indexOf(item);
13364             this.select(index, false);
13365         }
13366     },
13367
13368     // private
13369     onViewClick : function(view, doFocus, el, e)
13370     {
13371         var index = this.view.getSelectedIndexes()[0];
13372         
13373         var r = this.store.getAt(index);
13374         
13375         if(this.tickable){
13376             
13377             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13378                 return;
13379             }
13380             
13381             var rm = false;
13382             var _this = this;
13383             
13384             Roo.each(this.tickItems, function(v,k){
13385                 
13386                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13387                     Roo.log(v);
13388                     _this.tickItems.splice(k, 1);
13389                     
13390                     if(typeof(e) == 'undefined' && view == false){
13391                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13392                     }
13393                     
13394                     rm = true;
13395                     return;
13396                 }
13397             });
13398             
13399             if(rm){
13400                 return;
13401             }
13402             
13403             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13404                 this.tickItems.push(r.data);
13405             }
13406             
13407             if(typeof(e) == 'undefined' && view == false){
13408                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13409             }
13410                     
13411             return;
13412         }
13413         
13414         if(r){
13415             this.onSelect(r, index);
13416         }
13417         if(doFocus !== false && !this.blockFocus){
13418             this.inputEl().focus();
13419         }
13420     },
13421
13422     // private
13423     restrictHeight : function(){
13424         //this.innerList.dom.style.height = '';
13425         //var inner = this.innerList.dom;
13426         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13427         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13428         //this.list.beginUpdate();
13429         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13430         this.list.alignTo(this.inputEl(), this.listAlign);
13431         this.list.alignTo(this.inputEl(), this.listAlign);
13432         //this.list.endUpdate();
13433     },
13434
13435     // private
13436     onEmptyResults : function(){
13437         
13438         if(this.tickable && this.editable){
13439             this.restrictHeight();
13440             return;
13441         }
13442         
13443         this.collapse();
13444     },
13445
13446     /**
13447      * Returns true if the dropdown list is expanded, else false.
13448      */
13449     isExpanded : function(){
13450         return this.list.isVisible();
13451     },
13452
13453     /**
13454      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13455      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13456      * @param {String} value The data value of the item to select
13457      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13458      * selected item if it is not currently in view (defaults to true)
13459      * @return {Boolean} True if the value matched an item in the list, else false
13460      */
13461     selectByValue : function(v, scrollIntoView){
13462         if(v !== undefined && v !== null){
13463             var r = this.findRecord(this.valueField || this.displayField, v);
13464             if(r){
13465                 this.select(this.store.indexOf(r), scrollIntoView);
13466                 return true;
13467             }
13468         }
13469         return false;
13470     },
13471
13472     /**
13473      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13474      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13475      * @param {Number} index The zero-based index of the list item to select
13476      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13477      * selected item if it is not currently in view (defaults to true)
13478      */
13479     select : function(index, scrollIntoView){
13480         this.selectedIndex = index;
13481         this.view.select(index);
13482         if(scrollIntoView !== false){
13483             var el = this.view.getNode(index);
13484             /*
13485              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13486              */
13487             if(el){
13488                 this.list.scrollChildIntoView(el, false);
13489             }
13490         }
13491     },
13492
13493     // private
13494     selectNext : function(){
13495         var ct = this.store.getCount();
13496         if(ct > 0){
13497             if(this.selectedIndex == -1){
13498                 this.select(0);
13499             }else if(this.selectedIndex < ct-1){
13500                 this.select(this.selectedIndex+1);
13501             }
13502         }
13503     },
13504
13505     // private
13506     selectPrev : function(){
13507         var ct = this.store.getCount();
13508         if(ct > 0){
13509             if(this.selectedIndex == -1){
13510                 this.select(0);
13511             }else if(this.selectedIndex != 0){
13512                 this.select(this.selectedIndex-1);
13513             }
13514         }
13515     },
13516
13517     // private
13518     onKeyUp : function(e){
13519         if(this.editable !== false && !e.isSpecialKey()){
13520             this.lastKey = e.getKey();
13521             this.dqTask.delay(this.queryDelay);
13522         }
13523     },
13524
13525     // private
13526     validateBlur : function(){
13527         return !this.list || !this.list.isVisible();   
13528     },
13529
13530     // private
13531     initQuery : function(){
13532         
13533         var v = this.getRawValue();
13534         
13535         if(this.tickable && this.editable){
13536             v = this.tickableInputEl().getValue();
13537         }
13538         
13539         this.doQuery(v);
13540     },
13541
13542     // private
13543     doForce : function(){
13544         if(this.inputEl().dom.value.length > 0){
13545             this.inputEl().dom.value =
13546                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13547              
13548         }
13549     },
13550
13551     /**
13552      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13553      * query allowing the query action to be canceled if needed.
13554      * @param {String} query The SQL query to execute
13555      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13556      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13557      * saved in the current store (defaults to false)
13558      */
13559     doQuery : function(q, forceAll){
13560         
13561         if(q === undefined || q === null){
13562             q = '';
13563         }
13564         var qe = {
13565             query: q,
13566             forceAll: forceAll,
13567             combo: this,
13568             cancel:false
13569         };
13570         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13571             return false;
13572         }
13573         q = qe.query;
13574         
13575         forceAll = qe.forceAll;
13576         if(forceAll === true || (q.length >= this.minChars)){
13577             
13578             this.hasQuery = true;
13579             
13580             if(this.lastQuery != q || this.alwaysQuery){
13581                 this.lastQuery = q;
13582                 if(this.mode == 'local'){
13583                     this.selectedIndex = -1;
13584                     if(forceAll){
13585                         this.store.clearFilter();
13586                     }else{
13587                         
13588                         if(this.specialFilter){
13589                             this.fireEvent('specialfilter', this);
13590                             this.onLoad();
13591                             return;
13592                         }
13593                         
13594                         this.store.filter(this.displayField, q);
13595                     }
13596                     
13597                     this.store.fireEvent("datachanged", this.store);
13598                     
13599                     this.onLoad();
13600                     
13601                     
13602                 }else{
13603                     
13604                     this.store.baseParams[this.queryParam] = q;
13605                     
13606                     var options = {params : this.getParams(q)};
13607                     
13608                     if(this.loadNext){
13609                         options.add = true;
13610                         options.params.start = this.page * this.pageSize;
13611                     }
13612                     
13613                     this.store.load(options);
13614                     
13615                     /*
13616                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13617                      *  we should expand the list on onLoad
13618                      *  so command out it
13619                      */
13620 //                    this.expand();
13621                 }
13622             }else{
13623                 this.selectedIndex = -1;
13624                 this.onLoad();   
13625             }
13626         }
13627         
13628         this.loadNext = false;
13629     },
13630     
13631     // private
13632     getParams : function(q){
13633         var p = {};
13634         //p[this.queryParam] = q;
13635         
13636         if(this.pageSize){
13637             p.start = 0;
13638             p.limit = this.pageSize;
13639         }
13640         return p;
13641     },
13642
13643     /**
13644      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13645      */
13646     collapse : function(){
13647         if(!this.isExpanded()){
13648             return;
13649         }
13650         
13651         this.list.hide();
13652         
13653         this.hasFocus = false;
13654         
13655         if(this.tickable){
13656             this.okBtn.hide();
13657             this.cancelBtn.hide();
13658             this.trigger.show();
13659             
13660             if(this.editable){
13661                 this.tickableInputEl().dom.value = '';
13662                 this.tickableInputEl().blur();
13663             }
13664             
13665         }
13666         
13667         Roo.get(document).un('mousedown', this.collapseIf, this);
13668         Roo.get(document).un('mousewheel', this.collapseIf, this);
13669         if (!this.editable) {
13670             Roo.get(document).un('keydown', this.listKeyPress, this);
13671         }
13672         this.fireEvent('collapse', this);
13673         
13674         this.validate();
13675     },
13676
13677     // private
13678     collapseIf : function(e){
13679         var in_combo  = e.within(this.el);
13680         var in_list =  e.within(this.list);
13681         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13682         
13683         if (in_combo || in_list || is_list) {
13684             //e.stopPropagation();
13685             return;
13686         }
13687         
13688         if(this.tickable){
13689             this.onTickableFooterButtonClick(e, false, false);
13690         }
13691
13692         this.collapse();
13693         
13694     },
13695
13696     /**
13697      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13698      */
13699     expand : function(){
13700        
13701         if(this.isExpanded() || !this.hasFocus){
13702             return;
13703         }
13704         
13705         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13706         this.list.setWidth(lw);
13707         
13708         Roo.log('expand');
13709         
13710         this.list.show();
13711         
13712         this.restrictHeight();
13713         
13714         if(this.tickable){
13715             
13716             this.tickItems = Roo.apply([], this.item);
13717             
13718             this.okBtn.show();
13719             this.cancelBtn.show();
13720             this.trigger.hide();
13721             
13722             if(this.editable){
13723                 this.tickableInputEl().focus();
13724             }
13725             
13726         }
13727         
13728         Roo.get(document).on('mousedown', this.collapseIf, this);
13729         Roo.get(document).on('mousewheel', this.collapseIf, this);
13730         if (!this.editable) {
13731             Roo.get(document).on('keydown', this.listKeyPress, this);
13732         }
13733         
13734         this.fireEvent('expand', this);
13735     },
13736
13737     // private
13738     // Implements the default empty TriggerField.onTriggerClick function
13739     onTriggerClick : function(e)
13740     {
13741         Roo.log('trigger click');
13742         
13743         if(this.disabled || !this.triggerList){
13744             return;
13745         }
13746         
13747         this.page = 0;
13748         this.loadNext = false;
13749         
13750         if(this.isExpanded()){
13751             this.collapse();
13752             if (!this.blockFocus) {
13753                 this.inputEl().focus();
13754             }
13755             
13756         }else {
13757             this.hasFocus = true;
13758             if(this.triggerAction == 'all') {
13759                 this.doQuery(this.allQuery, true);
13760             } else {
13761                 this.doQuery(this.getRawValue());
13762             }
13763             if (!this.blockFocus) {
13764                 this.inputEl().focus();
13765             }
13766         }
13767     },
13768     
13769     onTickableTriggerClick : function(e)
13770     {
13771         if(this.disabled){
13772             return;
13773         }
13774         
13775         this.page = 0;
13776         this.loadNext = false;
13777         this.hasFocus = true;
13778         
13779         if(this.triggerAction == 'all') {
13780             this.doQuery(this.allQuery, true);
13781         } else {
13782             this.doQuery(this.getRawValue());
13783         }
13784     },
13785     
13786     onSearchFieldClick : function(e)
13787     {
13788         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13789             this.onTickableFooterButtonClick(e, false, false);
13790             return;
13791         }
13792         
13793         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13794             return;
13795         }
13796         
13797         this.page = 0;
13798         this.loadNext = false;
13799         this.hasFocus = true;
13800         
13801         if(this.triggerAction == 'all') {
13802             this.doQuery(this.allQuery, true);
13803         } else {
13804             this.doQuery(this.getRawValue());
13805         }
13806     },
13807     
13808     listKeyPress : function(e)
13809     {
13810         //Roo.log('listkeypress');
13811         // scroll to first matching element based on key pres..
13812         if (e.isSpecialKey()) {
13813             return false;
13814         }
13815         var k = String.fromCharCode(e.getKey()).toUpperCase();
13816         //Roo.log(k);
13817         var match  = false;
13818         var csel = this.view.getSelectedNodes();
13819         var cselitem = false;
13820         if (csel.length) {
13821             var ix = this.view.indexOf(csel[0]);
13822             cselitem  = this.store.getAt(ix);
13823             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13824                 cselitem = false;
13825             }
13826             
13827         }
13828         
13829         this.store.each(function(v) { 
13830             if (cselitem) {
13831                 // start at existing selection.
13832                 if (cselitem.id == v.id) {
13833                     cselitem = false;
13834                 }
13835                 return true;
13836             }
13837                 
13838             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13839                 match = this.store.indexOf(v);
13840                 return false;
13841             }
13842             return true;
13843         }, this);
13844         
13845         if (match === false) {
13846             return true; // no more action?
13847         }
13848         // scroll to?
13849         this.view.select(match);
13850         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13851         sn.scrollIntoView(sn.dom.parentNode, false);
13852     },
13853     
13854     onViewScroll : function(e, t){
13855         
13856         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){
13857             return;
13858         }
13859         
13860         this.hasQuery = true;
13861         
13862         this.loading = this.list.select('.loading', true).first();
13863         
13864         if(this.loading === null){
13865             this.list.createChild({
13866                 tag: 'div',
13867                 cls: 'loading roo-select2-more-results roo-select2-active',
13868                 html: 'Loading more results...'
13869             });
13870             
13871             this.loading = this.list.select('.loading', true).first();
13872             
13873             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13874             
13875             this.loading.hide();
13876         }
13877         
13878         this.loading.show();
13879         
13880         var _combo = this;
13881         
13882         this.page++;
13883         this.loadNext = true;
13884         
13885         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13886         
13887         return;
13888     },
13889     
13890     addItem : function(o)
13891     {   
13892         var dv = ''; // display value
13893         
13894         if (this.displayField) {
13895             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13896         } else {
13897             // this is an error condition!!!
13898             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13899         }
13900         
13901         if(!dv.length){
13902             return;
13903         }
13904         
13905         var choice = this.choices.createChild({
13906             tag: 'li',
13907             cls: 'roo-select2-search-choice',
13908             cn: [
13909                 {
13910                     tag: 'div',
13911                     html: dv
13912                 },
13913                 {
13914                     tag: 'a',
13915                     href: '#',
13916                     cls: 'roo-select2-search-choice-close',
13917                     tabindex: '-1'
13918                 }
13919             ]
13920             
13921         }, this.searchField);
13922         
13923         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13924         
13925         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13926         
13927         this.item.push(o);
13928         
13929         this.lastData = o;
13930         
13931         this.syncValue();
13932         
13933         this.inputEl().dom.value = '';
13934         
13935         this.validate();
13936     },
13937     
13938     onRemoveItem : function(e, _self, o)
13939     {
13940         e.preventDefault();
13941         
13942         this.lastItem = Roo.apply([], this.item);
13943         
13944         var index = this.item.indexOf(o.data) * 1;
13945         
13946         if( index < 0){
13947             Roo.log('not this item?!');
13948             return;
13949         }
13950         
13951         this.item.splice(index, 1);
13952         o.item.remove();
13953         
13954         this.syncValue();
13955         
13956         this.fireEvent('remove', this, e);
13957         
13958         this.validate();
13959         
13960     },
13961     
13962     syncValue : function()
13963     {
13964         if(!this.item.length){
13965             this.clearValue();
13966             return;
13967         }
13968             
13969         var value = [];
13970         var _this = this;
13971         Roo.each(this.item, function(i){
13972             if(_this.valueField){
13973                 value.push(i[_this.valueField]);
13974                 return;
13975             }
13976
13977             value.push(i);
13978         });
13979
13980         this.value = value.join(',');
13981
13982         if(this.hiddenField){
13983             this.hiddenField.dom.value = this.value;
13984         }
13985         
13986         this.store.fireEvent("datachanged", this.store);
13987         
13988         this.validate();
13989     },
13990     
13991     clearItem : function()
13992     {
13993         if(!this.multiple){
13994             return;
13995         }
13996         
13997         this.item = [];
13998         
13999         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14000            c.remove();
14001         });
14002         
14003         this.syncValue();
14004         
14005         this.validate();
14006         
14007         if(this.tickable && !Roo.isTouch){
14008             this.view.refresh();
14009         }
14010     },
14011     
14012     inputEl: function ()
14013     {
14014         if(Roo.isIOS && this.useNativeIOS){
14015             return this.el.select('select.roo-ios-select', true).first();
14016         }
14017         
14018         if(Roo.isTouch && this.mobileTouchView){
14019             return this.el.select('input.form-control',true).first();
14020         }
14021         
14022         if(this.tickable){
14023             return this.searchField;
14024         }
14025         
14026         return this.el.select('input.form-control',true).first();
14027     },
14028     
14029     onTickableFooterButtonClick : function(e, btn, el)
14030     {
14031         e.preventDefault();
14032         
14033         this.lastItem = Roo.apply([], this.item);
14034         
14035         if(btn && btn.name == 'cancel'){
14036             this.tickItems = Roo.apply([], this.item);
14037             this.collapse();
14038             return;
14039         }
14040         
14041         this.clearItem();
14042         
14043         var _this = this;
14044         
14045         Roo.each(this.tickItems, function(o){
14046             _this.addItem(o);
14047         });
14048         
14049         this.collapse();
14050         
14051     },
14052     
14053     validate : function()
14054     {
14055         var v = this.getRawValue();
14056         
14057         if(this.multiple){
14058             v = this.getValue();
14059         }
14060         
14061         if(this.disabled || this.allowBlank || v.length){
14062             this.markValid();
14063             return true;
14064         }
14065         
14066         this.markInvalid();
14067         return false;
14068     },
14069     
14070     tickableInputEl : function()
14071     {
14072         if(!this.tickable || !this.editable){
14073             return this.inputEl();
14074         }
14075         
14076         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14077     },
14078     
14079     
14080     getAutoCreateTouchView : function()
14081     {
14082         var id = Roo.id();
14083         
14084         var cfg = {
14085             cls: 'form-group' //input-group
14086         };
14087         
14088         var input =  {
14089             tag: 'input',
14090             id : id,
14091             type : this.inputType,
14092             cls : 'form-control x-combo-noedit',
14093             autocomplete: 'new-password',
14094             placeholder : this.placeholder || '',
14095             readonly : true
14096         };
14097         
14098         if (this.name) {
14099             input.name = this.name;
14100         }
14101         
14102         if (this.size) {
14103             input.cls += ' input-' + this.size;
14104         }
14105         
14106         if (this.disabled) {
14107             input.disabled = true;
14108         }
14109         
14110         var inputblock = {
14111             cls : '',
14112             cn : [
14113                 input
14114             ]
14115         };
14116         
14117         if(this.before){
14118             inputblock.cls += ' input-group';
14119             
14120             inputblock.cn.unshift({
14121                 tag :'span',
14122                 cls : 'input-group-addon',
14123                 html : this.before
14124             });
14125         }
14126         
14127         if(this.removable && !this.multiple){
14128             inputblock.cls += ' roo-removable';
14129             
14130             inputblock.cn.push({
14131                 tag: 'button',
14132                 html : 'x',
14133                 cls : 'roo-combo-removable-btn close'
14134             });
14135         }
14136
14137         if(this.hasFeedback && !this.allowBlank){
14138             
14139             inputblock.cls += ' has-feedback';
14140             
14141             inputblock.cn.push({
14142                 tag: 'span',
14143                 cls: 'glyphicon form-control-feedback'
14144             });
14145             
14146         }
14147         
14148         if (this.after) {
14149             
14150             inputblock.cls += (this.before) ? '' : ' input-group';
14151             
14152             inputblock.cn.push({
14153                 tag :'span',
14154                 cls : 'input-group-addon',
14155                 html : this.after
14156             });
14157         }
14158
14159         var box = {
14160             tag: 'div',
14161             cn: [
14162                 {
14163                     tag: 'input',
14164                     type : 'hidden',
14165                     cls: 'form-hidden-field'
14166                 },
14167                 inputblock
14168             ]
14169             
14170         };
14171         
14172         if(this.multiple){
14173             box = {
14174                 tag: 'div',
14175                 cn: [
14176                     {
14177                         tag: 'input',
14178                         type : 'hidden',
14179                         cls: 'form-hidden-field'
14180                     },
14181                     {
14182                         tag: 'ul',
14183                         cls: 'roo-select2-choices',
14184                         cn:[
14185                             {
14186                                 tag: 'li',
14187                                 cls: 'roo-select2-search-field',
14188                                 cn: [
14189
14190                                     inputblock
14191                                 ]
14192                             }
14193                         ]
14194                     }
14195                 ]
14196             }
14197         };
14198         
14199         var combobox = {
14200             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14201             cn: [
14202                 box
14203             ]
14204         };
14205         
14206         if(!this.multiple && this.showToggleBtn){
14207             
14208             var caret = {
14209                         tag: 'span',
14210                         cls: 'caret'
14211             };
14212             
14213             if (this.caret != false) {
14214                 caret = {
14215                      tag: 'i',
14216                      cls: 'fa fa-' + this.caret
14217                 };
14218                 
14219             }
14220             
14221             combobox.cn.push({
14222                 tag :'span',
14223                 cls : 'input-group-addon btn dropdown-toggle',
14224                 cn : [
14225                     caret,
14226                     {
14227                         tag: 'span',
14228                         cls: 'combobox-clear',
14229                         cn  : [
14230                             {
14231                                 tag : 'i',
14232                                 cls: 'icon-remove'
14233                             }
14234                         ]
14235                     }
14236                 ]
14237
14238             })
14239         }
14240         
14241         if(this.multiple){
14242             combobox.cls += ' roo-select2-container-multi';
14243         }
14244         
14245         var align = this.labelAlign || this.parentLabelAlign();
14246         
14247         cfg.cn = combobox;
14248         
14249         if(this.fieldLabel.length && this.labelWidth){
14250             
14251             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14252             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14253             
14254             cfg.cn = [
14255                 {
14256                    tag : 'i',
14257                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14258                    tooltip : 'This field is required'
14259                 },
14260                 {
14261                     tag: 'label',
14262                     cls : 'control-label ' + lw,
14263                     html : this.fieldLabel
14264
14265                 },
14266                 {
14267                     cls : cw, 
14268                     cn: [
14269                         combobox
14270                     ]
14271                 }
14272             ];
14273             
14274             if(this.indicatorpos == 'right'){
14275                 cfg.cn = [
14276                     {
14277                         tag: 'label',
14278                         cls : 'control-label ' + lw,
14279                         html : this.fieldLabel
14280
14281                     },
14282                     {
14283                        tag : 'i',
14284                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14285                        tooltip : 'This field is required'
14286                     },
14287                     {
14288                         cls : cw, 
14289                         cn: [
14290                             combobox
14291                         ]
14292                     }
14293                 ];
14294             }
14295         }
14296         
14297         var settings = this;
14298         
14299         ['xs','sm','md','lg'].map(function(size){
14300             if (settings[size]) {
14301                 cfg.cls += ' col-' + size + '-' + settings[size];
14302             }
14303         });
14304         
14305         return cfg;
14306     },
14307     
14308     initTouchView : function()
14309     {
14310         this.renderTouchView();
14311         
14312         this.touchViewEl.on('scroll', function(){
14313             this.el.dom.scrollTop = 0;
14314         }, this);
14315         
14316         this.originalValue = this.getValue();
14317         
14318         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14319         
14320         this.inputEl().on("click", this.showTouchView, this);
14321         if (this.triggerEl) {
14322             this.triggerEl.on("click", this.showTouchView, this);
14323         }
14324         
14325         
14326         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14327         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14328         
14329         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14330         
14331         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14332         this.store.on('load', this.onTouchViewLoad, this);
14333         this.store.on('loadexception', this.onTouchViewLoadException, this);
14334         
14335         if(this.hiddenName){
14336             
14337             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14338             
14339             this.hiddenField.dom.value =
14340                 this.hiddenValue !== undefined ? this.hiddenValue :
14341                 this.value !== undefined ? this.value : '';
14342         
14343             this.el.dom.removeAttribute('name');
14344             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14345         }
14346         
14347         if(this.multiple){
14348             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14349             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14350         }
14351         
14352         if(this.removable && !this.multiple){
14353             var close = this.closeTriggerEl();
14354             if(close){
14355                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14356                 close.on('click', this.removeBtnClick, this, close);
14357             }
14358         }
14359         /*
14360          * fix the bug in Safari iOS8
14361          */
14362         this.inputEl().on("focus", function(e){
14363             document.activeElement.blur();
14364         }, this);
14365         
14366         return;
14367         
14368         
14369     },
14370     
14371     renderTouchView : function()
14372     {
14373         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14374         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14375         
14376         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14377         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14378         
14379         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14380         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14381         this.touchViewBodyEl.setStyle('overflow', 'auto');
14382         
14383         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14384         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14385         
14386         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14387         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14388         
14389     },
14390     
14391     showTouchView : function()
14392     {
14393         if(this.disabled){
14394             return;
14395         }
14396         
14397         this.touchViewHeaderEl.hide();
14398
14399         if(this.modalTitle.length){
14400             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14401             this.touchViewHeaderEl.show();
14402         }
14403
14404         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14405         this.touchViewEl.show();
14406
14407         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14408         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14409                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14410
14411         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14412
14413         if(this.modalTitle.length){
14414             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14415         }
14416         
14417         this.touchViewBodyEl.setHeight(bodyHeight);
14418
14419         if(this.animate){
14420             var _this = this;
14421             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14422         }else{
14423             this.touchViewEl.addClass('in');
14424         }
14425
14426         this.doTouchViewQuery();
14427         
14428     },
14429     
14430     hideTouchView : function()
14431     {
14432         this.touchViewEl.removeClass('in');
14433
14434         if(this.animate){
14435             var _this = this;
14436             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14437         }else{
14438             this.touchViewEl.setStyle('display', 'none');
14439         }
14440         
14441     },
14442     
14443     setTouchViewValue : function()
14444     {
14445         if(this.multiple){
14446             this.clearItem();
14447         
14448             var _this = this;
14449
14450             Roo.each(this.tickItems, function(o){
14451                 this.addItem(o);
14452             }, this);
14453         }
14454         
14455         this.hideTouchView();
14456     },
14457     
14458     doTouchViewQuery : function()
14459     {
14460         var qe = {
14461             query: '',
14462             forceAll: true,
14463             combo: this,
14464             cancel:false
14465         };
14466         
14467         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14468             return false;
14469         }
14470         
14471         if(!this.alwaysQuery || this.mode == 'local'){
14472             this.onTouchViewLoad();
14473             return;
14474         }
14475         
14476         this.store.load();
14477     },
14478     
14479     onTouchViewBeforeLoad : function(combo,opts)
14480     {
14481         return;
14482     },
14483
14484     // private
14485     onTouchViewLoad : function()
14486     {
14487         if(this.store.getCount() < 1){
14488             this.onTouchViewEmptyResults();
14489             return;
14490         }
14491         
14492         this.clearTouchView();
14493         
14494         var rawValue = this.getRawValue();
14495         
14496         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14497         
14498         this.tickItems = [];
14499         
14500         this.store.data.each(function(d, rowIndex){
14501             var row = this.touchViewListGroup.createChild(template);
14502             
14503             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14504                 row.addClass(d.data.cls);
14505             }
14506             
14507             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14508                 var cfg = {
14509                     data : d.data,
14510                     html : d.data[this.displayField]
14511                 };
14512                 
14513                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14514                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14515                 }
14516             }
14517             row.removeClass('selected');
14518             if(!this.multiple && this.valueField &&
14519                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14520             {
14521                 // radio buttons..
14522                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14523                 row.addClass('selected');
14524             }
14525             
14526             if(this.multiple && this.valueField &&
14527                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14528             {
14529                 
14530                 // checkboxes...
14531                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14532                 this.tickItems.push(d.data);
14533             }
14534             
14535             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14536             
14537         }, this);
14538         
14539         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14540         
14541         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14542
14543         if(this.modalTitle.length){
14544             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14545         }
14546
14547         var listHeight = this.touchViewListGroup.getHeight();
14548         
14549         var _this = this;
14550         
14551         if(firstChecked && listHeight > bodyHeight){
14552             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14553         }
14554         
14555     },
14556     
14557     onTouchViewLoadException : function()
14558     {
14559         this.hideTouchView();
14560     },
14561     
14562     onTouchViewEmptyResults : function()
14563     {
14564         this.clearTouchView();
14565         
14566         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14567         
14568         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14569         
14570     },
14571     
14572     clearTouchView : function()
14573     {
14574         this.touchViewListGroup.dom.innerHTML = '';
14575     },
14576     
14577     onTouchViewClick : function(e, el, o)
14578     {
14579         e.preventDefault();
14580         
14581         var row = o.row;
14582         var rowIndex = o.rowIndex;
14583         
14584         var r = this.store.getAt(rowIndex);
14585         
14586         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14587             
14588             if(!this.multiple){
14589                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14590                     c.dom.removeAttribute('checked');
14591                 }, this);
14592
14593                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14594
14595                 this.setFromData(r.data);
14596
14597                 var close = this.closeTriggerEl();
14598
14599                 if(close){
14600                     close.show();
14601                 }
14602
14603                 this.hideTouchView();
14604
14605                 this.fireEvent('select', this, r, rowIndex);
14606
14607                 return;
14608             }
14609
14610             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14611                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14612                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14613                 return;
14614             }
14615
14616             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14617             this.addItem(r.data);
14618             this.tickItems.push(r.data);
14619         }
14620     },
14621     
14622     getAutoCreateNativeIOS : function()
14623     {
14624         var cfg = {
14625             cls: 'form-group' //input-group,
14626         };
14627         
14628         var combobox =  {
14629             tag: 'select',
14630             cls : 'roo-ios-select'
14631         };
14632         
14633         if (this.name) {
14634             combobox.name = this.name;
14635         }
14636         
14637         if (this.disabled) {
14638             combobox.disabled = true;
14639         }
14640         
14641         var settings = this;
14642         
14643         ['xs','sm','md','lg'].map(function(size){
14644             if (settings[size]) {
14645                 cfg.cls += ' col-' + size + '-' + settings[size];
14646             }
14647         });
14648         
14649         cfg.cn = combobox;
14650         
14651         return cfg;
14652         
14653     },
14654     
14655     initIOSView : function()
14656     {
14657         this.store.on('load', this.onIOSViewLoad, this);
14658         
14659         return;
14660     },
14661     
14662     onIOSViewLoad : function()
14663     {
14664         if(this.store.getCount() < 1){
14665             return;
14666         }
14667         
14668         this.clearIOSView();
14669         
14670         if(this.allowBlank) {
14671             
14672             var default_text = '-- SELECT --';
14673             
14674             var opt = this.inputEl().createChild({
14675                 tag: 'option',
14676                 value : 0,
14677                 html : default_text
14678             });
14679             
14680             var o = {};
14681             o[this.valueField] = 0;
14682             o[this.displayField] = default_text;
14683             
14684             this.ios_options.push({
14685                 data : o,
14686                 el : opt
14687             });
14688             
14689         }
14690         
14691         this.store.data.each(function(d, rowIndex){
14692             
14693             var html = '';
14694             
14695             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14696                 html = d.data[this.displayField];
14697             }
14698             
14699             var value = '';
14700             
14701             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14702                 value = d.data[this.valueField];
14703             }
14704             
14705             var option = {
14706                 tag: 'option',
14707                 value : value,
14708                 html : html
14709             };
14710             
14711             if(this.value == d.data[this.valueField]){
14712                 option['selected'] = true;
14713             }
14714             
14715             var opt = this.inputEl().createChild(option);
14716             
14717             this.ios_options.push({
14718                 data : d.data,
14719                 el : opt
14720             });
14721             
14722         }, this);
14723         
14724         this.inputEl().on('change', function(){
14725            this.fireEvent('select', this);
14726         }, this);
14727         
14728     },
14729     
14730     clearIOSView: function()
14731     {
14732         this.inputEl().dom.innerHTML = '';
14733         
14734         this.ios_options = [];
14735     },
14736     
14737     setIOSValue: function(v)
14738     {
14739         this.value = v;
14740         
14741         if(!this.ios_options){
14742             return;
14743         }
14744         
14745         Roo.each(this.ios_options, function(opts){
14746            
14747            opts.el.dom.removeAttribute('selected');
14748            
14749            if(opts.data[this.valueField] != v){
14750                return;
14751            }
14752            
14753            opts.el.dom.setAttribute('selected', true);
14754            
14755         }, this);
14756     }
14757
14758     /** 
14759     * @cfg {Boolean} grow 
14760     * @hide 
14761     */
14762     /** 
14763     * @cfg {Number} growMin 
14764     * @hide 
14765     */
14766     /** 
14767     * @cfg {Number} growMax 
14768     * @hide 
14769     */
14770     /**
14771      * @hide
14772      * @method autoSize
14773      */
14774 });
14775
14776 Roo.apply(Roo.bootstrap.ComboBox,  {
14777     
14778     header : {
14779         tag: 'div',
14780         cls: 'modal-header',
14781         cn: [
14782             {
14783                 tag: 'h4',
14784                 cls: 'modal-title'
14785             }
14786         ]
14787     },
14788     
14789     body : {
14790         tag: 'div',
14791         cls: 'modal-body',
14792         cn: [
14793             {
14794                 tag: 'ul',
14795                 cls: 'list-group'
14796             }
14797         ]
14798     },
14799     
14800     listItemRadio : {
14801         tag: 'li',
14802         cls: 'list-group-item',
14803         cn: [
14804             {
14805                 tag: 'span',
14806                 cls: 'roo-combobox-list-group-item-value'
14807             },
14808             {
14809                 tag: 'div',
14810                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14811                 cn: [
14812                     {
14813                         tag: 'input',
14814                         type: 'radio'
14815                     },
14816                     {
14817                         tag: 'label'
14818                     }
14819                 ]
14820             }
14821         ]
14822     },
14823     
14824     listItemCheckbox : {
14825         tag: 'li',
14826         cls: 'list-group-item',
14827         cn: [
14828             {
14829                 tag: 'span',
14830                 cls: 'roo-combobox-list-group-item-value'
14831             },
14832             {
14833                 tag: 'div',
14834                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14835                 cn: [
14836                     {
14837                         tag: 'input',
14838                         type: 'checkbox'
14839                     },
14840                     {
14841                         tag: 'label'
14842                     }
14843                 ]
14844             }
14845         ]
14846     },
14847     
14848     emptyResult : {
14849         tag: 'div',
14850         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14851     },
14852     
14853     footer : {
14854         tag: 'div',
14855         cls: 'modal-footer',
14856         cn: [
14857             {
14858                 tag: 'div',
14859                 cls: 'row',
14860                 cn: [
14861                     {
14862                         tag: 'div',
14863                         cls: 'col-xs-6 text-left',
14864                         cn: {
14865                             tag: 'button',
14866                             cls: 'btn btn-danger roo-touch-view-cancel',
14867                             html: 'Cancel'
14868                         }
14869                     },
14870                     {
14871                         tag: 'div',
14872                         cls: 'col-xs-6 text-right',
14873                         cn: {
14874                             tag: 'button',
14875                             cls: 'btn btn-success roo-touch-view-ok',
14876                             html: 'OK'
14877                         }
14878                     }
14879                 ]
14880             }
14881         ]
14882         
14883     }
14884 });
14885
14886 Roo.apply(Roo.bootstrap.ComboBox,  {
14887     
14888     touchViewTemplate : {
14889         tag: 'div',
14890         cls: 'modal fade roo-combobox-touch-view',
14891         cn: [
14892             {
14893                 tag: 'div',
14894                 cls: 'modal-dialog',
14895                 style : 'position:fixed', // we have to fix position....
14896                 cn: [
14897                     {
14898                         tag: 'div',
14899                         cls: 'modal-content',
14900                         cn: [
14901                             Roo.bootstrap.ComboBox.header,
14902                             Roo.bootstrap.ComboBox.body,
14903                             Roo.bootstrap.ComboBox.footer
14904                         ]
14905                     }
14906                 ]
14907             }
14908         ]
14909     }
14910 });/*
14911  * Based on:
14912  * Ext JS Library 1.1.1
14913  * Copyright(c) 2006-2007, Ext JS, LLC.
14914  *
14915  * Originally Released Under LGPL - original licence link has changed is not relivant.
14916  *
14917  * Fork - LGPL
14918  * <script type="text/javascript">
14919  */
14920
14921 /**
14922  * @class Roo.View
14923  * @extends Roo.util.Observable
14924  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14925  * This class also supports single and multi selection modes. <br>
14926  * Create a data model bound view:
14927  <pre><code>
14928  var store = new Roo.data.Store(...);
14929
14930  var view = new Roo.View({
14931     el : "my-element",
14932     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14933  
14934     singleSelect: true,
14935     selectedClass: "ydataview-selected",
14936     store: store
14937  });
14938
14939  // listen for node click?
14940  view.on("click", function(vw, index, node, e){
14941  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14942  });
14943
14944  // load XML data
14945  dataModel.load("foobar.xml");
14946  </code></pre>
14947  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14948  * <br><br>
14949  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14950  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14951  * 
14952  * Note: old style constructor is still suported (container, template, config)
14953  * 
14954  * @constructor
14955  * Create a new View
14956  * @param {Object} config The config object
14957  * 
14958  */
14959 Roo.View = function(config, depreciated_tpl, depreciated_config){
14960     
14961     this.parent = false;
14962     
14963     if (typeof(depreciated_tpl) == 'undefined') {
14964         // new way.. - universal constructor.
14965         Roo.apply(this, config);
14966         this.el  = Roo.get(this.el);
14967     } else {
14968         // old format..
14969         this.el  = Roo.get(config);
14970         this.tpl = depreciated_tpl;
14971         Roo.apply(this, depreciated_config);
14972     }
14973     this.wrapEl  = this.el.wrap().wrap();
14974     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14975     
14976     
14977     if(typeof(this.tpl) == "string"){
14978         this.tpl = new Roo.Template(this.tpl);
14979     } else {
14980         // support xtype ctors..
14981         this.tpl = new Roo.factory(this.tpl, Roo);
14982     }
14983     
14984     
14985     this.tpl.compile();
14986     
14987     /** @private */
14988     this.addEvents({
14989         /**
14990          * @event beforeclick
14991          * Fires before a click is processed. Returns false to cancel the default action.
14992          * @param {Roo.View} this
14993          * @param {Number} index The index of the target node
14994          * @param {HTMLElement} node The target node
14995          * @param {Roo.EventObject} e The raw event object
14996          */
14997             "beforeclick" : true,
14998         /**
14999          * @event click
15000          * Fires when a template node is clicked.
15001          * @param {Roo.View} this
15002          * @param {Number} index The index of the target node
15003          * @param {HTMLElement} node The target node
15004          * @param {Roo.EventObject} e The raw event object
15005          */
15006             "click" : true,
15007         /**
15008          * @event dblclick
15009          * Fires when a template node is double clicked.
15010          * @param {Roo.View} this
15011          * @param {Number} index The index of the target node
15012          * @param {HTMLElement} node The target node
15013          * @param {Roo.EventObject} e The raw event object
15014          */
15015             "dblclick" : true,
15016         /**
15017          * @event contextmenu
15018          * Fires when a template node is right clicked.
15019          * @param {Roo.View} this
15020          * @param {Number} index The index of the target node
15021          * @param {HTMLElement} node The target node
15022          * @param {Roo.EventObject} e The raw event object
15023          */
15024             "contextmenu" : true,
15025         /**
15026          * @event selectionchange
15027          * Fires when the selected nodes change.
15028          * @param {Roo.View} this
15029          * @param {Array} selections Array of the selected nodes
15030          */
15031             "selectionchange" : true,
15032     
15033         /**
15034          * @event beforeselect
15035          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15036          * @param {Roo.View} this
15037          * @param {HTMLElement} node The node to be selected
15038          * @param {Array} selections Array of currently selected nodes
15039          */
15040             "beforeselect" : true,
15041         /**
15042          * @event preparedata
15043          * Fires on every row to render, to allow you to change the data.
15044          * @param {Roo.View} this
15045          * @param {Object} data to be rendered (change this)
15046          */
15047           "preparedata" : true
15048           
15049           
15050         });
15051
15052
15053
15054     this.el.on({
15055         "click": this.onClick,
15056         "dblclick": this.onDblClick,
15057         "contextmenu": this.onContextMenu,
15058         scope:this
15059     });
15060
15061     this.selections = [];
15062     this.nodes = [];
15063     this.cmp = new Roo.CompositeElementLite([]);
15064     if(this.store){
15065         this.store = Roo.factory(this.store, Roo.data);
15066         this.setStore(this.store, true);
15067     }
15068     
15069     if ( this.footer && this.footer.xtype) {
15070            
15071          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15072         
15073         this.footer.dataSource = this.store;
15074         this.footer.container = fctr;
15075         this.footer = Roo.factory(this.footer, Roo);
15076         fctr.insertFirst(this.el);
15077         
15078         // this is a bit insane - as the paging toolbar seems to detach the el..
15079 //        dom.parentNode.parentNode.parentNode
15080          // they get detached?
15081     }
15082     
15083     
15084     Roo.View.superclass.constructor.call(this);
15085     
15086     
15087 };
15088
15089 Roo.extend(Roo.View, Roo.util.Observable, {
15090     
15091      /**
15092      * @cfg {Roo.data.Store} store Data store to load data from.
15093      */
15094     store : false,
15095     
15096     /**
15097      * @cfg {String|Roo.Element} el The container element.
15098      */
15099     el : '',
15100     
15101     /**
15102      * @cfg {String|Roo.Template} tpl The template used by this View 
15103      */
15104     tpl : false,
15105     /**
15106      * @cfg {String} dataName the named area of the template to use as the data area
15107      *                          Works with domtemplates roo-name="name"
15108      */
15109     dataName: false,
15110     /**
15111      * @cfg {String} selectedClass The css class to add to selected nodes
15112      */
15113     selectedClass : "x-view-selected",
15114      /**
15115      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15116      */
15117     emptyText : "",
15118     
15119     /**
15120      * @cfg {String} text to display on mask (default Loading)
15121      */
15122     mask : false,
15123     /**
15124      * @cfg {Boolean} multiSelect Allow multiple selection
15125      */
15126     multiSelect : false,
15127     /**
15128      * @cfg {Boolean} singleSelect Allow single selection
15129      */
15130     singleSelect:  false,
15131     
15132     /**
15133      * @cfg {Boolean} toggleSelect - selecting 
15134      */
15135     toggleSelect : false,
15136     
15137     /**
15138      * @cfg {Boolean} tickable - selecting 
15139      */
15140     tickable : false,
15141     
15142     /**
15143      * Returns the element this view is bound to.
15144      * @return {Roo.Element}
15145      */
15146     getEl : function(){
15147         return this.wrapEl;
15148     },
15149     
15150     
15151
15152     /**
15153      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15154      */
15155     refresh : function(){
15156         //Roo.log('refresh');
15157         var t = this.tpl;
15158         
15159         // if we are using something like 'domtemplate', then
15160         // the what gets used is:
15161         // t.applySubtemplate(NAME, data, wrapping data..)
15162         // the outer template then get' applied with
15163         //     the store 'extra data'
15164         // and the body get's added to the
15165         //      roo-name="data" node?
15166         //      <span class='roo-tpl-{name}'></span> ?????
15167         
15168         
15169         
15170         this.clearSelections();
15171         this.el.update("");
15172         var html = [];
15173         var records = this.store.getRange();
15174         if(records.length < 1) {
15175             
15176             // is this valid??  = should it render a template??
15177             
15178             this.el.update(this.emptyText);
15179             return;
15180         }
15181         var el = this.el;
15182         if (this.dataName) {
15183             this.el.update(t.apply(this.store.meta)); //????
15184             el = this.el.child('.roo-tpl-' + this.dataName);
15185         }
15186         
15187         for(var i = 0, len = records.length; i < len; i++){
15188             var data = this.prepareData(records[i].data, i, records[i]);
15189             this.fireEvent("preparedata", this, data, i, records[i]);
15190             
15191             var d = Roo.apply({}, data);
15192             
15193             if(this.tickable){
15194                 Roo.apply(d, {'roo-id' : Roo.id()});
15195                 
15196                 var _this = this;
15197             
15198                 Roo.each(this.parent.item, function(item){
15199                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15200                         return;
15201                     }
15202                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15203                 });
15204             }
15205             
15206             html[html.length] = Roo.util.Format.trim(
15207                 this.dataName ?
15208                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15209                     t.apply(d)
15210             );
15211         }
15212         
15213         
15214         
15215         el.update(html.join(""));
15216         this.nodes = el.dom.childNodes;
15217         this.updateIndexes(0);
15218     },
15219     
15220
15221     /**
15222      * Function to override to reformat the data that is sent to
15223      * the template for each node.
15224      * DEPRICATED - use the preparedata event handler.
15225      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15226      * a JSON object for an UpdateManager bound view).
15227      */
15228     prepareData : function(data, index, record)
15229     {
15230         this.fireEvent("preparedata", this, data, index, record);
15231         return data;
15232     },
15233
15234     onUpdate : function(ds, record){
15235         // Roo.log('on update');   
15236         this.clearSelections();
15237         var index = this.store.indexOf(record);
15238         var n = this.nodes[index];
15239         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15240         n.parentNode.removeChild(n);
15241         this.updateIndexes(index, index);
15242     },
15243
15244     
15245     
15246 // --------- FIXME     
15247     onAdd : function(ds, records, index)
15248     {
15249         //Roo.log(['on Add', ds, records, index] );        
15250         this.clearSelections();
15251         if(this.nodes.length == 0){
15252             this.refresh();
15253             return;
15254         }
15255         var n = this.nodes[index];
15256         for(var i = 0, len = records.length; i < len; i++){
15257             var d = this.prepareData(records[i].data, i, records[i]);
15258             if(n){
15259                 this.tpl.insertBefore(n, d);
15260             }else{
15261                 
15262                 this.tpl.append(this.el, d);
15263             }
15264         }
15265         this.updateIndexes(index);
15266     },
15267
15268     onRemove : function(ds, record, index){
15269        // Roo.log('onRemove');
15270         this.clearSelections();
15271         var el = this.dataName  ?
15272             this.el.child('.roo-tpl-' + this.dataName) :
15273             this.el; 
15274         
15275         el.dom.removeChild(this.nodes[index]);
15276         this.updateIndexes(index);
15277     },
15278
15279     /**
15280      * Refresh an individual node.
15281      * @param {Number} index
15282      */
15283     refreshNode : function(index){
15284         this.onUpdate(this.store, this.store.getAt(index));
15285     },
15286
15287     updateIndexes : function(startIndex, endIndex){
15288         var ns = this.nodes;
15289         startIndex = startIndex || 0;
15290         endIndex = endIndex || ns.length - 1;
15291         for(var i = startIndex; i <= endIndex; i++){
15292             ns[i].nodeIndex = i;
15293         }
15294     },
15295
15296     /**
15297      * Changes the data store this view uses and refresh the view.
15298      * @param {Store} store
15299      */
15300     setStore : function(store, initial){
15301         if(!initial && this.store){
15302             this.store.un("datachanged", this.refresh);
15303             this.store.un("add", this.onAdd);
15304             this.store.un("remove", this.onRemove);
15305             this.store.un("update", this.onUpdate);
15306             this.store.un("clear", this.refresh);
15307             this.store.un("beforeload", this.onBeforeLoad);
15308             this.store.un("load", this.onLoad);
15309             this.store.un("loadexception", this.onLoad);
15310         }
15311         if(store){
15312           
15313             store.on("datachanged", this.refresh, this);
15314             store.on("add", this.onAdd, this);
15315             store.on("remove", this.onRemove, this);
15316             store.on("update", this.onUpdate, this);
15317             store.on("clear", this.refresh, this);
15318             store.on("beforeload", this.onBeforeLoad, this);
15319             store.on("load", this.onLoad, this);
15320             store.on("loadexception", this.onLoad, this);
15321         }
15322         
15323         if(store){
15324             this.refresh();
15325         }
15326     },
15327     /**
15328      * onbeforeLoad - masks the loading area.
15329      *
15330      */
15331     onBeforeLoad : function(store,opts)
15332     {
15333          //Roo.log('onBeforeLoad');   
15334         if (!opts.add) {
15335             this.el.update("");
15336         }
15337         this.el.mask(this.mask ? this.mask : "Loading" ); 
15338     },
15339     onLoad : function ()
15340     {
15341         this.el.unmask();
15342     },
15343     
15344
15345     /**
15346      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15347      * @param {HTMLElement} node
15348      * @return {HTMLElement} The template node
15349      */
15350     findItemFromChild : function(node){
15351         var el = this.dataName  ?
15352             this.el.child('.roo-tpl-' + this.dataName,true) :
15353             this.el.dom; 
15354         
15355         if(!node || node.parentNode == el){
15356                     return node;
15357             }
15358             var p = node.parentNode;
15359             while(p && p != el){
15360             if(p.parentNode == el){
15361                 return p;
15362             }
15363             p = p.parentNode;
15364         }
15365             return null;
15366     },
15367
15368     /** @ignore */
15369     onClick : function(e){
15370         var item = this.findItemFromChild(e.getTarget());
15371         if(item){
15372             var index = this.indexOf(item);
15373             if(this.onItemClick(item, index, e) !== false){
15374                 this.fireEvent("click", this, index, item, e);
15375             }
15376         }else{
15377             this.clearSelections();
15378         }
15379     },
15380
15381     /** @ignore */
15382     onContextMenu : function(e){
15383         var item = this.findItemFromChild(e.getTarget());
15384         if(item){
15385             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15386         }
15387     },
15388
15389     /** @ignore */
15390     onDblClick : function(e){
15391         var item = this.findItemFromChild(e.getTarget());
15392         if(item){
15393             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15394         }
15395     },
15396
15397     onItemClick : function(item, index, e)
15398     {
15399         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15400             return false;
15401         }
15402         if (this.toggleSelect) {
15403             var m = this.isSelected(item) ? 'unselect' : 'select';
15404             //Roo.log(m);
15405             var _t = this;
15406             _t[m](item, true, false);
15407             return true;
15408         }
15409         if(this.multiSelect || this.singleSelect){
15410             if(this.multiSelect && e.shiftKey && this.lastSelection){
15411                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15412             }else{
15413                 this.select(item, this.multiSelect && e.ctrlKey);
15414                 this.lastSelection = item;
15415             }
15416             
15417             if(!this.tickable){
15418                 e.preventDefault();
15419             }
15420             
15421         }
15422         return true;
15423     },
15424
15425     /**
15426      * Get the number of selected nodes.
15427      * @return {Number}
15428      */
15429     getSelectionCount : function(){
15430         return this.selections.length;
15431     },
15432
15433     /**
15434      * Get the currently selected nodes.
15435      * @return {Array} An array of HTMLElements
15436      */
15437     getSelectedNodes : function(){
15438         return this.selections;
15439     },
15440
15441     /**
15442      * Get the indexes of the selected nodes.
15443      * @return {Array}
15444      */
15445     getSelectedIndexes : function(){
15446         var indexes = [], s = this.selections;
15447         for(var i = 0, len = s.length; i < len; i++){
15448             indexes.push(s[i].nodeIndex);
15449         }
15450         return indexes;
15451     },
15452
15453     /**
15454      * Clear all selections
15455      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15456      */
15457     clearSelections : function(suppressEvent){
15458         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15459             this.cmp.elements = this.selections;
15460             this.cmp.removeClass(this.selectedClass);
15461             this.selections = [];
15462             if(!suppressEvent){
15463                 this.fireEvent("selectionchange", this, this.selections);
15464             }
15465         }
15466     },
15467
15468     /**
15469      * Returns true if the passed node is selected
15470      * @param {HTMLElement/Number} node The node or node index
15471      * @return {Boolean}
15472      */
15473     isSelected : function(node){
15474         var s = this.selections;
15475         if(s.length < 1){
15476             return false;
15477         }
15478         node = this.getNode(node);
15479         return s.indexOf(node) !== -1;
15480     },
15481
15482     /**
15483      * Selects nodes.
15484      * @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
15485      * @param {Boolean} keepExisting (optional) true to keep existing selections
15486      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15487      */
15488     select : function(nodeInfo, keepExisting, suppressEvent){
15489         if(nodeInfo instanceof Array){
15490             if(!keepExisting){
15491                 this.clearSelections(true);
15492             }
15493             for(var i = 0, len = nodeInfo.length; i < len; i++){
15494                 this.select(nodeInfo[i], true, true);
15495             }
15496             return;
15497         } 
15498         var node = this.getNode(nodeInfo);
15499         if(!node || this.isSelected(node)){
15500             return; // already selected.
15501         }
15502         if(!keepExisting){
15503             this.clearSelections(true);
15504         }
15505         
15506         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15507             Roo.fly(node).addClass(this.selectedClass);
15508             this.selections.push(node);
15509             if(!suppressEvent){
15510                 this.fireEvent("selectionchange", this, this.selections);
15511             }
15512         }
15513         
15514         
15515     },
15516       /**
15517      * Unselects nodes.
15518      * @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
15519      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15520      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15521      */
15522     unselect : function(nodeInfo, keepExisting, suppressEvent)
15523     {
15524         if(nodeInfo instanceof Array){
15525             Roo.each(this.selections, function(s) {
15526                 this.unselect(s, nodeInfo);
15527             }, this);
15528             return;
15529         }
15530         var node = this.getNode(nodeInfo);
15531         if(!node || !this.isSelected(node)){
15532             //Roo.log("not selected");
15533             return; // not selected.
15534         }
15535         // fireevent???
15536         var ns = [];
15537         Roo.each(this.selections, function(s) {
15538             if (s == node ) {
15539                 Roo.fly(node).removeClass(this.selectedClass);
15540
15541                 return;
15542             }
15543             ns.push(s);
15544         },this);
15545         
15546         this.selections= ns;
15547         this.fireEvent("selectionchange", this, this.selections);
15548     },
15549
15550     /**
15551      * Gets a template node.
15552      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15553      * @return {HTMLElement} The node or null if it wasn't found
15554      */
15555     getNode : function(nodeInfo){
15556         if(typeof nodeInfo == "string"){
15557             return document.getElementById(nodeInfo);
15558         }else if(typeof nodeInfo == "number"){
15559             return this.nodes[nodeInfo];
15560         }
15561         return nodeInfo;
15562     },
15563
15564     /**
15565      * Gets a range template nodes.
15566      * @param {Number} startIndex
15567      * @param {Number} endIndex
15568      * @return {Array} An array of nodes
15569      */
15570     getNodes : function(start, end){
15571         var ns = this.nodes;
15572         start = start || 0;
15573         end = typeof end == "undefined" ? ns.length - 1 : end;
15574         var nodes = [];
15575         if(start <= end){
15576             for(var i = start; i <= end; i++){
15577                 nodes.push(ns[i]);
15578             }
15579         } else{
15580             for(var i = start; i >= end; i--){
15581                 nodes.push(ns[i]);
15582             }
15583         }
15584         return nodes;
15585     },
15586
15587     /**
15588      * Finds the index of the passed node
15589      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15590      * @return {Number} The index of the node or -1
15591      */
15592     indexOf : function(node){
15593         node = this.getNode(node);
15594         if(typeof node.nodeIndex == "number"){
15595             return node.nodeIndex;
15596         }
15597         var ns = this.nodes;
15598         for(var i = 0, len = ns.length; i < len; i++){
15599             if(ns[i] == node){
15600                 return i;
15601             }
15602         }
15603         return -1;
15604     }
15605 });
15606 /*
15607  * - LGPL
15608  *
15609  * based on jquery fullcalendar
15610  * 
15611  */
15612
15613 Roo.bootstrap = Roo.bootstrap || {};
15614 /**
15615  * @class Roo.bootstrap.Calendar
15616  * @extends Roo.bootstrap.Component
15617  * Bootstrap Calendar class
15618  * @cfg {Boolean} loadMask (true|false) default false
15619  * @cfg {Object} header generate the user specific header of the calendar, default false
15620
15621  * @constructor
15622  * Create a new Container
15623  * @param {Object} config The config object
15624  */
15625
15626
15627
15628 Roo.bootstrap.Calendar = function(config){
15629     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15630      this.addEvents({
15631         /**
15632              * @event select
15633              * Fires when a date is selected
15634              * @param {DatePicker} this
15635              * @param {Date} date The selected date
15636              */
15637         'select': true,
15638         /**
15639              * @event monthchange
15640              * Fires when the displayed month changes 
15641              * @param {DatePicker} this
15642              * @param {Date} date The selected month
15643              */
15644         'monthchange': true,
15645         /**
15646              * @event evententer
15647              * Fires when mouse over an event
15648              * @param {Calendar} this
15649              * @param {event} Event
15650              */
15651         'evententer': true,
15652         /**
15653              * @event eventleave
15654              * Fires when the mouse leaves an
15655              * @param {Calendar} this
15656              * @param {event}
15657              */
15658         'eventleave': true,
15659         /**
15660              * @event eventclick
15661              * Fires when the mouse click an
15662              * @param {Calendar} this
15663              * @param {event}
15664              */
15665         'eventclick': true
15666         
15667     });
15668
15669 };
15670
15671 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15672     
15673      /**
15674      * @cfg {Number} startDay
15675      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15676      */
15677     startDay : 0,
15678     
15679     loadMask : false,
15680     
15681     header : false,
15682       
15683     getAutoCreate : function(){
15684         
15685         
15686         var fc_button = function(name, corner, style, content ) {
15687             return Roo.apply({},{
15688                 tag : 'span',
15689                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15690                          (corner.length ?
15691                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15692                             ''
15693                         ),
15694                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15695                 unselectable: 'on'
15696             });
15697         };
15698         
15699         var header = {};
15700         
15701         if(!this.header){
15702             header = {
15703                 tag : 'table',
15704                 cls : 'fc-header',
15705                 style : 'width:100%',
15706                 cn : [
15707                     {
15708                         tag: 'tr',
15709                         cn : [
15710                             {
15711                                 tag : 'td',
15712                                 cls : 'fc-header-left',
15713                                 cn : [
15714                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15715                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15716                                     { tag: 'span', cls: 'fc-header-space' },
15717                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15718
15719
15720                                 ]
15721                             },
15722
15723                             {
15724                                 tag : 'td',
15725                                 cls : 'fc-header-center',
15726                                 cn : [
15727                                     {
15728                                         tag: 'span',
15729                                         cls: 'fc-header-title',
15730                                         cn : {
15731                                             tag: 'H2',
15732                                             html : 'month / year'
15733                                         }
15734                                     }
15735
15736                                 ]
15737                             },
15738                             {
15739                                 tag : 'td',
15740                                 cls : 'fc-header-right',
15741                                 cn : [
15742                               /*      fc_button('month', 'left', '', 'month' ),
15743                                     fc_button('week', '', '', 'week' ),
15744                                     fc_button('day', 'right', '', 'day' )
15745                                 */    
15746
15747                                 ]
15748                             }
15749
15750                         ]
15751                     }
15752                 ]
15753             };
15754         }
15755         
15756         header = this.header;
15757         
15758        
15759         var cal_heads = function() {
15760             var ret = [];
15761             // fixme - handle this.
15762             
15763             for (var i =0; i < Date.dayNames.length; i++) {
15764                 var d = Date.dayNames[i];
15765                 ret.push({
15766                     tag: 'th',
15767                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15768                     html : d.substring(0,3)
15769                 });
15770                 
15771             }
15772             ret[0].cls += ' fc-first';
15773             ret[6].cls += ' fc-last';
15774             return ret;
15775         };
15776         var cal_cell = function(n) {
15777             return  {
15778                 tag: 'td',
15779                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15780                 cn : [
15781                     {
15782                         cn : [
15783                             {
15784                                 cls: 'fc-day-number',
15785                                 html: 'D'
15786                             },
15787                             {
15788                                 cls: 'fc-day-content',
15789                              
15790                                 cn : [
15791                                      {
15792                                         style: 'position: relative;' // height: 17px;
15793                                     }
15794                                 ]
15795                             }
15796                             
15797                             
15798                         ]
15799                     }
15800                 ]
15801                 
15802             }
15803         };
15804         var cal_rows = function() {
15805             
15806             var ret = [];
15807             for (var r = 0; r < 6; r++) {
15808                 var row= {
15809                     tag : 'tr',
15810                     cls : 'fc-week',
15811                     cn : []
15812                 };
15813                 
15814                 for (var i =0; i < Date.dayNames.length; i++) {
15815                     var d = Date.dayNames[i];
15816                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15817
15818                 }
15819                 row.cn[0].cls+=' fc-first';
15820                 row.cn[0].cn[0].style = 'min-height:90px';
15821                 row.cn[6].cls+=' fc-last';
15822                 ret.push(row);
15823                 
15824             }
15825             ret[0].cls += ' fc-first';
15826             ret[4].cls += ' fc-prev-last';
15827             ret[5].cls += ' fc-last';
15828             return ret;
15829             
15830         };
15831         
15832         var cal_table = {
15833             tag: 'table',
15834             cls: 'fc-border-separate',
15835             style : 'width:100%',
15836             cellspacing  : 0,
15837             cn : [
15838                 { 
15839                     tag: 'thead',
15840                     cn : [
15841                         { 
15842                             tag: 'tr',
15843                             cls : 'fc-first fc-last',
15844                             cn : cal_heads()
15845                         }
15846                     ]
15847                 },
15848                 { 
15849                     tag: 'tbody',
15850                     cn : cal_rows()
15851                 }
15852                   
15853             ]
15854         };
15855          
15856          var cfg = {
15857             cls : 'fc fc-ltr',
15858             cn : [
15859                 header,
15860                 {
15861                     cls : 'fc-content',
15862                     style : "position: relative;",
15863                     cn : [
15864                         {
15865                             cls : 'fc-view fc-view-month fc-grid',
15866                             style : 'position: relative',
15867                             unselectable : 'on',
15868                             cn : [
15869                                 {
15870                                     cls : 'fc-event-container',
15871                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15872                                 },
15873                                 cal_table
15874                             ]
15875                         }
15876                     ]
15877     
15878                 }
15879            ] 
15880             
15881         };
15882         
15883          
15884         
15885         return cfg;
15886     },
15887     
15888     
15889     initEvents : function()
15890     {
15891         if(!this.store){
15892             throw "can not find store for calendar";
15893         }
15894         
15895         var mark = {
15896             tag: "div",
15897             cls:"x-dlg-mask",
15898             style: "text-align:center",
15899             cn: [
15900                 {
15901                     tag: "div",
15902                     style: "background-color:white;width:50%;margin:250 auto",
15903                     cn: [
15904                         {
15905                             tag: "img",
15906                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15907                         },
15908                         {
15909                             tag: "span",
15910                             html: "Loading"
15911                         }
15912                         
15913                     ]
15914                 }
15915             ]
15916         };
15917         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15918         
15919         var size = this.el.select('.fc-content', true).first().getSize();
15920         this.maskEl.setSize(size.width, size.height);
15921         this.maskEl.enableDisplayMode("block");
15922         if(!this.loadMask){
15923             this.maskEl.hide();
15924         }
15925         
15926         this.store = Roo.factory(this.store, Roo.data);
15927         this.store.on('load', this.onLoad, this);
15928         this.store.on('beforeload', this.onBeforeLoad, this);
15929         
15930         this.resize();
15931         
15932         this.cells = this.el.select('.fc-day',true);
15933         //Roo.log(this.cells);
15934         this.textNodes = this.el.query('.fc-day-number');
15935         this.cells.addClassOnOver('fc-state-hover');
15936         
15937         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15938         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15939         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15940         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15941         
15942         this.on('monthchange', this.onMonthChange, this);
15943         
15944         this.update(new Date().clearTime());
15945     },
15946     
15947     resize : function() {
15948         var sz  = this.el.getSize();
15949         
15950         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15951         this.el.select('.fc-day-content div',true).setHeight(34);
15952     },
15953     
15954     
15955     // private
15956     showPrevMonth : function(e){
15957         this.update(this.activeDate.add("mo", -1));
15958     },
15959     showToday : function(e){
15960         this.update(new Date().clearTime());
15961     },
15962     // private
15963     showNextMonth : function(e){
15964         this.update(this.activeDate.add("mo", 1));
15965     },
15966
15967     // private
15968     showPrevYear : function(){
15969         this.update(this.activeDate.add("y", -1));
15970     },
15971
15972     // private
15973     showNextYear : function(){
15974         this.update(this.activeDate.add("y", 1));
15975     },
15976
15977     
15978    // private
15979     update : function(date)
15980     {
15981         var vd = this.activeDate;
15982         this.activeDate = date;
15983 //        if(vd && this.el){
15984 //            var t = date.getTime();
15985 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15986 //                Roo.log('using add remove');
15987 //                
15988 //                this.fireEvent('monthchange', this, date);
15989 //                
15990 //                this.cells.removeClass("fc-state-highlight");
15991 //                this.cells.each(function(c){
15992 //                   if(c.dateValue == t){
15993 //                       c.addClass("fc-state-highlight");
15994 //                       setTimeout(function(){
15995 //                            try{c.dom.firstChild.focus();}catch(e){}
15996 //                       }, 50);
15997 //                       return false;
15998 //                   }
15999 //                   return true;
16000 //                });
16001 //                return;
16002 //            }
16003 //        }
16004         
16005         var days = date.getDaysInMonth();
16006         
16007         var firstOfMonth = date.getFirstDateOfMonth();
16008         var startingPos = firstOfMonth.getDay()-this.startDay;
16009         
16010         if(startingPos < this.startDay){
16011             startingPos += 7;
16012         }
16013         
16014         var pm = date.add(Date.MONTH, -1);
16015         var prevStart = pm.getDaysInMonth()-startingPos;
16016 //        
16017         this.cells = this.el.select('.fc-day',true);
16018         this.textNodes = this.el.query('.fc-day-number');
16019         this.cells.addClassOnOver('fc-state-hover');
16020         
16021         var cells = this.cells.elements;
16022         var textEls = this.textNodes;
16023         
16024         Roo.each(cells, function(cell){
16025             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16026         });
16027         
16028         days += startingPos;
16029
16030         // convert everything to numbers so it's fast
16031         var day = 86400000;
16032         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16033         //Roo.log(d);
16034         //Roo.log(pm);
16035         //Roo.log(prevStart);
16036         
16037         var today = new Date().clearTime().getTime();
16038         var sel = date.clearTime().getTime();
16039         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16040         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16041         var ddMatch = this.disabledDatesRE;
16042         var ddText = this.disabledDatesText;
16043         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16044         var ddaysText = this.disabledDaysText;
16045         var format = this.format;
16046         
16047         var setCellClass = function(cal, cell){
16048             cell.row = 0;
16049             cell.events = [];
16050             cell.more = [];
16051             //Roo.log('set Cell Class');
16052             cell.title = "";
16053             var t = d.getTime();
16054             
16055             //Roo.log(d);
16056             
16057             cell.dateValue = t;
16058             if(t == today){
16059                 cell.className += " fc-today";
16060                 cell.className += " fc-state-highlight";
16061                 cell.title = cal.todayText;
16062             }
16063             if(t == sel){
16064                 // disable highlight in other month..
16065                 //cell.className += " fc-state-highlight";
16066                 
16067             }
16068             // disabling
16069             if(t < min) {
16070                 cell.className = " fc-state-disabled";
16071                 cell.title = cal.minText;
16072                 return;
16073             }
16074             if(t > max) {
16075                 cell.className = " fc-state-disabled";
16076                 cell.title = cal.maxText;
16077                 return;
16078             }
16079             if(ddays){
16080                 if(ddays.indexOf(d.getDay()) != -1){
16081                     cell.title = ddaysText;
16082                     cell.className = " fc-state-disabled";
16083                 }
16084             }
16085             if(ddMatch && format){
16086                 var fvalue = d.dateFormat(format);
16087                 if(ddMatch.test(fvalue)){
16088                     cell.title = ddText.replace("%0", fvalue);
16089                     cell.className = " fc-state-disabled";
16090                 }
16091             }
16092             
16093             if (!cell.initialClassName) {
16094                 cell.initialClassName = cell.dom.className;
16095             }
16096             
16097             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16098         };
16099
16100         var i = 0;
16101         
16102         for(; i < startingPos; i++) {
16103             textEls[i].innerHTML = (++prevStart);
16104             d.setDate(d.getDate()+1);
16105             
16106             cells[i].className = "fc-past fc-other-month";
16107             setCellClass(this, cells[i]);
16108         }
16109         
16110         var intDay = 0;
16111         
16112         for(; i < days; i++){
16113             intDay = i - startingPos + 1;
16114             textEls[i].innerHTML = (intDay);
16115             d.setDate(d.getDate()+1);
16116             
16117             cells[i].className = ''; // "x-date-active";
16118             setCellClass(this, cells[i]);
16119         }
16120         var extraDays = 0;
16121         
16122         for(; i < 42; i++) {
16123             textEls[i].innerHTML = (++extraDays);
16124             d.setDate(d.getDate()+1);
16125             
16126             cells[i].className = "fc-future fc-other-month";
16127             setCellClass(this, cells[i]);
16128         }
16129         
16130         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16131         
16132         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16133         
16134         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16135         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16136         
16137         if(totalRows != 6){
16138             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16139             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16140         }
16141         
16142         this.fireEvent('monthchange', this, date);
16143         
16144         
16145         /*
16146         if(!this.internalRender){
16147             var main = this.el.dom.firstChild;
16148             var w = main.offsetWidth;
16149             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16150             Roo.fly(main).setWidth(w);
16151             this.internalRender = true;
16152             // opera does not respect the auto grow header center column
16153             // then, after it gets a width opera refuses to recalculate
16154             // without a second pass
16155             if(Roo.isOpera && !this.secondPass){
16156                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16157                 this.secondPass = true;
16158                 this.update.defer(10, this, [date]);
16159             }
16160         }
16161         */
16162         
16163     },
16164     
16165     findCell : function(dt) {
16166         dt = dt.clearTime().getTime();
16167         var ret = false;
16168         this.cells.each(function(c){
16169             //Roo.log("check " +c.dateValue + '?=' + dt);
16170             if(c.dateValue == dt){
16171                 ret = c;
16172                 return false;
16173             }
16174             return true;
16175         });
16176         
16177         return ret;
16178     },
16179     
16180     findCells : function(ev) {
16181         var s = ev.start.clone().clearTime().getTime();
16182        // Roo.log(s);
16183         var e= ev.end.clone().clearTime().getTime();
16184        // Roo.log(e);
16185         var ret = [];
16186         this.cells.each(function(c){
16187              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16188             
16189             if(c.dateValue > e){
16190                 return ;
16191             }
16192             if(c.dateValue < s){
16193                 return ;
16194             }
16195             ret.push(c);
16196         });
16197         
16198         return ret;    
16199     },
16200     
16201 //    findBestRow: function(cells)
16202 //    {
16203 //        var ret = 0;
16204 //        
16205 //        for (var i =0 ; i < cells.length;i++) {
16206 //            ret  = Math.max(cells[i].rows || 0,ret);
16207 //        }
16208 //        return ret;
16209 //        
16210 //    },
16211     
16212     
16213     addItem : function(ev)
16214     {
16215         // look for vertical location slot in
16216         var cells = this.findCells(ev);
16217         
16218 //        ev.row = this.findBestRow(cells);
16219         
16220         // work out the location.
16221         
16222         var crow = false;
16223         var rows = [];
16224         for(var i =0; i < cells.length; i++) {
16225             
16226             cells[i].row = cells[0].row;
16227             
16228             if(i == 0){
16229                 cells[i].row = cells[i].row + 1;
16230             }
16231             
16232             if (!crow) {
16233                 crow = {
16234                     start : cells[i],
16235                     end :  cells[i]
16236                 };
16237                 continue;
16238             }
16239             if (crow.start.getY() == cells[i].getY()) {
16240                 // on same row.
16241                 crow.end = cells[i];
16242                 continue;
16243             }
16244             // different row.
16245             rows.push(crow);
16246             crow = {
16247                 start: cells[i],
16248                 end : cells[i]
16249             };
16250             
16251         }
16252         
16253         rows.push(crow);
16254         ev.els = [];
16255         ev.rows = rows;
16256         ev.cells = cells;
16257         
16258         cells[0].events.push(ev);
16259         
16260         this.calevents.push(ev);
16261     },
16262     
16263     clearEvents: function() {
16264         
16265         if(!this.calevents){
16266             return;
16267         }
16268         
16269         Roo.each(this.cells.elements, function(c){
16270             c.row = 0;
16271             c.events = [];
16272             c.more = [];
16273         });
16274         
16275         Roo.each(this.calevents, function(e) {
16276             Roo.each(e.els, function(el) {
16277                 el.un('mouseenter' ,this.onEventEnter, this);
16278                 el.un('mouseleave' ,this.onEventLeave, this);
16279                 el.remove();
16280             },this);
16281         },this);
16282         
16283         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16284             e.remove();
16285         });
16286         
16287     },
16288     
16289     renderEvents: function()
16290     {   
16291         var _this = this;
16292         
16293         this.cells.each(function(c) {
16294             
16295             if(c.row < 5){
16296                 return;
16297             }
16298             
16299             var ev = c.events;
16300             
16301             var r = 4;
16302             if(c.row != c.events.length){
16303                 r = 4 - (4 - (c.row - c.events.length));
16304             }
16305             
16306             c.events = ev.slice(0, r);
16307             c.more = ev.slice(r);
16308             
16309             if(c.more.length && c.more.length == 1){
16310                 c.events.push(c.more.pop());
16311             }
16312             
16313             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16314             
16315         });
16316             
16317         this.cells.each(function(c) {
16318             
16319             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16320             
16321             
16322             for (var e = 0; e < c.events.length; e++){
16323                 var ev = c.events[e];
16324                 var rows = ev.rows;
16325                 
16326                 for(var i = 0; i < rows.length; i++) {
16327                 
16328                     // how many rows should it span..
16329
16330                     var  cfg = {
16331                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16332                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16333
16334                         unselectable : "on",
16335                         cn : [
16336                             {
16337                                 cls: 'fc-event-inner',
16338                                 cn : [
16339     //                                {
16340     //                                  tag:'span',
16341     //                                  cls: 'fc-event-time',
16342     //                                  html : cells.length > 1 ? '' : ev.time
16343     //                                },
16344                                     {
16345                                       tag:'span',
16346                                       cls: 'fc-event-title',
16347                                       html : String.format('{0}', ev.title)
16348                                     }
16349
16350
16351                                 ]
16352                             },
16353                             {
16354                                 cls: 'ui-resizable-handle ui-resizable-e',
16355                                 html : '&nbsp;&nbsp;&nbsp'
16356                             }
16357
16358                         ]
16359                     };
16360
16361                     if (i == 0) {
16362                         cfg.cls += ' fc-event-start';
16363                     }
16364                     if ((i+1) == rows.length) {
16365                         cfg.cls += ' fc-event-end';
16366                     }
16367
16368                     var ctr = _this.el.select('.fc-event-container',true).first();
16369                     var cg = ctr.createChild(cfg);
16370
16371                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16372                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16373
16374                     var r = (c.more.length) ? 1 : 0;
16375                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16376                     cg.setWidth(ebox.right - sbox.x -2);
16377
16378                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16379                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16380                     cg.on('click', _this.onEventClick, _this, ev);
16381
16382                     ev.els.push(cg);
16383                     
16384                 }
16385                 
16386             }
16387             
16388             
16389             if(c.more.length){
16390                 var  cfg = {
16391                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16392                     style : 'position: absolute',
16393                     unselectable : "on",
16394                     cn : [
16395                         {
16396                             cls: 'fc-event-inner',
16397                             cn : [
16398                                 {
16399                                   tag:'span',
16400                                   cls: 'fc-event-title',
16401                                   html : 'More'
16402                                 }
16403
16404
16405                             ]
16406                         },
16407                         {
16408                             cls: 'ui-resizable-handle ui-resizable-e',
16409                             html : '&nbsp;&nbsp;&nbsp'
16410                         }
16411
16412                     ]
16413                 };
16414
16415                 var ctr = _this.el.select('.fc-event-container',true).first();
16416                 var cg = ctr.createChild(cfg);
16417
16418                 var sbox = c.select('.fc-day-content',true).first().getBox();
16419                 var ebox = c.select('.fc-day-content',true).first().getBox();
16420                 //Roo.log(cg);
16421                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16422                 cg.setWidth(ebox.right - sbox.x -2);
16423
16424                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16425                 
16426             }
16427             
16428         });
16429         
16430         
16431         
16432     },
16433     
16434     onEventEnter: function (e, el,event,d) {
16435         this.fireEvent('evententer', this, el, event);
16436     },
16437     
16438     onEventLeave: function (e, el,event,d) {
16439         this.fireEvent('eventleave', this, el, event);
16440     },
16441     
16442     onEventClick: function (e, el,event,d) {
16443         this.fireEvent('eventclick', this, el, event);
16444     },
16445     
16446     onMonthChange: function () {
16447         this.store.load();
16448     },
16449     
16450     onMoreEventClick: function(e, el, more)
16451     {
16452         var _this = this;
16453         
16454         this.calpopover.placement = 'right';
16455         this.calpopover.setTitle('More');
16456         
16457         this.calpopover.setContent('');
16458         
16459         var ctr = this.calpopover.el.select('.popover-content', true).first();
16460         
16461         Roo.each(more, function(m){
16462             var cfg = {
16463                 cls : 'fc-event-hori fc-event-draggable',
16464                 html : m.title
16465             };
16466             var cg = ctr.createChild(cfg);
16467             
16468             cg.on('click', _this.onEventClick, _this, m);
16469         });
16470         
16471         this.calpopover.show(el);
16472         
16473         
16474     },
16475     
16476     onLoad: function () 
16477     {   
16478         this.calevents = [];
16479         var cal = this;
16480         
16481         if(this.store.getCount() > 0){
16482             this.store.data.each(function(d){
16483                cal.addItem({
16484                     id : d.data.id,
16485                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16486                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16487                     time : d.data.start_time,
16488                     title : d.data.title,
16489                     description : d.data.description,
16490                     venue : d.data.venue
16491                 });
16492             });
16493         }
16494         
16495         this.renderEvents();
16496         
16497         if(this.calevents.length && this.loadMask){
16498             this.maskEl.hide();
16499         }
16500     },
16501     
16502     onBeforeLoad: function()
16503     {
16504         this.clearEvents();
16505         if(this.loadMask){
16506             this.maskEl.show();
16507         }
16508     }
16509 });
16510
16511  
16512  /*
16513  * - LGPL
16514  *
16515  * element
16516  * 
16517  */
16518
16519 /**
16520  * @class Roo.bootstrap.Popover
16521  * @extends Roo.bootstrap.Component
16522  * Bootstrap Popover class
16523  * @cfg {String} html contents of the popover   (or false to use children..)
16524  * @cfg {String} title of popover (or false to hide)
16525  * @cfg {String} placement how it is placed
16526  * @cfg {String} trigger click || hover (or false to trigger manually)
16527  * @cfg {String} over what (parent or false to trigger manually.)
16528  * @cfg {Number} delay - delay before showing
16529  
16530  * @constructor
16531  * Create a new Popover
16532  * @param {Object} config The config object
16533  */
16534
16535 Roo.bootstrap.Popover = function(config){
16536     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16537     
16538     this.addEvents({
16539         // raw events
16540          /**
16541          * @event show
16542          * After the popover show
16543          * 
16544          * @param {Roo.bootstrap.Popover} this
16545          */
16546         "show" : true,
16547         /**
16548          * @event hide
16549          * After the popover hide
16550          * 
16551          * @param {Roo.bootstrap.Popover} this
16552          */
16553         "hide" : true
16554     });
16555 };
16556
16557 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16558     
16559     title: 'Fill in a title',
16560     html: false,
16561     
16562     placement : 'right',
16563     trigger : 'hover', // hover
16564     
16565     delay : 0,
16566     
16567     over: 'parent',
16568     
16569     can_build_overlaid : false,
16570     
16571     getChildContainer : function()
16572     {
16573         return this.el.select('.popover-content',true).first();
16574     },
16575     
16576     getAutoCreate : function(){
16577          
16578         var cfg = {
16579            cls : 'popover roo-dynamic',
16580            style: 'display:block',
16581            cn : [
16582                 {
16583                     cls : 'arrow'
16584                 },
16585                 {
16586                     cls : 'popover-inner',
16587                     cn : [
16588                         {
16589                             tag: 'h3',
16590                             cls: 'popover-title',
16591                             html : this.title
16592                         },
16593                         {
16594                             cls : 'popover-content',
16595                             html : this.html
16596                         }
16597                     ]
16598                     
16599                 }
16600            ]
16601         };
16602         
16603         return cfg;
16604     },
16605     setTitle: function(str)
16606     {
16607         this.title = str;
16608         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16609     },
16610     setContent: function(str)
16611     {
16612         this.html = str;
16613         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16614     },
16615     // as it get's added to the bottom of the page.
16616     onRender : function(ct, position)
16617     {
16618         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16619         if(!this.el){
16620             var cfg = Roo.apply({},  this.getAutoCreate());
16621             cfg.id = Roo.id();
16622             
16623             if (this.cls) {
16624                 cfg.cls += ' ' + this.cls;
16625             }
16626             if (this.style) {
16627                 cfg.style = this.style;
16628             }
16629             //Roo.log("adding to ");
16630             this.el = Roo.get(document.body).createChild(cfg, position);
16631 //            Roo.log(this.el);
16632         }
16633         this.initEvents();
16634     },
16635     
16636     initEvents : function()
16637     {
16638         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16639         this.el.enableDisplayMode('block');
16640         this.el.hide();
16641         if (this.over === false) {
16642             return; 
16643         }
16644         if (this.triggers === false) {
16645             return;
16646         }
16647         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16648         var triggers = this.trigger ? this.trigger.split(' ') : [];
16649         Roo.each(triggers, function(trigger) {
16650         
16651             if (trigger == 'click') {
16652                 on_el.on('click', this.toggle, this);
16653             } else if (trigger != 'manual') {
16654                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16655                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16656       
16657                 on_el.on(eventIn  ,this.enter, this);
16658                 on_el.on(eventOut, this.leave, this);
16659             }
16660         }, this);
16661         
16662     },
16663     
16664     
16665     // private
16666     timeout : null,
16667     hoverState : null,
16668     
16669     toggle : function () {
16670         this.hoverState == 'in' ? this.leave() : this.enter();
16671     },
16672     
16673     enter : function () {
16674         
16675         clearTimeout(this.timeout);
16676     
16677         this.hoverState = 'in';
16678     
16679         if (!this.delay || !this.delay.show) {
16680             this.show();
16681             return;
16682         }
16683         var _t = this;
16684         this.timeout = setTimeout(function () {
16685             if (_t.hoverState == 'in') {
16686                 _t.show();
16687             }
16688         }, this.delay.show)
16689     },
16690     
16691     leave : function() {
16692         clearTimeout(this.timeout);
16693     
16694         this.hoverState = 'out';
16695     
16696         if (!this.delay || !this.delay.hide) {
16697             this.hide();
16698             return;
16699         }
16700         var _t = this;
16701         this.timeout = setTimeout(function () {
16702             if (_t.hoverState == 'out') {
16703                 _t.hide();
16704             }
16705         }, this.delay.hide)
16706     },
16707     
16708     show : function (on_el)
16709     {
16710         if (!on_el) {
16711             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16712         }
16713         
16714         // set content.
16715         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16716         if (this.html !== false) {
16717             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16718         }
16719         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16720         if (!this.title.length) {
16721             this.el.select('.popover-title',true).hide();
16722         }
16723         
16724         var placement = typeof this.placement == 'function' ?
16725             this.placement.call(this, this.el, on_el) :
16726             this.placement;
16727             
16728         var autoToken = /\s?auto?\s?/i;
16729         var autoPlace = autoToken.test(placement);
16730         if (autoPlace) {
16731             placement = placement.replace(autoToken, '') || 'top';
16732         }
16733         
16734         //this.el.detach()
16735         //this.el.setXY([0,0]);
16736         this.el.show();
16737         this.el.dom.style.display='block';
16738         this.el.addClass(placement);
16739         
16740         //this.el.appendTo(on_el);
16741         
16742         var p = this.getPosition();
16743         var box = this.el.getBox();
16744         
16745         if (autoPlace) {
16746             // fixme..
16747         }
16748         var align = Roo.bootstrap.Popover.alignment[placement];
16749         this.el.alignTo(on_el, align[0],align[1]);
16750         //var arrow = this.el.select('.arrow',true).first();
16751         //arrow.set(align[2], 
16752         
16753         this.el.addClass('in');
16754         
16755         
16756         if (this.el.hasClass('fade')) {
16757             // fade it?
16758         }
16759         
16760         this.hoverState = 'in';
16761         
16762         this.fireEvent('show', this);
16763         
16764     },
16765     hide : function()
16766     {
16767         this.el.setXY([0,0]);
16768         this.el.removeClass('in');
16769         this.el.hide();
16770         this.hoverState = null;
16771         
16772         this.fireEvent('hide', this);
16773     }
16774     
16775 });
16776
16777 Roo.bootstrap.Popover.alignment = {
16778     'left' : ['r-l', [-10,0], 'right'],
16779     'right' : ['l-r', [10,0], 'left'],
16780     'bottom' : ['t-b', [0,10], 'top'],
16781     'top' : [ 'b-t', [0,-10], 'bottom']
16782 };
16783
16784  /*
16785  * - LGPL
16786  *
16787  * Progress
16788  * 
16789  */
16790
16791 /**
16792  * @class Roo.bootstrap.Progress
16793  * @extends Roo.bootstrap.Component
16794  * Bootstrap Progress class
16795  * @cfg {Boolean} striped striped of the progress bar
16796  * @cfg {Boolean} active animated of the progress bar
16797  * 
16798  * 
16799  * @constructor
16800  * Create a new Progress
16801  * @param {Object} config The config object
16802  */
16803
16804 Roo.bootstrap.Progress = function(config){
16805     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16806 };
16807
16808 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16809     
16810     striped : false,
16811     active: false,
16812     
16813     getAutoCreate : function(){
16814         var cfg = {
16815             tag: 'div',
16816             cls: 'progress'
16817         };
16818         
16819         
16820         if(this.striped){
16821             cfg.cls += ' progress-striped';
16822         }
16823       
16824         if(this.active){
16825             cfg.cls += ' active';
16826         }
16827         
16828         
16829         return cfg;
16830     }
16831    
16832 });
16833
16834  
16835
16836  /*
16837  * - LGPL
16838  *
16839  * ProgressBar
16840  * 
16841  */
16842
16843 /**
16844  * @class Roo.bootstrap.ProgressBar
16845  * @extends Roo.bootstrap.Component
16846  * Bootstrap ProgressBar class
16847  * @cfg {Number} aria_valuenow aria-value now
16848  * @cfg {Number} aria_valuemin aria-value min
16849  * @cfg {Number} aria_valuemax aria-value max
16850  * @cfg {String} label label for the progress bar
16851  * @cfg {String} panel (success | info | warning | danger )
16852  * @cfg {String} role role of the progress bar
16853  * @cfg {String} sr_only text
16854  * 
16855  * 
16856  * @constructor
16857  * Create a new ProgressBar
16858  * @param {Object} config The config object
16859  */
16860
16861 Roo.bootstrap.ProgressBar = function(config){
16862     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16863 };
16864
16865 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16866     
16867     aria_valuenow : 0,
16868     aria_valuemin : 0,
16869     aria_valuemax : 100,
16870     label : false,
16871     panel : false,
16872     role : false,
16873     sr_only: false,
16874     
16875     getAutoCreate : function()
16876     {
16877         
16878         var cfg = {
16879             tag: 'div',
16880             cls: 'progress-bar',
16881             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16882         };
16883         
16884         if(this.sr_only){
16885             cfg.cn = {
16886                 tag: 'span',
16887                 cls: 'sr-only',
16888                 html: this.sr_only
16889             }
16890         }
16891         
16892         if(this.role){
16893             cfg.role = this.role;
16894         }
16895         
16896         if(this.aria_valuenow){
16897             cfg['aria-valuenow'] = this.aria_valuenow;
16898         }
16899         
16900         if(this.aria_valuemin){
16901             cfg['aria-valuemin'] = this.aria_valuemin;
16902         }
16903         
16904         if(this.aria_valuemax){
16905             cfg['aria-valuemax'] = this.aria_valuemax;
16906         }
16907         
16908         if(this.label && !this.sr_only){
16909             cfg.html = this.label;
16910         }
16911         
16912         if(this.panel){
16913             cfg.cls += ' progress-bar-' + this.panel;
16914         }
16915         
16916         return cfg;
16917     },
16918     
16919     update : function(aria_valuenow)
16920     {
16921         this.aria_valuenow = aria_valuenow;
16922         
16923         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16924     }
16925    
16926 });
16927
16928  
16929
16930  /*
16931  * - LGPL
16932  *
16933  * column
16934  * 
16935  */
16936
16937 /**
16938  * @class Roo.bootstrap.TabGroup
16939  * @extends Roo.bootstrap.Column
16940  * Bootstrap Column class
16941  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16942  * @cfg {Boolean} carousel true to make the group behave like a carousel
16943  * @cfg {Boolean} bullets show bullets for the panels
16944  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16945  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16946  * @cfg {Boolean} showarrow (true|false) show arrow default true
16947  * 
16948  * @constructor
16949  * Create a new TabGroup
16950  * @param {Object} config The config object
16951  */
16952
16953 Roo.bootstrap.TabGroup = function(config){
16954     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16955     if (!this.navId) {
16956         this.navId = Roo.id();
16957     }
16958     this.tabs = [];
16959     Roo.bootstrap.TabGroup.register(this);
16960     
16961 };
16962
16963 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16964     
16965     carousel : false,
16966     transition : false,
16967     bullets : 0,
16968     timer : 0,
16969     autoslide : false,
16970     slideFn : false,
16971     slideOnTouch : false,
16972     showarrow : true,
16973     
16974     getAutoCreate : function()
16975     {
16976         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16977         
16978         cfg.cls += ' tab-content';
16979         
16980         if (this.carousel) {
16981             cfg.cls += ' carousel slide';
16982             
16983             cfg.cn = [{
16984                cls : 'carousel-inner',
16985                cn : []
16986             }];
16987         
16988             if(this.bullets  && !Roo.isTouch){
16989                 
16990                 var bullets = {
16991                     cls : 'carousel-bullets',
16992                     cn : []
16993                 };
16994                
16995                 if(this.bullets_cls){
16996                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16997                 }
16998                 
16999                 bullets.cn.push({
17000                     cls : 'clear'
17001                 });
17002                 
17003                 cfg.cn[0].cn.push(bullets);
17004             }
17005             
17006             if(this.showarrow){
17007                 cfg.cn[0].cn.push({
17008                     tag : 'div',
17009                     class : 'carousel-arrow',
17010                     cn : [
17011                         {
17012                             tag : 'div',
17013                             class : 'carousel-prev',
17014                             cn : [
17015                                 {
17016                                     tag : 'i',
17017                                     class : 'fa fa-chevron-left'
17018                                 }
17019                             ]
17020                         },
17021                         {
17022                             tag : 'div',
17023                             class : 'carousel-next',
17024                             cn : [
17025                                 {
17026                                     tag : 'i',
17027                                     class : 'fa fa-chevron-right'
17028                                 }
17029                             ]
17030                         }
17031                     ]
17032                 });
17033             }
17034             
17035         }
17036         
17037         return cfg;
17038     },
17039     
17040     initEvents:  function()
17041     {
17042 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17043 //            this.el.on("touchstart", this.onTouchStart, this);
17044 //        }
17045         
17046         if(this.autoslide){
17047             var _this = this;
17048             
17049             this.slideFn = window.setInterval(function() {
17050                 _this.showPanelNext();
17051             }, this.timer);
17052         }
17053         
17054         if(this.showarrow){
17055             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17056             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17057         }
17058         
17059         
17060     },
17061     
17062 //    onTouchStart : function(e, el, o)
17063 //    {
17064 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17065 //            return;
17066 //        }
17067 //        
17068 //        this.showPanelNext();
17069 //    },
17070     
17071     
17072     getChildContainer : function()
17073     {
17074         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17075     },
17076     
17077     /**
17078     * register a Navigation item
17079     * @param {Roo.bootstrap.NavItem} the navitem to add
17080     */
17081     register : function(item)
17082     {
17083         this.tabs.push( item);
17084         item.navId = this.navId; // not really needed..
17085         this.addBullet();
17086     
17087     },
17088     
17089     getActivePanel : function()
17090     {
17091         var r = false;
17092         Roo.each(this.tabs, function(t) {
17093             if (t.active) {
17094                 r = t;
17095                 return false;
17096             }
17097             return null;
17098         });
17099         return r;
17100         
17101     },
17102     getPanelByName : function(n)
17103     {
17104         var r = false;
17105         Roo.each(this.tabs, function(t) {
17106             if (t.tabId == n) {
17107                 r = t;
17108                 return false;
17109             }
17110             return null;
17111         });
17112         return r;
17113     },
17114     indexOfPanel : function(p)
17115     {
17116         var r = false;
17117         Roo.each(this.tabs, function(t,i) {
17118             if (t.tabId == p.tabId) {
17119                 r = i;
17120                 return false;
17121             }
17122             return null;
17123         });
17124         return r;
17125     },
17126     /**
17127      * show a specific panel
17128      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17129      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17130      */
17131     showPanel : function (pan)
17132     {
17133         if(this.transition || typeof(pan) == 'undefined'){
17134             Roo.log("waiting for the transitionend");
17135             return;
17136         }
17137         
17138         if (typeof(pan) == 'number') {
17139             pan = this.tabs[pan];
17140         }
17141         
17142         if (typeof(pan) == 'string') {
17143             pan = this.getPanelByName(pan);
17144         }
17145         
17146         var cur = this.getActivePanel();
17147         
17148         if(!pan || !cur){
17149             Roo.log('pan or acitve pan is undefined');
17150             return false;
17151         }
17152         
17153         if (pan.tabId == this.getActivePanel().tabId) {
17154             return true;
17155         }
17156         
17157         if (false === cur.fireEvent('beforedeactivate')) {
17158             return false;
17159         }
17160         
17161         if(this.bullets > 0 && !Roo.isTouch){
17162             this.setActiveBullet(this.indexOfPanel(pan));
17163         }
17164         
17165         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17166             
17167             this.transition = true;
17168             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17169             var lr = dir == 'next' ? 'left' : 'right';
17170             pan.el.addClass(dir); // or prev
17171             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17172             cur.el.addClass(lr); // or right
17173             pan.el.addClass(lr);
17174             
17175             var _this = this;
17176             cur.el.on('transitionend', function() {
17177                 Roo.log("trans end?");
17178                 
17179                 pan.el.removeClass([lr,dir]);
17180                 pan.setActive(true);
17181                 
17182                 cur.el.removeClass([lr]);
17183                 cur.setActive(false);
17184                 
17185                 _this.transition = false;
17186                 
17187             }, this, { single:  true } );
17188             
17189             return true;
17190         }
17191         
17192         cur.setActive(false);
17193         pan.setActive(true);
17194         
17195         return true;
17196         
17197     },
17198     showPanelNext : function()
17199     {
17200         var i = this.indexOfPanel(this.getActivePanel());
17201         
17202         if (i >= this.tabs.length - 1 && !this.autoslide) {
17203             return;
17204         }
17205         
17206         if (i >= this.tabs.length - 1 && this.autoslide) {
17207             i = -1;
17208         }
17209         
17210         this.showPanel(this.tabs[i+1]);
17211     },
17212     
17213     showPanelPrev : function()
17214     {
17215         var i = this.indexOfPanel(this.getActivePanel());
17216         
17217         if (i  < 1 && !this.autoslide) {
17218             return;
17219         }
17220         
17221         if (i < 1 && this.autoslide) {
17222             i = this.tabs.length;
17223         }
17224         
17225         this.showPanel(this.tabs[i-1]);
17226     },
17227     
17228     
17229     addBullet: function()
17230     {
17231         if(!this.bullets || Roo.isTouch){
17232             return;
17233         }
17234         var ctr = this.el.select('.carousel-bullets',true).first();
17235         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17236         var bullet = ctr.createChild({
17237             cls : 'bullet bullet-' + i
17238         },ctr.dom.lastChild);
17239         
17240         
17241         var _this = this;
17242         
17243         bullet.on('click', (function(e, el, o, ii, t){
17244
17245             e.preventDefault();
17246
17247             this.showPanel(ii);
17248
17249             if(this.autoslide && this.slideFn){
17250                 clearInterval(this.slideFn);
17251                 this.slideFn = window.setInterval(function() {
17252                     _this.showPanelNext();
17253                 }, this.timer);
17254             }
17255
17256         }).createDelegate(this, [i, bullet], true));
17257                 
17258         
17259     },
17260      
17261     setActiveBullet : function(i)
17262     {
17263         if(Roo.isTouch){
17264             return;
17265         }
17266         
17267         Roo.each(this.el.select('.bullet', true).elements, function(el){
17268             el.removeClass('selected');
17269         });
17270
17271         var bullet = this.el.select('.bullet-' + i, true).first();
17272         
17273         if(!bullet){
17274             return;
17275         }
17276         
17277         bullet.addClass('selected');
17278     }
17279     
17280     
17281   
17282 });
17283
17284  
17285
17286  
17287  
17288 Roo.apply(Roo.bootstrap.TabGroup, {
17289     
17290     groups: {},
17291      /**
17292     * register a Navigation Group
17293     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17294     */
17295     register : function(navgrp)
17296     {
17297         this.groups[navgrp.navId] = navgrp;
17298         
17299     },
17300     /**
17301     * fetch a Navigation Group based on the navigation ID
17302     * if one does not exist , it will get created.
17303     * @param {string} the navgroup to add
17304     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17305     */
17306     get: function(navId) {
17307         if (typeof(this.groups[navId]) == 'undefined') {
17308             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17309         }
17310         return this.groups[navId] ;
17311     }
17312     
17313     
17314     
17315 });
17316
17317  /*
17318  * - LGPL
17319  *
17320  * TabPanel
17321  * 
17322  */
17323
17324 /**
17325  * @class Roo.bootstrap.TabPanel
17326  * @extends Roo.bootstrap.Component
17327  * Bootstrap TabPanel class
17328  * @cfg {Boolean} active panel active
17329  * @cfg {String} html panel content
17330  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17331  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17332  * @cfg {String} href click to link..
17333  * 
17334  * 
17335  * @constructor
17336  * Create a new TabPanel
17337  * @param {Object} config The config object
17338  */
17339
17340 Roo.bootstrap.TabPanel = function(config){
17341     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17342     this.addEvents({
17343         /**
17344              * @event changed
17345              * Fires when the active status changes
17346              * @param {Roo.bootstrap.TabPanel} this
17347              * @param {Boolean} state the new state
17348             
17349          */
17350         'changed': true,
17351         /**
17352              * @event beforedeactivate
17353              * Fires before a tab is de-activated - can be used to do validation on a form.
17354              * @param {Roo.bootstrap.TabPanel} this
17355              * @return {Boolean} false if there is an error
17356             
17357          */
17358         'beforedeactivate': true
17359      });
17360     
17361     this.tabId = this.tabId || Roo.id();
17362   
17363 };
17364
17365 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17366     
17367     active: false,
17368     html: false,
17369     tabId: false,
17370     navId : false,
17371     href : '',
17372     
17373     getAutoCreate : function(){
17374         var cfg = {
17375             tag: 'div',
17376             // item is needed for carousel - not sure if it has any effect otherwise
17377             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17378             html: this.html || ''
17379         };
17380         
17381         if(this.active){
17382             cfg.cls += ' active';
17383         }
17384         
17385         if(this.tabId){
17386             cfg.tabId = this.tabId;
17387         }
17388         
17389         
17390         return cfg;
17391     },
17392     
17393     initEvents:  function()
17394     {
17395         var p = this.parent();
17396         
17397         this.navId = this.navId || p.navId;
17398         
17399         if (typeof(this.navId) != 'undefined') {
17400             // not really needed.. but just in case.. parent should be a NavGroup.
17401             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17402             
17403             tg.register(this);
17404             
17405             var i = tg.tabs.length - 1;
17406             
17407             if(this.active && tg.bullets > 0 && i < tg.bullets){
17408                 tg.setActiveBullet(i);
17409             }
17410         }
17411         
17412         this.el.on('click', this.onClick, this);
17413         
17414         if(Roo.isTouch){
17415             this.el.on("touchstart", this.onTouchStart, this);
17416             this.el.on("touchmove", this.onTouchMove, this);
17417             this.el.on("touchend", this.onTouchEnd, this);
17418         }
17419         
17420     },
17421     
17422     onRender : function(ct, position)
17423     {
17424         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17425     },
17426     
17427     setActive : function(state)
17428     {
17429         Roo.log("panel - set active " + this.tabId + "=" + state);
17430         
17431         this.active = state;
17432         if (!state) {
17433             this.el.removeClass('active');
17434             
17435         } else  if (!this.el.hasClass('active')) {
17436             this.el.addClass('active');
17437         }
17438         
17439         this.fireEvent('changed', this, state);
17440     },
17441     
17442     onClick : function(e)
17443     {
17444         e.preventDefault();
17445         
17446         if(!this.href.length){
17447             return;
17448         }
17449         
17450         window.location.href = this.href;
17451     },
17452     
17453     startX : 0,
17454     startY : 0,
17455     endX : 0,
17456     endY : 0,
17457     swiping : false,
17458     
17459     onTouchStart : function(e)
17460     {
17461         this.swiping = false;
17462         
17463         this.startX = e.browserEvent.touches[0].clientX;
17464         this.startY = e.browserEvent.touches[0].clientY;
17465     },
17466     
17467     onTouchMove : function(e)
17468     {
17469         this.swiping = true;
17470         
17471         this.endX = e.browserEvent.touches[0].clientX;
17472         this.endY = e.browserEvent.touches[0].clientY;
17473     },
17474     
17475     onTouchEnd : function(e)
17476     {
17477         if(!this.swiping){
17478             this.onClick(e);
17479             return;
17480         }
17481         
17482         var tabGroup = this.parent();
17483         
17484         if(this.endX > this.startX){ // swiping right
17485             tabGroup.showPanelPrev();
17486             return;
17487         }
17488         
17489         if(this.startX > this.endX){ // swiping left
17490             tabGroup.showPanelNext();
17491             return;
17492         }
17493     }
17494     
17495     
17496 });
17497  
17498
17499  
17500
17501  /*
17502  * - LGPL
17503  *
17504  * DateField
17505  * 
17506  */
17507
17508 /**
17509  * @class Roo.bootstrap.DateField
17510  * @extends Roo.bootstrap.Input
17511  * Bootstrap DateField class
17512  * @cfg {Number} weekStart default 0
17513  * @cfg {String} viewMode default empty, (months|years)
17514  * @cfg {String} minViewMode default empty, (months|years)
17515  * @cfg {Number} startDate default -Infinity
17516  * @cfg {Number} endDate default Infinity
17517  * @cfg {Boolean} todayHighlight default false
17518  * @cfg {Boolean} todayBtn default false
17519  * @cfg {Boolean} calendarWeeks default false
17520  * @cfg {Object} daysOfWeekDisabled default empty
17521  * @cfg {Boolean} singleMode default false (true | false)
17522  * 
17523  * @cfg {Boolean} keyboardNavigation default true
17524  * @cfg {String} language default en
17525  * 
17526  * @constructor
17527  * Create a new DateField
17528  * @param {Object} config The config object
17529  */
17530
17531 Roo.bootstrap.DateField = function(config){
17532     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17533      this.addEvents({
17534             /**
17535              * @event show
17536              * Fires when this field show.
17537              * @param {Roo.bootstrap.DateField} this
17538              * @param {Mixed} date The date value
17539              */
17540             show : true,
17541             /**
17542              * @event show
17543              * Fires when this field hide.
17544              * @param {Roo.bootstrap.DateField} this
17545              * @param {Mixed} date The date value
17546              */
17547             hide : true,
17548             /**
17549              * @event select
17550              * Fires when select a date.
17551              * @param {Roo.bootstrap.DateField} this
17552              * @param {Mixed} date The date value
17553              */
17554             select : true,
17555             /**
17556              * @event beforeselect
17557              * Fires when before select a date.
17558              * @param {Roo.bootstrap.DateField} this
17559              * @param {Mixed} date The date value
17560              */
17561             beforeselect : true
17562         });
17563 };
17564
17565 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17566     
17567     /**
17568      * @cfg {String} format
17569      * The default date format string which can be overriden for localization support.  The format must be
17570      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17571      */
17572     format : "m/d/y",
17573     /**
17574      * @cfg {String} altFormats
17575      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17576      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17577      */
17578     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17579     
17580     weekStart : 0,
17581     
17582     viewMode : '',
17583     
17584     minViewMode : '',
17585     
17586     todayHighlight : false,
17587     
17588     todayBtn: false,
17589     
17590     language: 'en',
17591     
17592     keyboardNavigation: true,
17593     
17594     calendarWeeks: false,
17595     
17596     startDate: -Infinity,
17597     
17598     endDate: Infinity,
17599     
17600     daysOfWeekDisabled: [],
17601     
17602     _events: [],
17603     
17604     singleMode : false,
17605     
17606     UTCDate: function()
17607     {
17608         return new Date(Date.UTC.apply(Date, arguments));
17609     },
17610     
17611     UTCToday: function()
17612     {
17613         var today = new Date();
17614         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17615     },
17616     
17617     getDate: function() {
17618             var d = this.getUTCDate();
17619             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17620     },
17621     
17622     getUTCDate: function() {
17623             return this.date;
17624     },
17625     
17626     setDate: function(d) {
17627             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17628     },
17629     
17630     setUTCDate: function(d) {
17631             this.date = d;
17632             this.setValue(this.formatDate(this.date));
17633     },
17634         
17635     onRender: function(ct, position)
17636     {
17637         
17638         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17639         
17640         this.language = this.language || 'en';
17641         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17642         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17643         
17644         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17645         this.format = this.format || 'm/d/y';
17646         this.isInline = false;
17647         this.isInput = true;
17648         this.component = this.el.select('.add-on', true).first() || false;
17649         this.component = (this.component && this.component.length === 0) ? false : this.component;
17650         this.hasInput = this.component && this.inputEl().length;
17651         
17652         if (typeof(this.minViewMode === 'string')) {
17653             switch (this.minViewMode) {
17654                 case 'months':
17655                     this.minViewMode = 1;
17656                     break;
17657                 case 'years':
17658                     this.minViewMode = 2;
17659                     break;
17660                 default:
17661                     this.minViewMode = 0;
17662                     break;
17663             }
17664         }
17665         
17666         if (typeof(this.viewMode === 'string')) {
17667             switch (this.viewMode) {
17668                 case 'months':
17669                     this.viewMode = 1;
17670                     break;
17671                 case 'years':
17672                     this.viewMode = 2;
17673                     break;
17674                 default:
17675                     this.viewMode = 0;
17676                     break;
17677             }
17678         }
17679                 
17680         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17681         
17682 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17683         
17684         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17685         
17686         this.picker().on('mousedown', this.onMousedown, this);
17687         this.picker().on('click', this.onClick, this);
17688         
17689         this.picker().addClass('datepicker-dropdown');
17690         
17691         this.startViewMode = this.viewMode;
17692         
17693         if(this.singleMode){
17694             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17695                 v.setVisibilityMode(Roo.Element.DISPLAY);
17696                 v.hide();
17697             });
17698             
17699             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17700                 v.setStyle('width', '189px');
17701             });
17702         }
17703         
17704         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17705             if(!this.calendarWeeks){
17706                 v.remove();
17707                 return;
17708             }
17709             
17710             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17711             v.attr('colspan', function(i, val){
17712                 return parseInt(val) + 1;
17713             });
17714         });
17715                         
17716         
17717         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17718         
17719         this.setStartDate(this.startDate);
17720         this.setEndDate(this.endDate);
17721         
17722         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17723         
17724         this.fillDow();
17725         this.fillMonths();
17726         this.update();
17727         this.showMode();
17728         
17729         if(this.isInline) {
17730             this.show();
17731         }
17732     },
17733     
17734     picker : function()
17735     {
17736         return this.pickerEl;
17737 //        return this.el.select('.datepicker', true).first();
17738     },
17739     
17740     fillDow: function()
17741     {
17742         var dowCnt = this.weekStart;
17743         
17744         var dow = {
17745             tag: 'tr',
17746             cn: [
17747                 
17748             ]
17749         };
17750         
17751         if(this.calendarWeeks){
17752             dow.cn.push({
17753                 tag: 'th',
17754                 cls: 'cw',
17755                 html: '&nbsp;'
17756             })
17757         }
17758         
17759         while (dowCnt < this.weekStart + 7) {
17760             dow.cn.push({
17761                 tag: 'th',
17762                 cls: 'dow',
17763                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17764             });
17765         }
17766         
17767         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17768     },
17769     
17770     fillMonths: function()
17771     {    
17772         var i = 0;
17773         var months = this.picker().select('>.datepicker-months td', true).first();
17774         
17775         months.dom.innerHTML = '';
17776         
17777         while (i < 12) {
17778             var month = {
17779                 tag: 'span',
17780                 cls: 'month',
17781                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17782             };
17783             
17784             months.createChild(month);
17785         }
17786         
17787     },
17788     
17789     update: function()
17790     {
17791         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;
17792         
17793         if (this.date < this.startDate) {
17794             this.viewDate = new Date(this.startDate);
17795         } else if (this.date > this.endDate) {
17796             this.viewDate = new Date(this.endDate);
17797         } else {
17798             this.viewDate = new Date(this.date);
17799         }
17800         
17801         this.fill();
17802     },
17803     
17804     fill: function() 
17805     {
17806         var d = new Date(this.viewDate),
17807                 year = d.getUTCFullYear(),
17808                 month = d.getUTCMonth(),
17809                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17810                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17811                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17812                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17813                 currentDate = this.date && this.date.valueOf(),
17814                 today = this.UTCToday();
17815         
17816         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17817         
17818 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17819         
17820 //        this.picker.select('>tfoot th.today').
17821 //                                              .text(dates[this.language].today)
17822 //                                              .toggle(this.todayBtn !== false);
17823     
17824         this.updateNavArrows();
17825         this.fillMonths();
17826                                                 
17827         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17828         
17829         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17830          
17831         prevMonth.setUTCDate(day);
17832         
17833         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17834         
17835         var nextMonth = new Date(prevMonth);
17836         
17837         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17838         
17839         nextMonth = nextMonth.valueOf();
17840         
17841         var fillMonths = false;
17842         
17843         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17844         
17845         while(prevMonth.valueOf() < nextMonth) {
17846             var clsName = '';
17847             
17848             if (prevMonth.getUTCDay() === this.weekStart) {
17849                 if(fillMonths){
17850                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17851                 }
17852                     
17853                 fillMonths = {
17854                     tag: 'tr',
17855                     cn: []
17856                 };
17857                 
17858                 if(this.calendarWeeks){
17859                     // ISO 8601: First week contains first thursday.
17860                     // ISO also states week starts on Monday, but we can be more abstract here.
17861                     var
17862                     // Start of current week: based on weekstart/current date
17863                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17864                     // Thursday of this week
17865                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17866                     // First Thursday of year, year from thursday
17867                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17868                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17869                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17870                     
17871                     fillMonths.cn.push({
17872                         tag: 'td',
17873                         cls: 'cw',
17874                         html: calWeek
17875                     });
17876                 }
17877             }
17878             
17879             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17880                 clsName += ' old';
17881             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17882                 clsName += ' new';
17883             }
17884             if (this.todayHighlight &&
17885                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17886                 prevMonth.getUTCMonth() == today.getMonth() &&
17887                 prevMonth.getUTCDate() == today.getDate()) {
17888                 clsName += ' today';
17889             }
17890             
17891             if (currentDate && prevMonth.valueOf() === currentDate) {
17892                 clsName += ' active';
17893             }
17894             
17895             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17896                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17897                     clsName += ' disabled';
17898             }
17899             
17900             fillMonths.cn.push({
17901                 tag: 'td',
17902                 cls: 'day ' + clsName,
17903                 html: prevMonth.getDate()
17904             });
17905             
17906             prevMonth.setDate(prevMonth.getDate()+1);
17907         }
17908           
17909         var currentYear = this.date && this.date.getUTCFullYear();
17910         var currentMonth = this.date && this.date.getUTCMonth();
17911         
17912         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17913         
17914         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17915             v.removeClass('active');
17916             
17917             if(currentYear === year && k === currentMonth){
17918                 v.addClass('active');
17919             }
17920             
17921             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17922                 v.addClass('disabled');
17923             }
17924             
17925         });
17926         
17927         
17928         year = parseInt(year/10, 10) * 10;
17929         
17930         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17931         
17932         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17933         
17934         year -= 1;
17935         for (var i = -1; i < 11; i++) {
17936             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17937                 tag: 'span',
17938                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17939                 html: year
17940             });
17941             
17942             year += 1;
17943         }
17944     },
17945     
17946     showMode: function(dir) 
17947     {
17948         if (dir) {
17949             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17950         }
17951         
17952         Roo.each(this.picker().select('>div',true).elements, function(v){
17953             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17954             v.hide();
17955         });
17956         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17957     },
17958     
17959     place: function()
17960     {
17961         if(this.isInline) {
17962             return;
17963         }
17964         
17965         this.picker().removeClass(['bottom', 'top']);
17966         
17967         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17968             /*
17969              * place to the top of element!
17970              *
17971              */
17972             
17973             this.picker().addClass('top');
17974             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17975             
17976             return;
17977         }
17978         
17979         this.picker().addClass('bottom');
17980         
17981         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17982     },
17983     
17984     parseDate : function(value)
17985     {
17986         if(!value || value instanceof Date){
17987             return value;
17988         }
17989         var v = Date.parseDate(value, this.format);
17990         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17991             v = Date.parseDate(value, 'Y-m-d');
17992         }
17993         if(!v && this.altFormats){
17994             if(!this.altFormatsArray){
17995                 this.altFormatsArray = this.altFormats.split("|");
17996             }
17997             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17998                 v = Date.parseDate(value, this.altFormatsArray[i]);
17999             }
18000         }
18001         return v;
18002     },
18003     
18004     formatDate : function(date, fmt)
18005     {   
18006         return (!date || !(date instanceof Date)) ?
18007         date : date.dateFormat(fmt || this.format);
18008     },
18009     
18010     onFocus : function()
18011     {
18012         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18013         this.show();
18014     },
18015     
18016     onBlur : function()
18017     {
18018         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18019         
18020         var d = this.inputEl().getValue();
18021         
18022         this.setValue(d);
18023                 
18024         this.hide();
18025     },
18026     
18027     show : function()
18028     {
18029         this.picker().show();
18030         this.update();
18031         this.place();
18032         
18033         this.fireEvent('show', this, this.date);
18034     },
18035     
18036     hide : function()
18037     {
18038         if(this.isInline) {
18039             return;
18040         }
18041         this.picker().hide();
18042         this.viewMode = this.startViewMode;
18043         this.showMode();
18044         
18045         this.fireEvent('hide', this, this.date);
18046         
18047     },
18048     
18049     onMousedown: function(e)
18050     {
18051         e.stopPropagation();
18052         e.preventDefault();
18053     },
18054     
18055     keyup: function(e)
18056     {
18057         Roo.bootstrap.DateField.superclass.keyup.call(this);
18058         this.update();
18059     },
18060
18061     setValue: function(v)
18062     {
18063         if(this.fireEvent('beforeselect', this, v) !== false){
18064             var d = new Date(this.parseDate(v) ).clearTime();
18065         
18066             if(isNaN(d.getTime())){
18067                 this.date = this.viewDate = '';
18068                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18069                 return;
18070             }
18071
18072             v = this.formatDate(d);
18073
18074             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18075
18076             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18077
18078             this.update();
18079
18080             this.fireEvent('select', this, this.date);
18081         }
18082     },
18083     
18084     getValue: function()
18085     {
18086         return this.formatDate(this.date);
18087     },
18088     
18089     fireKey: function(e)
18090     {
18091         if (!this.picker().isVisible()){
18092             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18093                 this.show();
18094             }
18095             return;
18096         }
18097         
18098         var dateChanged = false,
18099         dir, day, month,
18100         newDate, newViewDate;
18101         
18102         switch(e.keyCode){
18103             case 27: // escape
18104                 this.hide();
18105                 e.preventDefault();
18106                 break;
18107             case 37: // left
18108             case 39: // right
18109                 if (!this.keyboardNavigation) {
18110                     break;
18111                 }
18112                 dir = e.keyCode == 37 ? -1 : 1;
18113                 
18114                 if (e.ctrlKey){
18115                     newDate = this.moveYear(this.date, dir);
18116                     newViewDate = this.moveYear(this.viewDate, dir);
18117                 } else if (e.shiftKey){
18118                     newDate = this.moveMonth(this.date, dir);
18119                     newViewDate = this.moveMonth(this.viewDate, dir);
18120                 } else {
18121                     newDate = new Date(this.date);
18122                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18123                     newViewDate = new Date(this.viewDate);
18124                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18125                 }
18126                 if (this.dateWithinRange(newDate)){
18127                     this.date = newDate;
18128                     this.viewDate = newViewDate;
18129                     this.setValue(this.formatDate(this.date));
18130 //                    this.update();
18131                     e.preventDefault();
18132                     dateChanged = true;
18133                 }
18134                 break;
18135             case 38: // up
18136             case 40: // down
18137                 if (!this.keyboardNavigation) {
18138                     break;
18139                 }
18140                 dir = e.keyCode == 38 ? -1 : 1;
18141                 if (e.ctrlKey){
18142                     newDate = this.moveYear(this.date, dir);
18143                     newViewDate = this.moveYear(this.viewDate, dir);
18144                 } else if (e.shiftKey){
18145                     newDate = this.moveMonth(this.date, dir);
18146                     newViewDate = this.moveMonth(this.viewDate, dir);
18147                 } else {
18148                     newDate = new Date(this.date);
18149                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18150                     newViewDate = new Date(this.viewDate);
18151                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18152                 }
18153                 if (this.dateWithinRange(newDate)){
18154                     this.date = newDate;
18155                     this.viewDate = newViewDate;
18156                     this.setValue(this.formatDate(this.date));
18157 //                    this.update();
18158                     e.preventDefault();
18159                     dateChanged = true;
18160                 }
18161                 break;
18162             case 13: // enter
18163                 this.setValue(this.formatDate(this.date));
18164                 this.hide();
18165                 e.preventDefault();
18166                 break;
18167             case 9: // tab
18168                 this.setValue(this.formatDate(this.date));
18169                 this.hide();
18170                 break;
18171             case 16: // shift
18172             case 17: // ctrl
18173             case 18: // alt
18174                 break;
18175             default :
18176                 this.hide();
18177                 
18178         }
18179     },
18180     
18181     
18182     onClick: function(e) 
18183     {
18184         e.stopPropagation();
18185         e.preventDefault();
18186         
18187         var target = e.getTarget();
18188         
18189         if(target.nodeName.toLowerCase() === 'i'){
18190             target = Roo.get(target).dom.parentNode;
18191         }
18192         
18193         var nodeName = target.nodeName;
18194         var className = target.className;
18195         var html = target.innerHTML;
18196         //Roo.log(nodeName);
18197         
18198         switch(nodeName.toLowerCase()) {
18199             case 'th':
18200                 switch(className) {
18201                     case 'switch':
18202                         this.showMode(1);
18203                         break;
18204                     case 'prev':
18205                     case 'next':
18206                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18207                         switch(this.viewMode){
18208                                 case 0:
18209                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18210                                         break;
18211                                 case 1:
18212                                 case 2:
18213                                         this.viewDate = this.moveYear(this.viewDate, dir);
18214                                         break;
18215                         }
18216                         this.fill();
18217                         break;
18218                     case 'today':
18219                         var date = new Date();
18220                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18221 //                        this.fill()
18222                         this.setValue(this.formatDate(this.date));
18223                         
18224                         this.hide();
18225                         break;
18226                 }
18227                 break;
18228             case 'span':
18229                 if (className.indexOf('disabled') < 0) {
18230                     this.viewDate.setUTCDate(1);
18231                     if (className.indexOf('month') > -1) {
18232                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18233                     } else {
18234                         var year = parseInt(html, 10) || 0;
18235                         this.viewDate.setUTCFullYear(year);
18236                         
18237                     }
18238                     
18239                     if(this.singleMode){
18240                         this.setValue(this.formatDate(this.viewDate));
18241                         this.hide();
18242                         return;
18243                     }
18244                     
18245                     this.showMode(-1);
18246                     this.fill();
18247                 }
18248                 break;
18249                 
18250             case 'td':
18251                 //Roo.log(className);
18252                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18253                     var day = parseInt(html, 10) || 1;
18254                     var year = this.viewDate.getUTCFullYear(),
18255                         month = this.viewDate.getUTCMonth();
18256
18257                     if (className.indexOf('old') > -1) {
18258                         if(month === 0 ){
18259                             month = 11;
18260                             year -= 1;
18261                         }else{
18262                             month -= 1;
18263                         }
18264                     } else if (className.indexOf('new') > -1) {
18265                         if (month == 11) {
18266                             month = 0;
18267                             year += 1;
18268                         } else {
18269                             month += 1;
18270                         }
18271                     }
18272                     //Roo.log([year,month,day]);
18273                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18274                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18275 //                    this.fill();
18276                     //Roo.log(this.formatDate(this.date));
18277                     this.setValue(this.formatDate(this.date));
18278                     this.hide();
18279                 }
18280                 break;
18281         }
18282     },
18283     
18284     setStartDate: function(startDate)
18285     {
18286         this.startDate = startDate || -Infinity;
18287         if (this.startDate !== -Infinity) {
18288             this.startDate = this.parseDate(this.startDate);
18289         }
18290         this.update();
18291         this.updateNavArrows();
18292     },
18293
18294     setEndDate: function(endDate)
18295     {
18296         this.endDate = endDate || Infinity;
18297         if (this.endDate !== Infinity) {
18298             this.endDate = this.parseDate(this.endDate);
18299         }
18300         this.update();
18301         this.updateNavArrows();
18302     },
18303     
18304     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18305     {
18306         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18307         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18308             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18309         }
18310         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18311             return parseInt(d, 10);
18312         });
18313         this.update();
18314         this.updateNavArrows();
18315     },
18316     
18317     updateNavArrows: function() 
18318     {
18319         if(this.singleMode){
18320             return;
18321         }
18322         
18323         var d = new Date(this.viewDate),
18324         year = d.getUTCFullYear(),
18325         month = d.getUTCMonth();
18326         
18327         Roo.each(this.picker().select('.prev', true).elements, function(v){
18328             v.show();
18329             switch (this.viewMode) {
18330                 case 0:
18331
18332                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18333                         v.hide();
18334                     }
18335                     break;
18336                 case 1:
18337                 case 2:
18338                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18339                         v.hide();
18340                     }
18341                     break;
18342             }
18343         });
18344         
18345         Roo.each(this.picker().select('.next', true).elements, function(v){
18346             v.show();
18347             switch (this.viewMode) {
18348                 case 0:
18349
18350                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18351                         v.hide();
18352                     }
18353                     break;
18354                 case 1:
18355                 case 2:
18356                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18357                         v.hide();
18358                     }
18359                     break;
18360             }
18361         })
18362     },
18363     
18364     moveMonth: function(date, dir)
18365     {
18366         if (!dir) {
18367             return date;
18368         }
18369         var new_date = new Date(date.valueOf()),
18370         day = new_date.getUTCDate(),
18371         month = new_date.getUTCMonth(),
18372         mag = Math.abs(dir),
18373         new_month, test;
18374         dir = dir > 0 ? 1 : -1;
18375         if (mag == 1){
18376             test = dir == -1
18377             // If going back one month, make sure month is not current month
18378             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18379             ? function(){
18380                 return new_date.getUTCMonth() == month;
18381             }
18382             // If going forward one month, make sure month is as expected
18383             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18384             : function(){
18385                 return new_date.getUTCMonth() != new_month;
18386             };
18387             new_month = month + dir;
18388             new_date.setUTCMonth(new_month);
18389             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18390             if (new_month < 0 || new_month > 11) {
18391                 new_month = (new_month + 12) % 12;
18392             }
18393         } else {
18394             // For magnitudes >1, move one month at a time...
18395             for (var i=0; i<mag; i++) {
18396                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18397                 new_date = this.moveMonth(new_date, dir);
18398             }
18399             // ...then reset the day, keeping it in the new month
18400             new_month = new_date.getUTCMonth();
18401             new_date.setUTCDate(day);
18402             test = function(){
18403                 return new_month != new_date.getUTCMonth();
18404             };
18405         }
18406         // Common date-resetting loop -- if date is beyond end of month, make it
18407         // end of month
18408         while (test()){
18409             new_date.setUTCDate(--day);
18410             new_date.setUTCMonth(new_month);
18411         }
18412         return new_date;
18413     },
18414
18415     moveYear: function(date, dir)
18416     {
18417         return this.moveMonth(date, dir*12);
18418     },
18419
18420     dateWithinRange: function(date)
18421     {
18422         return date >= this.startDate && date <= this.endDate;
18423     },
18424
18425     
18426     remove: function() 
18427     {
18428         this.picker().remove();
18429     },
18430     
18431     validateValue : function(value)
18432     {
18433         if(value.length < 1)  {
18434             if(this.allowBlank){
18435                 return true;
18436             }
18437             return false;
18438         }
18439         
18440         if(value.length < this.minLength){
18441             return false;
18442         }
18443         if(value.length > this.maxLength){
18444             return false;
18445         }
18446         if(this.vtype){
18447             var vt = Roo.form.VTypes;
18448             if(!vt[this.vtype](value, this)){
18449                 return false;
18450             }
18451         }
18452         if(typeof this.validator == "function"){
18453             var msg = this.validator(value);
18454             if(msg !== true){
18455                 return false;
18456             }
18457         }
18458         
18459         if(this.regex && !this.regex.test(value)){
18460             return false;
18461         }
18462         
18463         if(typeof(this.parseDate(value)) == 'undefined'){
18464             return false;
18465         }
18466         
18467         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18468             return false;
18469         }      
18470         
18471         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18472             return false;
18473         } 
18474         
18475         
18476         return true;
18477     }
18478    
18479 });
18480
18481 Roo.apply(Roo.bootstrap.DateField,  {
18482     
18483     head : {
18484         tag: 'thead',
18485         cn: [
18486         {
18487             tag: 'tr',
18488             cn: [
18489             {
18490                 tag: 'th',
18491                 cls: 'prev',
18492                 html: '<i class="fa fa-arrow-left"/>'
18493             },
18494             {
18495                 tag: 'th',
18496                 cls: 'switch',
18497                 colspan: '5'
18498             },
18499             {
18500                 tag: 'th',
18501                 cls: 'next',
18502                 html: '<i class="fa fa-arrow-right"/>'
18503             }
18504
18505             ]
18506         }
18507         ]
18508     },
18509     
18510     content : {
18511         tag: 'tbody',
18512         cn: [
18513         {
18514             tag: 'tr',
18515             cn: [
18516             {
18517                 tag: 'td',
18518                 colspan: '7'
18519             }
18520             ]
18521         }
18522         ]
18523     },
18524     
18525     footer : {
18526         tag: 'tfoot',
18527         cn: [
18528         {
18529             tag: 'tr',
18530             cn: [
18531             {
18532                 tag: 'th',
18533                 colspan: '7',
18534                 cls: 'today'
18535             }
18536                     
18537             ]
18538         }
18539         ]
18540     },
18541     
18542     dates:{
18543         en: {
18544             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18545             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18546             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18547             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18548             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18549             today: "Today"
18550         }
18551     },
18552     
18553     modes: [
18554     {
18555         clsName: 'days',
18556         navFnc: 'Month',
18557         navStep: 1
18558     },
18559     {
18560         clsName: 'months',
18561         navFnc: 'FullYear',
18562         navStep: 1
18563     },
18564     {
18565         clsName: 'years',
18566         navFnc: 'FullYear',
18567         navStep: 10
18568     }]
18569 });
18570
18571 Roo.apply(Roo.bootstrap.DateField,  {
18572   
18573     template : {
18574         tag: 'div',
18575         cls: 'datepicker dropdown-menu roo-dynamic',
18576         cn: [
18577         {
18578             tag: 'div',
18579             cls: 'datepicker-days',
18580             cn: [
18581             {
18582                 tag: 'table',
18583                 cls: 'table-condensed',
18584                 cn:[
18585                 Roo.bootstrap.DateField.head,
18586                 {
18587                     tag: 'tbody'
18588                 },
18589                 Roo.bootstrap.DateField.footer
18590                 ]
18591             }
18592             ]
18593         },
18594         {
18595             tag: 'div',
18596             cls: 'datepicker-months',
18597             cn: [
18598             {
18599                 tag: 'table',
18600                 cls: 'table-condensed',
18601                 cn:[
18602                 Roo.bootstrap.DateField.head,
18603                 Roo.bootstrap.DateField.content,
18604                 Roo.bootstrap.DateField.footer
18605                 ]
18606             }
18607             ]
18608         },
18609         {
18610             tag: 'div',
18611             cls: 'datepicker-years',
18612             cn: [
18613             {
18614                 tag: 'table',
18615                 cls: 'table-condensed',
18616                 cn:[
18617                 Roo.bootstrap.DateField.head,
18618                 Roo.bootstrap.DateField.content,
18619                 Roo.bootstrap.DateField.footer
18620                 ]
18621             }
18622             ]
18623         }
18624         ]
18625     }
18626 });
18627
18628  
18629
18630  /*
18631  * - LGPL
18632  *
18633  * TimeField
18634  * 
18635  */
18636
18637 /**
18638  * @class Roo.bootstrap.TimeField
18639  * @extends Roo.bootstrap.Input
18640  * Bootstrap DateField class
18641  * 
18642  * 
18643  * @constructor
18644  * Create a new TimeField
18645  * @param {Object} config The config object
18646  */
18647
18648 Roo.bootstrap.TimeField = function(config){
18649     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18650     this.addEvents({
18651             /**
18652              * @event show
18653              * Fires when this field show.
18654              * @param {Roo.bootstrap.DateField} thisthis
18655              * @param {Mixed} date The date value
18656              */
18657             show : true,
18658             /**
18659              * @event show
18660              * Fires when this field hide.
18661              * @param {Roo.bootstrap.DateField} this
18662              * @param {Mixed} date The date value
18663              */
18664             hide : true,
18665             /**
18666              * @event select
18667              * Fires when select a date.
18668              * @param {Roo.bootstrap.DateField} this
18669              * @param {Mixed} date The date value
18670              */
18671             select : true
18672         });
18673 };
18674
18675 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18676     
18677     /**
18678      * @cfg {String} format
18679      * The default time format string which can be overriden for localization support.  The format must be
18680      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18681      */
18682     format : "H:i",
18683        
18684     onRender: function(ct, position)
18685     {
18686         
18687         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18688                 
18689         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18690         
18691         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18692         
18693         this.pop = this.picker().select('>.datepicker-time',true).first();
18694         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18695         
18696         this.picker().on('mousedown', this.onMousedown, this);
18697         this.picker().on('click', this.onClick, this);
18698         
18699         this.picker().addClass('datepicker-dropdown');
18700     
18701         this.fillTime();
18702         this.update();
18703             
18704         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18705         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18706         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18707         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18708         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18709         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18710
18711     },
18712     
18713     fireKey: function(e){
18714         if (!this.picker().isVisible()){
18715             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18716                 this.show();
18717             }
18718             return;
18719         }
18720
18721         e.preventDefault();
18722         
18723         switch(e.keyCode){
18724             case 27: // escape
18725                 this.hide();
18726                 break;
18727             case 37: // left
18728             case 39: // right
18729                 this.onTogglePeriod();
18730                 break;
18731             case 38: // up
18732                 this.onIncrementMinutes();
18733                 break;
18734             case 40: // down
18735                 this.onDecrementMinutes();
18736                 break;
18737             case 13: // enter
18738             case 9: // tab
18739                 this.setTime();
18740                 break;
18741         }
18742     },
18743     
18744     onClick: function(e) {
18745         e.stopPropagation();
18746         e.preventDefault();
18747     },
18748     
18749     picker : function()
18750     {
18751         return this.el.select('.datepicker', true).first();
18752     },
18753     
18754     fillTime: function()
18755     {    
18756         var time = this.pop.select('tbody', true).first();
18757         
18758         time.dom.innerHTML = '';
18759         
18760         time.createChild({
18761             tag: 'tr',
18762             cn: [
18763                 {
18764                     tag: 'td',
18765                     cn: [
18766                         {
18767                             tag: 'a',
18768                             href: '#',
18769                             cls: 'btn',
18770                             cn: [
18771                                 {
18772                                     tag: 'span',
18773                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18774                                 }
18775                             ]
18776                         } 
18777                     ]
18778                 },
18779                 {
18780                     tag: 'td',
18781                     cls: 'separator'
18782                 },
18783                 {
18784                     tag: 'td',
18785                     cn: [
18786                         {
18787                             tag: 'a',
18788                             href: '#',
18789                             cls: 'btn',
18790                             cn: [
18791                                 {
18792                                     tag: 'span',
18793                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18794                                 }
18795                             ]
18796                         }
18797                     ]
18798                 },
18799                 {
18800                     tag: 'td',
18801                     cls: 'separator'
18802                 }
18803             ]
18804         });
18805         
18806         time.createChild({
18807             tag: 'tr',
18808             cn: [
18809                 {
18810                     tag: 'td',
18811                     cn: [
18812                         {
18813                             tag: 'span',
18814                             cls: 'timepicker-hour',
18815                             html: '00'
18816                         }  
18817                     ]
18818                 },
18819                 {
18820                     tag: 'td',
18821                     cls: 'separator',
18822                     html: ':'
18823                 },
18824                 {
18825                     tag: 'td',
18826                     cn: [
18827                         {
18828                             tag: 'span',
18829                             cls: 'timepicker-minute',
18830                             html: '00'
18831                         }  
18832                     ]
18833                 },
18834                 {
18835                     tag: 'td',
18836                     cls: 'separator'
18837                 },
18838                 {
18839                     tag: 'td',
18840                     cn: [
18841                         {
18842                             tag: 'button',
18843                             type: 'button',
18844                             cls: 'btn btn-primary period',
18845                             html: 'AM'
18846                             
18847                         }
18848                     ]
18849                 }
18850             ]
18851         });
18852         
18853         time.createChild({
18854             tag: 'tr',
18855             cn: [
18856                 {
18857                     tag: 'td',
18858                     cn: [
18859                         {
18860                             tag: 'a',
18861                             href: '#',
18862                             cls: 'btn',
18863                             cn: [
18864                                 {
18865                                     tag: 'span',
18866                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18867                                 }
18868                             ]
18869                         }
18870                     ]
18871                 },
18872                 {
18873                     tag: 'td',
18874                     cls: 'separator'
18875                 },
18876                 {
18877                     tag: 'td',
18878                     cn: [
18879                         {
18880                             tag: 'a',
18881                             href: '#',
18882                             cls: 'btn',
18883                             cn: [
18884                                 {
18885                                     tag: 'span',
18886                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18887                                 }
18888                             ]
18889                         }
18890                     ]
18891                 },
18892                 {
18893                     tag: 'td',
18894                     cls: 'separator'
18895                 }
18896             ]
18897         });
18898         
18899     },
18900     
18901     update: function()
18902     {
18903         
18904         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18905         
18906         this.fill();
18907     },
18908     
18909     fill: function() 
18910     {
18911         var hours = this.time.getHours();
18912         var minutes = this.time.getMinutes();
18913         var period = 'AM';
18914         
18915         if(hours > 11){
18916             period = 'PM';
18917         }
18918         
18919         if(hours == 0){
18920             hours = 12;
18921         }
18922         
18923         
18924         if(hours > 12){
18925             hours = hours - 12;
18926         }
18927         
18928         if(hours < 10){
18929             hours = '0' + hours;
18930         }
18931         
18932         if(minutes < 10){
18933             minutes = '0' + minutes;
18934         }
18935         
18936         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18937         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18938         this.pop.select('button', true).first().dom.innerHTML = period;
18939         
18940     },
18941     
18942     place: function()
18943     {   
18944         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18945         
18946         var cls = ['bottom'];
18947         
18948         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18949             cls.pop();
18950             cls.push('top');
18951         }
18952         
18953         cls.push('right');
18954         
18955         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18956             cls.pop();
18957             cls.push('left');
18958         }
18959         
18960         this.picker().addClass(cls.join('-'));
18961         
18962         var _this = this;
18963         
18964         Roo.each(cls, function(c){
18965             if(c == 'bottom'){
18966                 _this.picker().setTop(_this.inputEl().getHeight());
18967                 return;
18968             }
18969             if(c == 'top'){
18970                 _this.picker().setTop(0 - _this.picker().getHeight());
18971                 return;
18972             }
18973             
18974             if(c == 'left'){
18975                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18976                 return;
18977             }
18978             if(c == 'right'){
18979                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18980                 return;
18981             }
18982         });
18983         
18984     },
18985   
18986     onFocus : function()
18987     {
18988         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18989         this.show();
18990     },
18991     
18992     onBlur : function()
18993     {
18994         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18995         this.hide();
18996     },
18997     
18998     show : function()
18999     {
19000         this.picker().show();
19001         this.pop.show();
19002         this.update();
19003         this.place();
19004         
19005         this.fireEvent('show', this, this.date);
19006     },
19007     
19008     hide : function()
19009     {
19010         this.picker().hide();
19011         this.pop.hide();
19012         
19013         this.fireEvent('hide', this, this.date);
19014     },
19015     
19016     setTime : function()
19017     {
19018         this.hide();
19019         this.setValue(this.time.format(this.format));
19020         
19021         this.fireEvent('select', this, this.date);
19022         
19023         
19024     },
19025     
19026     onMousedown: function(e){
19027         e.stopPropagation();
19028         e.preventDefault();
19029     },
19030     
19031     onIncrementHours: function()
19032     {
19033         Roo.log('onIncrementHours');
19034         this.time = this.time.add(Date.HOUR, 1);
19035         this.update();
19036         
19037     },
19038     
19039     onDecrementHours: function()
19040     {
19041         Roo.log('onDecrementHours');
19042         this.time = this.time.add(Date.HOUR, -1);
19043         this.update();
19044     },
19045     
19046     onIncrementMinutes: function()
19047     {
19048         Roo.log('onIncrementMinutes');
19049         this.time = this.time.add(Date.MINUTE, 1);
19050         this.update();
19051     },
19052     
19053     onDecrementMinutes: function()
19054     {
19055         Roo.log('onDecrementMinutes');
19056         this.time = this.time.add(Date.MINUTE, -1);
19057         this.update();
19058     },
19059     
19060     onTogglePeriod: function()
19061     {
19062         Roo.log('onTogglePeriod');
19063         this.time = this.time.add(Date.HOUR, 12);
19064         this.update();
19065     }
19066     
19067    
19068 });
19069
19070 Roo.apply(Roo.bootstrap.TimeField,  {
19071     
19072     content : {
19073         tag: 'tbody',
19074         cn: [
19075             {
19076                 tag: 'tr',
19077                 cn: [
19078                 {
19079                     tag: 'td',
19080                     colspan: '7'
19081                 }
19082                 ]
19083             }
19084         ]
19085     },
19086     
19087     footer : {
19088         tag: 'tfoot',
19089         cn: [
19090             {
19091                 tag: 'tr',
19092                 cn: [
19093                 {
19094                     tag: 'th',
19095                     colspan: '7',
19096                     cls: '',
19097                     cn: [
19098                         {
19099                             tag: 'button',
19100                             cls: 'btn btn-info ok',
19101                             html: 'OK'
19102                         }
19103                     ]
19104                 }
19105
19106                 ]
19107             }
19108         ]
19109     }
19110 });
19111
19112 Roo.apply(Roo.bootstrap.TimeField,  {
19113   
19114     template : {
19115         tag: 'div',
19116         cls: 'datepicker dropdown-menu',
19117         cn: [
19118             {
19119                 tag: 'div',
19120                 cls: 'datepicker-time',
19121                 cn: [
19122                 {
19123                     tag: 'table',
19124                     cls: 'table-condensed',
19125                     cn:[
19126                     Roo.bootstrap.TimeField.content,
19127                     Roo.bootstrap.TimeField.footer
19128                     ]
19129                 }
19130                 ]
19131             }
19132         ]
19133     }
19134 });
19135
19136  
19137
19138  /*
19139  * - LGPL
19140  *
19141  * MonthField
19142  * 
19143  */
19144
19145 /**
19146  * @class Roo.bootstrap.MonthField
19147  * @extends Roo.bootstrap.Input
19148  * Bootstrap MonthField class
19149  * 
19150  * @cfg {String} language default en
19151  * 
19152  * @constructor
19153  * Create a new MonthField
19154  * @param {Object} config The config object
19155  */
19156
19157 Roo.bootstrap.MonthField = function(config){
19158     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19159     
19160     this.addEvents({
19161         /**
19162          * @event show
19163          * Fires when this field show.
19164          * @param {Roo.bootstrap.MonthField} this
19165          * @param {Mixed} date The date value
19166          */
19167         show : true,
19168         /**
19169          * @event show
19170          * Fires when this field hide.
19171          * @param {Roo.bootstrap.MonthField} this
19172          * @param {Mixed} date The date value
19173          */
19174         hide : true,
19175         /**
19176          * @event select
19177          * Fires when select a date.
19178          * @param {Roo.bootstrap.MonthField} this
19179          * @param {String} oldvalue The old value
19180          * @param {String} newvalue The new value
19181          */
19182         select : true
19183     });
19184 };
19185
19186 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19187     
19188     onRender: function(ct, position)
19189     {
19190         
19191         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19192         
19193         this.language = this.language || 'en';
19194         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19195         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19196         
19197         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19198         this.isInline = false;
19199         this.isInput = true;
19200         this.component = this.el.select('.add-on', true).first() || false;
19201         this.component = (this.component && this.component.length === 0) ? false : this.component;
19202         this.hasInput = this.component && this.inputEL().length;
19203         
19204         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19205         
19206         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19207         
19208         this.picker().on('mousedown', this.onMousedown, this);
19209         this.picker().on('click', this.onClick, this);
19210         
19211         this.picker().addClass('datepicker-dropdown');
19212         
19213         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19214             v.setStyle('width', '189px');
19215         });
19216         
19217         this.fillMonths();
19218         
19219         this.update();
19220         
19221         if(this.isInline) {
19222             this.show();
19223         }
19224         
19225     },
19226     
19227     setValue: function(v, suppressEvent)
19228     {   
19229         var o = this.getValue();
19230         
19231         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19232         
19233         this.update();
19234
19235         if(suppressEvent !== true){
19236             this.fireEvent('select', this, o, v);
19237         }
19238         
19239     },
19240     
19241     getValue: function()
19242     {
19243         return this.value;
19244     },
19245     
19246     onClick: function(e) 
19247     {
19248         e.stopPropagation();
19249         e.preventDefault();
19250         
19251         var target = e.getTarget();
19252         
19253         if(target.nodeName.toLowerCase() === 'i'){
19254             target = Roo.get(target).dom.parentNode;
19255         }
19256         
19257         var nodeName = target.nodeName;
19258         var className = target.className;
19259         var html = target.innerHTML;
19260         
19261         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19262             return;
19263         }
19264         
19265         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19266         
19267         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19268         
19269         this.hide();
19270                         
19271     },
19272     
19273     picker : function()
19274     {
19275         return this.pickerEl;
19276     },
19277     
19278     fillMonths: function()
19279     {    
19280         var i = 0;
19281         var months = this.picker().select('>.datepicker-months td', true).first();
19282         
19283         months.dom.innerHTML = '';
19284         
19285         while (i < 12) {
19286             var month = {
19287                 tag: 'span',
19288                 cls: 'month',
19289                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19290             };
19291             
19292             months.createChild(month);
19293         }
19294         
19295     },
19296     
19297     update: function()
19298     {
19299         var _this = this;
19300         
19301         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19302             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19303         }
19304         
19305         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19306             e.removeClass('active');
19307             
19308             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19309                 e.addClass('active');
19310             }
19311         })
19312     },
19313     
19314     place: function()
19315     {
19316         if(this.isInline) {
19317             return;
19318         }
19319         
19320         this.picker().removeClass(['bottom', 'top']);
19321         
19322         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19323             /*
19324              * place to the top of element!
19325              *
19326              */
19327             
19328             this.picker().addClass('top');
19329             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19330             
19331             return;
19332         }
19333         
19334         this.picker().addClass('bottom');
19335         
19336         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19337     },
19338     
19339     onFocus : function()
19340     {
19341         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19342         this.show();
19343     },
19344     
19345     onBlur : function()
19346     {
19347         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19348         
19349         var d = this.inputEl().getValue();
19350         
19351         this.setValue(d);
19352                 
19353         this.hide();
19354     },
19355     
19356     show : function()
19357     {
19358         this.picker().show();
19359         this.picker().select('>.datepicker-months', true).first().show();
19360         this.update();
19361         this.place();
19362         
19363         this.fireEvent('show', this, this.date);
19364     },
19365     
19366     hide : function()
19367     {
19368         if(this.isInline) {
19369             return;
19370         }
19371         this.picker().hide();
19372         this.fireEvent('hide', this, this.date);
19373         
19374     },
19375     
19376     onMousedown: function(e)
19377     {
19378         e.stopPropagation();
19379         e.preventDefault();
19380     },
19381     
19382     keyup: function(e)
19383     {
19384         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19385         this.update();
19386     },
19387
19388     fireKey: function(e)
19389     {
19390         if (!this.picker().isVisible()){
19391             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19392                 this.show();
19393             }
19394             return;
19395         }
19396         
19397         var dir;
19398         
19399         switch(e.keyCode){
19400             case 27: // escape
19401                 this.hide();
19402                 e.preventDefault();
19403                 break;
19404             case 37: // left
19405             case 39: // right
19406                 dir = e.keyCode == 37 ? -1 : 1;
19407                 
19408                 this.vIndex = this.vIndex + dir;
19409                 
19410                 if(this.vIndex < 0){
19411                     this.vIndex = 0;
19412                 }
19413                 
19414                 if(this.vIndex > 11){
19415                     this.vIndex = 11;
19416                 }
19417                 
19418                 if(isNaN(this.vIndex)){
19419                     this.vIndex = 0;
19420                 }
19421                 
19422                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19423                 
19424                 break;
19425             case 38: // up
19426             case 40: // down
19427                 
19428                 dir = e.keyCode == 38 ? -1 : 1;
19429                 
19430                 this.vIndex = this.vIndex + dir * 4;
19431                 
19432                 if(this.vIndex < 0){
19433                     this.vIndex = 0;
19434                 }
19435                 
19436                 if(this.vIndex > 11){
19437                     this.vIndex = 11;
19438                 }
19439                 
19440                 if(isNaN(this.vIndex)){
19441                     this.vIndex = 0;
19442                 }
19443                 
19444                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19445                 break;
19446                 
19447             case 13: // enter
19448                 
19449                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19450                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19451                 }
19452                 
19453                 this.hide();
19454                 e.preventDefault();
19455                 break;
19456             case 9: // tab
19457                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19458                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19459                 }
19460                 this.hide();
19461                 break;
19462             case 16: // shift
19463             case 17: // ctrl
19464             case 18: // alt
19465                 break;
19466             default :
19467                 this.hide();
19468                 
19469         }
19470     },
19471     
19472     remove: function() 
19473     {
19474         this.picker().remove();
19475     }
19476    
19477 });
19478
19479 Roo.apply(Roo.bootstrap.MonthField,  {
19480     
19481     content : {
19482         tag: 'tbody',
19483         cn: [
19484         {
19485             tag: 'tr',
19486             cn: [
19487             {
19488                 tag: 'td',
19489                 colspan: '7'
19490             }
19491             ]
19492         }
19493         ]
19494     },
19495     
19496     dates:{
19497         en: {
19498             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19499             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19500         }
19501     }
19502 });
19503
19504 Roo.apply(Roo.bootstrap.MonthField,  {
19505   
19506     template : {
19507         tag: 'div',
19508         cls: 'datepicker dropdown-menu roo-dynamic',
19509         cn: [
19510             {
19511                 tag: 'div',
19512                 cls: 'datepicker-months',
19513                 cn: [
19514                 {
19515                     tag: 'table',
19516                     cls: 'table-condensed',
19517                     cn:[
19518                         Roo.bootstrap.DateField.content
19519                     ]
19520                 }
19521                 ]
19522             }
19523         ]
19524     }
19525 });
19526
19527  
19528
19529  
19530  /*
19531  * - LGPL
19532  *
19533  * CheckBox
19534  * 
19535  */
19536
19537 /**
19538  * @class Roo.bootstrap.CheckBox
19539  * @extends Roo.bootstrap.Input
19540  * Bootstrap CheckBox class
19541  * 
19542  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19543  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19544  * @cfg {String} boxLabel The text that appears beside the checkbox
19545  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19546  * @cfg {Boolean} checked initnal the element
19547  * @cfg {Boolean} inline inline the element (default false)
19548  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19549  * 
19550  * @constructor
19551  * Create a new CheckBox
19552  * @param {Object} config The config object
19553  */
19554
19555 Roo.bootstrap.CheckBox = function(config){
19556     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19557    
19558     this.addEvents({
19559         /**
19560         * @event check
19561         * Fires when the element is checked or unchecked.
19562         * @param {Roo.bootstrap.CheckBox} this This input
19563         * @param {Boolean} checked The new checked value
19564         */
19565        check : true
19566     });
19567     
19568 };
19569
19570 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19571   
19572     inputType: 'checkbox',
19573     inputValue: 1,
19574     valueOff: 0,
19575     boxLabel: false,
19576     checked: false,
19577     weight : false,
19578     inline: false,
19579     
19580     getAutoCreate : function()
19581     {
19582         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19583         
19584         var id = Roo.id();
19585         
19586         var cfg = {};
19587         
19588         cfg.cls = 'form-group ' + this.inputType; //input-group
19589         
19590         if(this.inline){
19591             cfg.cls += ' ' + this.inputType + '-inline';
19592         }
19593         
19594         var input =  {
19595             tag: 'input',
19596             id : id,
19597             type : this.inputType,
19598             value : this.inputValue,
19599             cls : 'roo-' + this.inputType, //'form-box',
19600             placeholder : this.placeholder || ''
19601             
19602         };
19603         
19604         if(this.inputType != 'radio'){
19605             var hidden =  {
19606                 tag: 'input',
19607                 type : 'hidden',
19608                 cls : 'roo-hidden-value',
19609                 value : this.checked ? this.valueOff : this.inputValue
19610             };
19611         }
19612         
19613             
19614         if (this.weight) { // Validity check?
19615             cfg.cls += " " + this.inputType + "-" + this.weight;
19616         }
19617         
19618         if (this.disabled) {
19619             input.disabled=true;
19620         }
19621         
19622         if(this.checked){
19623             input.checked = this.checked;
19624             
19625         }
19626         
19627         
19628         if (this.name) {
19629             
19630             input.name = this.name;
19631             
19632             if(this.inputType != 'radio'){
19633                 hidden.name = this.name;
19634                 input.name = '_hidden_' + this.name;
19635             }
19636         }
19637         
19638         if (this.size) {
19639             input.cls += ' input-' + this.size;
19640         }
19641         
19642         var settings=this;
19643         
19644         ['xs','sm','md','lg'].map(function(size){
19645             if (settings[size]) {
19646                 cfg.cls += ' col-' + size + '-' + settings[size];
19647             }
19648         });
19649         
19650         var inputblock = input;
19651          
19652         if (this.before || this.after) {
19653             
19654             inputblock = {
19655                 cls : 'input-group',
19656                 cn :  [] 
19657             };
19658             
19659             if (this.before) {
19660                 inputblock.cn.push({
19661                     tag :'span',
19662                     cls : 'input-group-addon',
19663                     html : this.before
19664                 });
19665             }
19666             
19667             inputblock.cn.push(input);
19668             
19669             if(this.inputType != 'radio'){
19670                 inputblock.cn.push(hidden);
19671             }
19672             
19673             if (this.after) {
19674                 inputblock.cn.push({
19675                     tag :'span',
19676                     cls : 'input-group-addon',
19677                     html : this.after
19678                 });
19679             }
19680             
19681         }
19682         
19683         if (align ==='left' && this.fieldLabel.length) {
19684 //                Roo.log("left and has label");
19685                 cfg.cn = [
19686                     
19687                     {
19688                         tag: 'label',
19689                         'for' :  id,
19690                         cls : 'control-label col-md-' + this.labelWidth,
19691                         html : this.fieldLabel
19692                         
19693                     },
19694                     {
19695                         cls : "col-md-" + (12 - this.labelWidth), 
19696                         cn: [
19697                             inputblock
19698                         ]
19699                     }
19700                     
19701                 ];
19702         } else if ( this.fieldLabel.length) {
19703 //                Roo.log(" label");
19704                 cfg.cn = [
19705                    
19706                     {
19707                         tag: this.boxLabel ? 'span' : 'label',
19708                         'for': id,
19709                         cls: 'control-label box-input-label',
19710                         //cls : 'input-group-addon',
19711                         html : this.fieldLabel
19712                         
19713                     },
19714                     
19715                     inputblock
19716                     
19717                 ];
19718
19719         } else {
19720             
19721 //                Roo.log(" no label && no align");
19722                 cfg.cn = [  inputblock ] ;
19723                 
19724                 
19725         }
19726         
19727         if(this.boxLabel){
19728              var boxLabelCfg = {
19729                 tag: 'label',
19730                 //'for': id, // box label is handled by onclick - so no for...
19731                 cls: 'box-label',
19732                 html: this.boxLabel
19733             };
19734             
19735             if(this.tooltip){
19736                 boxLabelCfg.tooltip = this.tooltip;
19737             }
19738              
19739             cfg.cn.push(boxLabelCfg);
19740         }
19741         
19742         if(this.inputType != 'radio'){
19743             cfg.cn.push(hidden);
19744         }
19745         
19746         return cfg;
19747         
19748     },
19749     
19750     /**
19751      * return the real input element.
19752      */
19753     inputEl: function ()
19754     {
19755         return this.el.select('input.roo-' + this.inputType,true).first();
19756     },
19757     hiddenEl: function ()
19758     {
19759         return this.el.select('input.roo-hidden-value',true).first();
19760     },
19761     
19762     labelEl: function()
19763     {
19764         return this.el.select('label.control-label',true).first();
19765     },
19766     /* depricated... */
19767     
19768     label: function()
19769     {
19770         return this.labelEl();
19771     },
19772     
19773     boxLabelEl: function()
19774     {
19775         return this.el.select('label.box-label',true).first();
19776     },
19777     
19778     initEvents : function()
19779     {
19780 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19781         
19782         this.inputEl().on('click', this.onClick,  this);
19783         
19784         if (this.boxLabel) { 
19785             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19786         }
19787         
19788         this.startValue = this.getValue();
19789         
19790         if(this.groupId){
19791             Roo.bootstrap.CheckBox.register(this);
19792         }
19793     },
19794     
19795     onClick : function()
19796     {   
19797         this.setChecked(!this.checked);
19798     },
19799     
19800     setChecked : function(state,suppressEvent)
19801     {
19802         this.startValue = this.getValue();
19803
19804         if(this.inputType == 'radio'){
19805             
19806             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19807                 e.dom.checked = false;
19808             });
19809             
19810             this.inputEl().dom.checked = true;
19811             
19812             this.inputEl().dom.value = this.inputValue;
19813             
19814             if(suppressEvent !== true){
19815                 this.fireEvent('check', this, true);
19816             }
19817             
19818             this.validate();
19819             
19820             return;
19821         }
19822         
19823         this.checked = state;
19824         
19825         this.inputEl().dom.checked = state;
19826         
19827         
19828         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19829         
19830         if(suppressEvent !== true){
19831             this.fireEvent('check', this, state);
19832         }
19833         
19834         this.validate();
19835     },
19836     
19837     getValue : function()
19838     {
19839         if(this.inputType == 'radio'){
19840             return this.getGroupValue();
19841         }
19842         
19843         return this.hiddenEl().dom.value;
19844         
19845     },
19846     
19847     getGroupValue : function()
19848     {
19849         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19850             return '';
19851         }
19852         
19853         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19854     },
19855     
19856     setValue : function(v,suppressEvent)
19857     {
19858         if(this.inputType == 'radio'){
19859             this.setGroupValue(v, suppressEvent);
19860             return;
19861         }
19862         
19863         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19864         
19865         this.validate();
19866     },
19867     
19868     setGroupValue : function(v, suppressEvent)
19869     {
19870         this.startValue = this.getValue();
19871         
19872         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19873             e.dom.checked = false;
19874             
19875             if(e.dom.value == v){
19876                 e.dom.checked = true;
19877             }
19878         });
19879         
19880         if(suppressEvent !== true){
19881             this.fireEvent('check', this, true);
19882         }
19883
19884         this.validate();
19885         
19886         return;
19887     },
19888     
19889     validate : function()
19890     {
19891         if(
19892                 this.disabled || 
19893                 (this.inputType == 'radio' && this.validateRadio()) ||
19894                 (this.inputType == 'checkbox' && this.validateCheckbox())
19895         ){
19896             this.markValid();
19897             return true;
19898         }
19899         
19900         this.markInvalid();
19901         return false;
19902     },
19903     
19904     validateRadio : function()
19905     {
19906         if(this.allowBlank){
19907             return true;
19908         }
19909         
19910         var valid = false;
19911         
19912         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19913             if(!e.dom.checked){
19914                 return;
19915             }
19916             
19917             valid = true;
19918             
19919             return false;
19920         });
19921         
19922         return valid;
19923     },
19924     
19925     validateCheckbox : function()
19926     {
19927         if(!this.groupId){
19928             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19929         }
19930         
19931         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19932         
19933         if(!group){
19934             return false;
19935         }
19936         
19937         var r = false;
19938         
19939         for(var i in group){
19940             if(r){
19941                 break;
19942             }
19943             
19944             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19945         }
19946         
19947         return r;
19948     },
19949     
19950     /**
19951      * Mark this field as valid
19952      */
19953     markValid : function()
19954     {
19955         var _this = this;
19956         
19957         this.fireEvent('valid', this);
19958         
19959         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19960         
19961         if(this.groupId){
19962             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19963         }
19964         
19965         if(label){
19966             label.markValid();
19967         }
19968
19969         if(this.inputType == 'radio'){
19970             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19971                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19972                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19973             });
19974             
19975             return;
19976         }
19977         
19978         if(!this.groupId){
19979             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19980             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19981             return;
19982         }
19983         
19984         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19985             
19986         if(!group){
19987             return;
19988         }
19989         
19990         for(var i in group){
19991             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19992             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19993         }
19994     },
19995     
19996      /**
19997      * Mark this field as invalid
19998      * @param {String} msg The validation message
19999      */
20000     markInvalid : function(msg)
20001     {
20002         if(this.allowBlank){
20003             return;
20004         }
20005         
20006         var _this = this;
20007         
20008         this.fireEvent('invalid', this, msg);
20009         
20010         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20011         
20012         if(this.groupId){
20013             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20014         }
20015         
20016         if(label){
20017             label.markInvalid();
20018         }
20019             
20020         if(this.inputType == 'radio'){
20021             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20022                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20023                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20024             });
20025             
20026             return;
20027         }
20028         
20029         if(!this.groupId){
20030             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20031             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20032             return;
20033         }
20034         
20035         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20036         
20037         if(!group){
20038             return;
20039         }
20040         
20041         for(var i in group){
20042             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20043             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20044         }
20045         
20046     },
20047     
20048     clearInvalid : function()
20049     {
20050         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20051         
20052         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20053         
20054         if (label) {
20055             label.iconEl.removeClass(label.validClass);
20056             label.iconEl.removeClass(label.invalidClass);
20057         }
20058     },
20059     
20060     disable : function()
20061     {
20062         if(this.inputType != 'radio'){
20063             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20064             return;
20065         }
20066         
20067         var _this = this;
20068         
20069         if(this.rendered){
20070             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20071                 _this.getActionEl().addClass(this.disabledClass);
20072                 e.dom.disabled = true;
20073             });
20074         }
20075         
20076         this.disabled = true;
20077         this.fireEvent("disable", this);
20078         return this;
20079     },
20080
20081     enable : function()
20082     {
20083         if(this.inputType != 'radio'){
20084             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20085             return;
20086         }
20087         
20088         var _this = this;
20089         
20090         if(this.rendered){
20091             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20092                 _this.getActionEl().removeClass(this.disabledClass);
20093                 e.dom.disabled = false;
20094             });
20095         }
20096         
20097         this.disabled = false;
20098         this.fireEvent("enable", this);
20099         return this;
20100     }
20101
20102 });
20103
20104 Roo.apply(Roo.bootstrap.CheckBox, {
20105     
20106     groups: {},
20107     
20108      /**
20109     * register a CheckBox Group
20110     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20111     */
20112     register : function(checkbox)
20113     {
20114         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20115             this.groups[checkbox.groupId] = {};
20116         }
20117         
20118         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20119             return;
20120         }
20121         
20122         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20123         
20124     },
20125     /**
20126     * fetch a CheckBox Group based on the group ID
20127     * @param {string} the group ID
20128     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20129     */
20130     get: function(groupId) {
20131         if (typeof(this.groups[groupId]) == 'undefined') {
20132             return false;
20133         }
20134         
20135         return this.groups[groupId] ;
20136     }
20137     
20138     
20139 });
20140 /*
20141  * - LGPL
20142  *
20143  * Radio
20144  *
20145  *
20146  * not inline
20147  *<div class="radio">
20148   <label>
20149     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20150     Option one is this and that&mdash;be sure to include why it's great
20151   </label>
20152 </div>
20153  *
20154  *
20155  *inline
20156  *<span>
20157  *<label class="radio-inline">fieldLabel</label>
20158  *<label class="radio-inline">
20159   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20160 </label>
20161 <span>
20162  *
20163  *
20164  */
20165
20166 /**
20167  * @class Roo.bootstrap.Radio
20168  * @extends Roo.bootstrap.CheckBox
20169  * Bootstrap Radio class
20170
20171  * @constructor
20172  * Create a new Radio
20173  * @param {Object} config The config object
20174  */
20175
20176 Roo.bootstrap.Radio = function(config){
20177     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20178
20179 };
20180
20181 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20182
20183     inputType: 'radio',
20184     inputValue: '',
20185     valueOff: '',
20186
20187     getAutoCreate : function()
20188     {
20189         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20190         align = align || 'left'; // default...
20191
20192
20193
20194         var id = Roo.id();
20195
20196         var cfg = {
20197                 tag : this.inline ? 'span' : 'div',
20198                 cls : 'form-group',
20199                 cn : []
20200         };
20201
20202         var inline = this.inline ? ' radio-inline' : '';
20203
20204         var lbl = {
20205                 tag: 'label' ,
20206                 // does not need for, as we wrap the input with it..
20207                 'for' : id,
20208                 cls : 'control-label box-label' + inline,
20209                 cn : []
20210         };
20211         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20212
20213         var fieldLabel = {
20214             tag: 'label' ,
20215             //cls : 'control-label' + inline,
20216             html : this.fieldLabel,
20217             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20218         };
20219
20220         var input =  {
20221             tag: 'input',
20222             id : id,
20223             type : this.inputType,
20224             //value : (!this.checked) ? this.valueOff : this.inputValue,
20225             value : this.inputValue,
20226             cls : 'roo-radio',
20227             placeholder : this.placeholder || '' // ?? needed????
20228
20229         };
20230         if (this.weight) { // Validity check?
20231             input.cls += " radio-" + this.weight;
20232         }
20233         if (this.disabled) {
20234             input.disabled=true;
20235         }
20236
20237         if(this.checked){
20238             input.checked = this.checked;
20239         }
20240
20241         if (this.name) {
20242             input.name = this.name;
20243         }
20244
20245         if (this.size) {
20246             input.cls += ' input-' + this.size;
20247         }
20248
20249         //?? can span's inline have a width??
20250
20251         var settings=this;
20252         ['xs','sm','md','lg'].map(function(size){
20253             if (settings[size]) {
20254                 cfg.cls += ' col-' + size + '-' + settings[size];
20255             }
20256         });
20257
20258         var inputblock = input;
20259
20260         if (this.before || this.after) {
20261
20262             inputblock = {
20263                 cls : 'input-group',
20264                 tag : 'span',
20265                 cn :  []
20266             };
20267             if (this.before) {
20268                 inputblock.cn.push({
20269                     tag :'span',
20270                     cls : 'input-group-addon',
20271                     html : this.before
20272                 });
20273             }
20274             inputblock.cn.push(input);
20275             if (this.after) {
20276                 inputblock.cn.push({
20277                     tag :'span',
20278                     cls : 'input-group-addon',
20279                     html : this.after
20280                 });
20281             }
20282
20283         };
20284
20285
20286         if (this.fieldLabel && this.fieldLabel.length) {
20287             cfg.cn.push(fieldLabel);
20288         }
20289
20290         // normal bootstrap puts the input inside the label.
20291         // however with our styled version - it has to go after the input.
20292
20293         //lbl.cn.push(inputblock);
20294
20295         var lblwrap =  {
20296             tag: 'span',
20297             cls: 'radio' + inline,
20298             cn: [
20299                 inputblock,
20300                 lbl
20301             ]
20302         };
20303
20304         cfg.cn.push( lblwrap);
20305
20306         if(this.boxLabel){
20307             lbl.cn.push({
20308                 tag: 'span',
20309                 html: this.boxLabel
20310             })
20311         }
20312
20313
20314         return cfg;
20315
20316     },
20317
20318     initEvents : function()
20319     {
20320 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20321
20322         this.inputEl().on('click', this.onClick,  this);
20323         if (this.boxLabel) {
20324             //Roo.log('find label');
20325             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20326         }
20327
20328     },
20329
20330     inputEl: function ()
20331     {
20332         return this.el.select('input.roo-radio',true).first();
20333     },
20334     onClick : function()
20335     {
20336         Roo.log("click");
20337         this.setChecked(true);
20338     },
20339
20340     setChecked : function(state,suppressEvent)
20341     {
20342         if(state){
20343             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20344                 v.dom.checked = false;
20345             });
20346         }
20347         this.checked = state;
20348         this.inputEl().dom.checked = state;
20349
20350         if(suppressEvent !== true){
20351             this.fireEvent('check', this, state);
20352         }
20353         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20354         this.validate()
20355     },
20356
20357     getGroupValue : function()
20358     {
20359         var value = '';
20360         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20361             if(v.dom.checked == true){
20362                 value = v.dom.value;
20363             }
20364         });
20365
20366         return value;
20367     },
20368
20369     /**
20370      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20371      * @return {Mixed} value The field value
20372      */
20373     getValue : function(){
20374         return this.getGroupValue();
20375     }
20376
20377 });
20378 //<script type="text/javascript">
20379
20380 /*
20381  * Based  Ext JS Library 1.1.1
20382  * Copyright(c) 2006-2007, Ext JS, LLC.
20383  * LGPL
20384  *
20385  */
20386  
20387 /**
20388  * @class Roo.HtmlEditorCore
20389  * @extends Roo.Component
20390  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20391  *
20392  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20393  */
20394
20395 Roo.HtmlEditorCore = function(config){
20396     
20397     
20398     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20399     
20400     
20401     this.addEvents({
20402         /**
20403          * @event initialize
20404          * Fires when the editor is fully initialized (including the iframe)
20405          * @param {Roo.HtmlEditorCore} this
20406          */
20407         initialize: true,
20408         /**
20409          * @event activate
20410          * Fires when the editor is first receives the focus. Any insertion must wait
20411          * until after this event.
20412          * @param {Roo.HtmlEditorCore} this
20413          */
20414         activate: true,
20415          /**
20416          * @event beforesync
20417          * Fires before the textarea is updated with content from the editor iframe. Return false
20418          * to cancel the sync.
20419          * @param {Roo.HtmlEditorCore} this
20420          * @param {String} html
20421          */
20422         beforesync: true,
20423          /**
20424          * @event beforepush
20425          * Fires before the iframe editor is updated with content from the textarea. Return false
20426          * to cancel the push.
20427          * @param {Roo.HtmlEditorCore} this
20428          * @param {String} html
20429          */
20430         beforepush: true,
20431          /**
20432          * @event sync
20433          * Fires when the textarea is updated with content from the editor iframe.
20434          * @param {Roo.HtmlEditorCore} this
20435          * @param {String} html
20436          */
20437         sync: true,
20438          /**
20439          * @event push
20440          * Fires when the iframe editor is updated with content from the textarea.
20441          * @param {Roo.HtmlEditorCore} this
20442          * @param {String} html
20443          */
20444         push: true,
20445         
20446         /**
20447          * @event editorevent
20448          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20449          * @param {Roo.HtmlEditorCore} this
20450          */
20451         editorevent: true
20452         
20453     });
20454     
20455     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20456     
20457     // defaults : white / black...
20458     this.applyBlacklists();
20459     
20460     
20461     
20462 };
20463
20464
20465 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20466
20467
20468      /**
20469      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20470      */
20471     
20472     owner : false,
20473     
20474      /**
20475      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20476      *                        Roo.resizable.
20477      */
20478     resizable : false,
20479      /**
20480      * @cfg {Number} height (in pixels)
20481      */   
20482     height: 300,
20483    /**
20484      * @cfg {Number} width (in pixels)
20485      */   
20486     width: 500,
20487     
20488     /**
20489      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20490      * 
20491      */
20492     stylesheets: false,
20493     
20494     // id of frame..
20495     frameId: false,
20496     
20497     // private properties
20498     validationEvent : false,
20499     deferHeight: true,
20500     initialized : false,
20501     activated : false,
20502     sourceEditMode : false,
20503     onFocus : Roo.emptyFn,
20504     iframePad:3,
20505     hideMode:'offsets',
20506     
20507     clearUp: true,
20508     
20509     // blacklist + whitelisted elements..
20510     black: false,
20511     white: false,
20512      
20513     
20514
20515     /**
20516      * Protected method that will not generally be called directly. It
20517      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20518      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20519      */
20520     getDocMarkup : function(){
20521         // body styles..
20522         var st = '';
20523         
20524         // inherit styels from page...?? 
20525         if (this.stylesheets === false) {
20526             
20527             Roo.get(document.head).select('style').each(function(node) {
20528                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20529             });
20530             
20531             Roo.get(document.head).select('link').each(function(node) { 
20532                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20533             });
20534             
20535         } else if (!this.stylesheets.length) {
20536                 // simple..
20537                 st = '<style type="text/css">' +
20538                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20539                    '</style>';
20540         } else { 
20541             
20542         }
20543         
20544         st +=  '<style type="text/css">' +
20545             'IMG { cursor: pointer } ' +
20546         '</style>';
20547
20548         
20549         return '<html><head>' + st  +
20550             //<style type="text/css">' +
20551             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20552             //'</style>' +
20553             ' </head><body class="roo-htmleditor-body"></body></html>';
20554     },
20555
20556     // private
20557     onRender : function(ct, position)
20558     {
20559         var _t = this;
20560         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20561         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20562         
20563         
20564         this.el.dom.style.border = '0 none';
20565         this.el.dom.setAttribute('tabIndex', -1);
20566         this.el.addClass('x-hidden hide');
20567         
20568         
20569         
20570         if(Roo.isIE){ // fix IE 1px bogus margin
20571             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20572         }
20573        
20574         
20575         this.frameId = Roo.id();
20576         
20577          
20578         
20579         var iframe = this.owner.wrap.createChild({
20580             tag: 'iframe',
20581             cls: 'form-control', // bootstrap..
20582             id: this.frameId,
20583             name: this.frameId,
20584             frameBorder : 'no',
20585             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20586         }, this.el
20587         );
20588         
20589         
20590         this.iframe = iframe.dom;
20591
20592          this.assignDocWin();
20593         
20594         this.doc.designMode = 'on';
20595        
20596         this.doc.open();
20597         this.doc.write(this.getDocMarkup());
20598         this.doc.close();
20599
20600         
20601         var task = { // must defer to wait for browser to be ready
20602             run : function(){
20603                 //console.log("run task?" + this.doc.readyState);
20604                 this.assignDocWin();
20605                 if(this.doc.body || this.doc.readyState == 'complete'){
20606                     try {
20607                         this.doc.designMode="on";
20608                     } catch (e) {
20609                         return;
20610                     }
20611                     Roo.TaskMgr.stop(task);
20612                     this.initEditor.defer(10, this);
20613                 }
20614             },
20615             interval : 10,
20616             duration: 10000,
20617             scope: this
20618         };
20619         Roo.TaskMgr.start(task);
20620
20621     },
20622
20623     // private
20624     onResize : function(w, h)
20625     {
20626          Roo.log('resize: ' +w + ',' + h );
20627         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20628         if(!this.iframe){
20629             return;
20630         }
20631         if(typeof w == 'number'){
20632             
20633             this.iframe.style.width = w + 'px';
20634         }
20635         if(typeof h == 'number'){
20636             
20637             this.iframe.style.height = h + 'px';
20638             if(this.doc){
20639                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20640             }
20641         }
20642         
20643     },
20644
20645     /**
20646      * Toggles the editor between standard and source edit mode.
20647      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20648      */
20649     toggleSourceEdit : function(sourceEditMode){
20650         
20651         this.sourceEditMode = sourceEditMode === true;
20652         
20653         if(this.sourceEditMode){
20654  
20655             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20656             
20657         }else{
20658             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20659             //this.iframe.className = '';
20660             this.deferFocus();
20661         }
20662         //this.setSize(this.owner.wrap.getSize());
20663         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20664     },
20665
20666     
20667   
20668
20669     /**
20670      * Protected method that will not generally be called directly. If you need/want
20671      * custom HTML cleanup, this is the method you should override.
20672      * @param {String} html The HTML to be cleaned
20673      * return {String} The cleaned HTML
20674      */
20675     cleanHtml : function(html){
20676         html = String(html);
20677         if(html.length > 5){
20678             if(Roo.isSafari){ // strip safari nonsense
20679                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20680             }
20681         }
20682         if(html == '&nbsp;'){
20683             html = '';
20684         }
20685         return html;
20686     },
20687
20688     /**
20689      * HTML Editor -> Textarea
20690      * Protected method that will not generally be called directly. Syncs the contents
20691      * of the editor iframe with the textarea.
20692      */
20693     syncValue : function(){
20694         if(this.initialized){
20695             var bd = (this.doc.body || this.doc.documentElement);
20696             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20697             var html = bd.innerHTML;
20698             if(Roo.isSafari){
20699                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20700                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20701                 if(m && m[1]){
20702                     html = '<div style="'+m[0]+'">' + html + '</div>';
20703                 }
20704             }
20705             html = this.cleanHtml(html);
20706             // fix up the special chars.. normaly like back quotes in word...
20707             // however we do not want to do this with chinese..
20708             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20709                 var cc = b.charCodeAt();
20710                 if (
20711                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20712                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20713                     (cc >= 0xf900 && cc < 0xfb00 )
20714                 ) {
20715                         return b;
20716                 }
20717                 return "&#"+cc+";" 
20718             });
20719             if(this.owner.fireEvent('beforesync', this, html) !== false){
20720                 this.el.dom.value = html;
20721                 this.owner.fireEvent('sync', this, html);
20722             }
20723         }
20724     },
20725
20726     /**
20727      * Protected method that will not generally be called directly. Pushes the value of the textarea
20728      * into the iframe editor.
20729      */
20730     pushValue : function(){
20731         if(this.initialized){
20732             var v = this.el.dom.value.trim();
20733             
20734 //            if(v.length < 1){
20735 //                v = '&#160;';
20736 //            }
20737             
20738             if(this.owner.fireEvent('beforepush', this, v) !== false){
20739                 var d = (this.doc.body || this.doc.documentElement);
20740                 d.innerHTML = v;
20741                 this.cleanUpPaste();
20742                 this.el.dom.value = d.innerHTML;
20743                 this.owner.fireEvent('push', this, v);
20744             }
20745         }
20746     },
20747
20748     // private
20749     deferFocus : function(){
20750         this.focus.defer(10, this);
20751     },
20752
20753     // doc'ed in Field
20754     focus : function(){
20755         if(this.win && !this.sourceEditMode){
20756             this.win.focus();
20757         }else{
20758             this.el.focus();
20759         }
20760     },
20761     
20762     assignDocWin: function()
20763     {
20764         var iframe = this.iframe;
20765         
20766          if(Roo.isIE){
20767             this.doc = iframe.contentWindow.document;
20768             this.win = iframe.contentWindow;
20769         } else {
20770 //            if (!Roo.get(this.frameId)) {
20771 //                return;
20772 //            }
20773 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20774 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20775             
20776             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20777                 return;
20778             }
20779             
20780             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20781             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20782         }
20783     },
20784     
20785     // private
20786     initEditor : function(){
20787         //console.log("INIT EDITOR");
20788         this.assignDocWin();
20789         
20790         
20791         
20792         this.doc.designMode="on";
20793         this.doc.open();
20794         this.doc.write(this.getDocMarkup());
20795         this.doc.close();
20796         
20797         var dbody = (this.doc.body || this.doc.documentElement);
20798         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20799         // this copies styles from the containing element into thsi one..
20800         // not sure why we need all of this..
20801         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20802         
20803         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20804         //ss['background-attachment'] = 'fixed'; // w3c
20805         dbody.bgProperties = 'fixed'; // ie
20806         //Roo.DomHelper.applyStyles(dbody, ss);
20807         Roo.EventManager.on(this.doc, {
20808             //'mousedown': this.onEditorEvent,
20809             'mouseup': this.onEditorEvent,
20810             'dblclick': this.onEditorEvent,
20811             'click': this.onEditorEvent,
20812             'keyup': this.onEditorEvent,
20813             buffer:100,
20814             scope: this
20815         });
20816         if(Roo.isGecko){
20817             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20818         }
20819         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20820             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20821         }
20822         this.initialized = true;
20823
20824         this.owner.fireEvent('initialize', this);
20825         this.pushValue();
20826     },
20827
20828     // private
20829     onDestroy : function(){
20830         
20831         
20832         
20833         if(this.rendered){
20834             
20835             //for (var i =0; i < this.toolbars.length;i++) {
20836             //    // fixme - ask toolbars for heights?
20837             //    this.toolbars[i].onDestroy();
20838            // }
20839             
20840             //this.wrap.dom.innerHTML = '';
20841             //this.wrap.remove();
20842         }
20843     },
20844
20845     // private
20846     onFirstFocus : function(){
20847         
20848         this.assignDocWin();
20849         
20850         
20851         this.activated = true;
20852          
20853     
20854         if(Roo.isGecko){ // prevent silly gecko errors
20855             this.win.focus();
20856             var s = this.win.getSelection();
20857             if(!s.focusNode || s.focusNode.nodeType != 3){
20858                 var r = s.getRangeAt(0);
20859                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20860                 r.collapse(true);
20861                 this.deferFocus();
20862             }
20863             try{
20864                 this.execCmd('useCSS', true);
20865                 this.execCmd('styleWithCSS', false);
20866             }catch(e){}
20867         }
20868         this.owner.fireEvent('activate', this);
20869     },
20870
20871     // private
20872     adjustFont: function(btn){
20873         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20874         //if(Roo.isSafari){ // safari
20875         //    adjust *= 2;
20876        // }
20877         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20878         if(Roo.isSafari){ // safari
20879             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20880             v =  (v < 10) ? 10 : v;
20881             v =  (v > 48) ? 48 : v;
20882             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20883             
20884         }
20885         
20886         
20887         v = Math.max(1, v+adjust);
20888         
20889         this.execCmd('FontSize', v  );
20890     },
20891
20892     onEditorEvent : function(e)
20893     {
20894         this.owner.fireEvent('editorevent', this, e);
20895       //  this.updateToolbar();
20896         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20897     },
20898
20899     insertTag : function(tg)
20900     {
20901         // could be a bit smarter... -> wrap the current selected tRoo..
20902         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20903             
20904             range = this.createRange(this.getSelection());
20905             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20906             wrappingNode.appendChild(range.extractContents());
20907             range.insertNode(wrappingNode);
20908
20909             return;
20910             
20911             
20912             
20913         }
20914         this.execCmd("formatblock",   tg);
20915         
20916     },
20917     
20918     insertText : function(txt)
20919     {
20920         
20921         
20922         var range = this.createRange();
20923         range.deleteContents();
20924                //alert(Sender.getAttribute('label'));
20925                
20926         range.insertNode(this.doc.createTextNode(txt));
20927     } ,
20928     
20929      
20930
20931     /**
20932      * Executes a Midas editor command on the editor document and performs necessary focus and
20933      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20934      * @param {String} cmd The Midas command
20935      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20936      */
20937     relayCmd : function(cmd, value){
20938         this.win.focus();
20939         this.execCmd(cmd, value);
20940         this.owner.fireEvent('editorevent', this);
20941         //this.updateToolbar();
20942         this.owner.deferFocus();
20943     },
20944
20945     /**
20946      * Executes a Midas editor command directly on the editor document.
20947      * For visual commands, you should use {@link #relayCmd} instead.
20948      * <b>This should only be called after the editor is initialized.</b>
20949      * @param {String} cmd The Midas command
20950      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20951      */
20952     execCmd : function(cmd, value){
20953         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20954         this.syncValue();
20955     },
20956  
20957  
20958    
20959     /**
20960      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20961      * to insert tRoo.
20962      * @param {String} text | dom node.. 
20963      */
20964     insertAtCursor : function(text)
20965     {
20966         
20967         
20968         
20969         if(!this.activated){
20970             return;
20971         }
20972         /*
20973         if(Roo.isIE){
20974             this.win.focus();
20975             var r = this.doc.selection.createRange();
20976             if(r){
20977                 r.collapse(true);
20978                 r.pasteHTML(text);
20979                 this.syncValue();
20980                 this.deferFocus();
20981             
20982             }
20983             return;
20984         }
20985         */
20986         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20987             this.win.focus();
20988             
20989             
20990             // from jquery ui (MIT licenced)
20991             var range, node;
20992             var win = this.win;
20993             
20994             if (win.getSelection && win.getSelection().getRangeAt) {
20995                 range = win.getSelection().getRangeAt(0);
20996                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20997                 range.insertNode(node);
20998             } else if (win.document.selection && win.document.selection.createRange) {
20999                 // no firefox support
21000                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21001                 win.document.selection.createRange().pasteHTML(txt);
21002             } else {
21003                 // no firefox support
21004                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21005                 this.execCmd('InsertHTML', txt);
21006             } 
21007             
21008             this.syncValue();
21009             
21010             this.deferFocus();
21011         }
21012     },
21013  // private
21014     mozKeyPress : function(e){
21015         if(e.ctrlKey){
21016             var c = e.getCharCode(), cmd;
21017           
21018             if(c > 0){
21019                 c = String.fromCharCode(c).toLowerCase();
21020                 switch(c){
21021                     case 'b':
21022                         cmd = 'bold';
21023                         break;
21024                     case 'i':
21025                         cmd = 'italic';
21026                         break;
21027                     
21028                     case 'u':
21029                         cmd = 'underline';
21030                         break;
21031                     
21032                     case 'v':
21033                         this.cleanUpPaste.defer(100, this);
21034                         return;
21035                         
21036                 }
21037                 if(cmd){
21038                     this.win.focus();
21039                     this.execCmd(cmd);
21040                     this.deferFocus();
21041                     e.preventDefault();
21042                 }
21043                 
21044             }
21045         }
21046     },
21047
21048     // private
21049     fixKeys : function(){ // load time branching for fastest keydown performance
21050         if(Roo.isIE){
21051             return function(e){
21052                 var k = e.getKey(), r;
21053                 if(k == e.TAB){
21054                     e.stopEvent();
21055                     r = this.doc.selection.createRange();
21056                     if(r){
21057                         r.collapse(true);
21058                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21059                         this.deferFocus();
21060                     }
21061                     return;
21062                 }
21063                 
21064                 if(k == e.ENTER){
21065                     r = this.doc.selection.createRange();
21066                     if(r){
21067                         var target = r.parentElement();
21068                         if(!target || target.tagName.toLowerCase() != 'li'){
21069                             e.stopEvent();
21070                             r.pasteHTML('<br />');
21071                             r.collapse(false);
21072                             r.select();
21073                         }
21074                     }
21075                 }
21076                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21077                     this.cleanUpPaste.defer(100, this);
21078                     return;
21079                 }
21080                 
21081                 
21082             };
21083         }else if(Roo.isOpera){
21084             return function(e){
21085                 var k = e.getKey();
21086                 if(k == e.TAB){
21087                     e.stopEvent();
21088                     this.win.focus();
21089                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21090                     this.deferFocus();
21091                 }
21092                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21093                     this.cleanUpPaste.defer(100, this);
21094                     return;
21095                 }
21096                 
21097             };
21098         }else if(Roo.isSafari){
21099             return function(e){
21100                 var k = e.getKey();
21101                 
21102                 if(k == e.TAB){
21103                     e.stopEvent();
21104                     this.execCmd('InsertText','\t');
21105                     this.deferFocus();
21106                     return;
21107                 }
21108                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21109                     this.cleanUpPaste.defer(100, this);
21110                     return;
21111                 }
21112                 
21113              };
21114         }
21115     }(),
21116     
21117     getAllAncestors: function()
21118     {
21119         var p = this.getSelectedNode();
21120         var a = [];
21121         if (!p) {
21122             a.push(p); // push blank onto stack..
21123             p = this.getParentElement();
21124         }
21125         
21126         
21127         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21128             a.push(p);
21129             p = p.parentNode;
21130         }
21131         a.push(this.doc.body);
21132         return a;
21133     },
21134     lastSel : false,
21135     lastSelNode : false,
21136     
21137     
21138     getSelection : function() 
21139     {
21140         this.assignDocWin();
21141         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21142     },
21143     
21144     getSelectedNode: function() 
21145     {
21146         // this may only work on Gecko!!!
21147         
21148         // should we cache this!!!!
21149         
21150         
21151         
21152          
21153         var range = this.createRange(this.getSelection()).cloneRange();
21154         
21155         if (Roo.isIE) {
21156             var parent = range.parentElement();
21157             while (true) {
21158                 var testRange = range.duplicate();
21159                 testRange.moveToElementText(parent);
21160                 if (testRange.inRange(range)) {
21161                     break;
21162                 }
21163                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21164                     break;
21165                 }
21166                 parent = parent.parentElement;
21167             }
21168             return parent;
21169         }
21170         
21171         // is ancestor a text element.
21172         var ac =  range.commonAncestorContainer;
21173         if (ac.nodeType == 3) {
21174             ac = ac.parentNode;
21175         }
21176         
21177         var ar = ac.childNodes;
21178          
21179         var nodes = [];
21180         var other_nodes = [];
21181         var has_other_nodes = false;
21182         for (var i=0;i<ar.length;i++) {
21183             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21184                 continue;
21185             }
21186             // fullly contained node.
21187             
21188             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21189                 nodes.push(ar[i]);
21190                 continue;
21191             }
21192             
21193             // probably selected..
21194             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21195                 other_nodes.push(ar[i]);
21196                 continue;
21197             }
21198             // outer..
21199             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21200                 continue;
21201             }
21202             
21203             
21204             has_other_nodes = true;
21205         }
21206         if (!nodes.length && other_nodes.length) {
21207             nodes= other_nodes;
21208         }
21209         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21210             return false;
21211         }
21212         
21213         return nodes[0];
21214     },
21215     createRange: function(sel)
21216     {
21217         // this has strange effects when using with 
21218         // top toolbar - not sure if it's a great idea.
21219         //this.editor.contentWindow.focus();
21220         if (typeof sel != "undefined") {
21221             try {
21222                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21223             } catch(e) {
21224                 return this.doc.createRange();
21225             }
21226         } else {
21227             return this.doc.createRange();
21228         }
21229     },
21230     getParentElement: function()
21231     {
21232         
21233         this.assignDocWin();
21234         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21235         
21236         var range = this.createRange(sel);
21237          
21238         try {
21239             var p = range.commonAncestorContainer;
21240             while (p.nodeType == 3) { // text node
21241                 p = p.parentNode;
21242             }
21243             return p;
21244         } catch (e) {
21245             return null;
21246         }
21247     
21248     },
21249     /***
21250      *
21251      * Range intersection.. the hard stuff...
21252      *  '-1' = before
21253      *  '0' = hits..
21254      *  '1' = after.
21255      *         [ -- selected range --- ]
21256      *   [fail]                        [fail]
21257      *
21258      *    basically..
21259      *      if end is before start or  hits it. fail.
21260      *      if start is after end or hits it fail.
21261      *
21262      *   if either hits (but other is outside. - then it's not 
21263      *   
21264      *    
21265      **/
21266     
21267     
21268     // @see http://www.thismuchiknow.co.uk/?p=64.
21269     rangeIntersectsNode : function(range, node)
21270     {
21271         var nodeRange = node.ownerDocument.createRange();
21272         try {
21273             nodeRange.selectNode(node);
21274         } catch (e) {
21275             nodeRange.selectNodeContents(node);
21276         }
21277     
21278         var rangeStartRange = range.cloneRange();
21279         rangeStartRange.collapse(true);
21280     
21281         var rangeEndRange = range.cloneRange();
21282         rangeEndRange.collapse(false);
21283     
21284         var nodeStartRange = nodeRange.cloneRange();
21285         nodeStartRange.collapse(true);
21286     
21287         var nodeEndRange = nodeRange.cloneRange();
21288         nodeEndRange.collapse(false);
21289     
21290         return rangeStartRange.compareBoundaryPoints(
21291                  Range.START_TO_START, nodeEndRange) == -1 &&
21292                rangeEndRange.compareBoundaryPoints(
21293                  Range.START_TO_START, nodeStartRange) == 1;
21294         
21295          
21296     },
21297     rangeCompareNode : function(range, node)
21298     {
21299         var nodeRange = node.ownerDocument.createRange();
21300         try {
21301             nodeRange.selectNode(node);
21302         } catch (e) {
21303             nodeRange.selectNodeContents(node);
21304         }
21305         
21306         
21307         range.collapse(true);
21308     
21309         nodeRange.collapse(true);
21310      
21311         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21312         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21313          
21314         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21315         
21316         var nodeIsBefore   =  ss == 1;
21317         var nodeIsAfter    = ee == -1;
21318         
21319         if (nodeIsBefore && nodeIsAfter) {
21320             return 0; // outer
21321         }
21322         if (!nodeIsBefore && nodeIsAfter) {
21323             return 1; //right trailed.
21324         }
21325         
21326         if (nodeIsBefore && !nodeIsAfter) {
21327             return 2;  // left trailed.
21328         }
21329         // fully contined.
21330         return 3;
21331     },
21332
21333     // private? - in a new class?
21334     cleanUpPaste :  function()
21335     {
21336         // cleans up the whole document..
21337         Roo.log('cleanuppaste');
21338         
21339         this.cleanUpChildren(this.doc.body);
21340         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21341         if (clean != this.doc.body.innerHTML) {
21342             this.doc.body.innerHTML = clean;
21343         }
21344         
21345     },
21346     
21347     cleanWordChars : function(input) {// change the chars to hex code
21348         var he = Roo.HtmlEditorCore;
21349         
21350         var output = input;
21351         Roo.each(he.swapCodes, function(sw) { 
21352             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21353             
21354             output = output.replace(swapper, sw[1]);
21355         });
21356         
21357         return output;
21358     },
21359     
21360     
21361     cleanUpChildren : function (n)
21362     {
21363         if (!n.childNodes.length) {
21364             return;
21365         }
21366         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21367            this.cleanUpChild(n.childNodes[i]);
21368         }
21369     },
21370     
21371     
21372         
21373     
21374     cleanUpChild : function (node)
21375     {
21376         var ed = this;
21377         //console.log(node);
21378         if (node.nodeName == "#text") {
21379             // clean up silly Windows -- stuff?
21380             return; 
21381         }
21382         if (node.nodeName == "#comment") {
21383             node.parentNode.removeChild(node);
21384             // clean up silly Windows -- stuff?
21385             return; 
21386         }
21387         var lcname = node.tagName.toLowerCase();
21388         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21389         // whitelist of tags..
21390         
21391         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21392             // remove node.
21393             node.parentNode.removeChild(node);
21394             return;
21395             
21396         }
21397         
21398         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21399         
21400         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21401         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21402         
21403         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21404         //    remove_keep_children = true;
21405         //}
21406         
21407         if (remove_keep_children) {
21408             this.cleanUpChildren(node);
21409             // inserts everything just before this node...
21410             while (node.childNodes.length) {
21411                 var cn = node.childNodes[0];
21412                 node.removeChild(cn);
21413                 node.parentNode.insertBefore(cn, node);
21414             }
21415             node.parentNode.removeChild(node);
21416             return;
21417         }
21418         
21419         if (!node.attributes || !node.attributes.length) {
21420             this.cleanUpChildren(node);
21421             return;
21422         }
21423         
21424         function cleanAttr(n,v)
21425         {
21426             
21427             if (v.match(/^\./) || v.match(/^\//)) {
21428                 return;
21429             }
21430             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21431                 return;
21432             }
21433             if (v.match(/^#/)) {
21434                 return;
21435             }
21436 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21437             node.removeAttribute(n);
21438             
21439         }
21440         
21441         var cwhite = this.cwhite;
21442         var cblack = this.cblack;
21443             
21444         function cleanStyle(n,v)
21445         {
21446             if (v.match(/expression/)) { //XSS?? should we even bother..
21447                 node.removeAttribute(n);
21448                 return;
21449             }
21450             
21451             var parts = v.split(/;/);
21452             var clean = [];
21453             
21454             Roo.each(parts, function(p) {
21455                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21456                 if (!p.length) {
21457                     return true;
21458                 }
21459                 var l = p.split(':').shift().replace(/\s+/g,'');
21460                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21461                 
21462                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21463 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21464                     //node.removeAttribute(n);
21465                     return true;
21466                 }
21467                 //Roo.log()
21468                 // only allow 'c whitelisted system attributes'
21469                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21470 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21471                     //node.removeAttribute(n);
21472                     return true;
21473                 }
21474                 
21475                 
21476                  
21477                 
21478                 clean.push(p);
21479                 return true;
21480             });
21481             if (clean.length) { 
21482                 node.setAttribute(n, clean.join(';'));
21483             } else {
21484                 node.removeAttribute(n);
21485             }
21486             
21487         }
21488         
21489         
21490         for (var i = node.attributes.length-1; i > -1 ; i--) {
21491             var a = node.attributes[i];
21492             //console.log(a);
21493             
21494             if (a.name.toLowerCase().substr(0,2)=='on')  {
21495                 node.removeAttribute(a.name);
21496                 continue;
21497             }
21498             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21499                 node.removeAttribute(a.name);
21500                 continue;
21501             }
21502             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21503                 cleanAttr(a.name,a.value); // fixme..
21504                 continue;
21505             }
21506             if (a.name == 'style') {
21507                 cleanStyle(a.name,a.value);
21508                 continue;
21509             }
21510             /// clean up MS crap..
21511             // tecnically this should be a list of valid class'es..
21512             
21513             
21514             if (a.name == 'class') {
21515                 if (a.value.match(/^Mso/)) {
21516                     node.className = '';
21517                 }
21518                 
21519                 if (a.value.match(/body/)) {
21520                     node.className = '';
21521                 }
21522                 continue;
21523             }
21524             
21525             // style cleanup!?
21526             // class cleanup?
21527             
21528         }
21529         
21530         
21531         this.cleanUpChildren(node);
21532         
21533         
21534     },
21535     
21536     /**
21537      * Clean up MS wordisms...
21538      */
21539     cleanWord : function(node)
21540     {
21541         
21542         
21543         if (!node) {
21544             this.cleanWord(this.doc.body);
21545             return;
21546         }
21547         if (node.nodeName == "#text") {
21548             // clean up silly Windows -- stuff?
21549             return; 
21550         }
21551         if (node.nodeName == "#comment") {
21552             node.parentNode.removeChild(node);
21553             // clean up silly Windows -- stuff?
21554             return; 
21555         }
21556         
21557         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21558             node.parentNode.removeChild(node);
21559             return;
21560         }
21561         
21562         // remove - but keep children..
21563         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21564             while (node.childNodes.length) {
21565                 var cn = node.childNodes[0];
21566                 node.removeChild(cn);
21567                 node.parentNode.insertBefore(cn, node);
21568             }
21569             node.parentNode.removeChild(node);
21570             this.iterateChildren(node, this.cleanWord);
21571             return;
21572         }
21573         // clean styles
21574         if (node.className.length) {
21575             
21576             var cn = node.className.split(/\W+/);
21577             var cna = [];
21578             Roo.each(cn, function(cls) {
21579                 if (cls.match(/Mso[a-zA-Z]+/)) {
21580                     return;
21581                 }
21582                 cna.push(cls);
21583             });
21584             node.className = cna.length ? cna.join(' ') : '';
21585             if (!cna.length) {
21586                 node.removeAttribute("class");
21587             }
21588         }
21589         
21590         if (node.hasAttribute("lang")) {
21591             node.removeAttribute("lang");
21592         }
21593         
21594         if (node.hasAttribute("style")) {
21595             
21596             var styles = node.getAttribute("style").split(";");
21597             var nstyle = [];
21598             Roo.each(styles, function(s) {
21599                 if (!s.match(/:/)) {
21600                     return;
21601                 }
21602                 var kv = s.split(":");
21603                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21604                     return;
21605                 }
21606                 // what ever is left... we allow.
21607                 nstyle.push(s);
21608             });
21609             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21610             if (!nstyle.length) {
21611                 node.removeAttribute('style');
21612             }
21613         }
21614         this.iterateChildren(node, this.cleanWord);
21615         
21616         
21617         
21618     },
21619     /**
21620      * iterateChildren of a Node, calling fn each time, using this as the scole..
21621      * @param {DomNode} node node to iterate children of.
21622      * @param {Function} fn method of this class to call on each item.
21623      */
21624     iterateChildren : function(node, fn)
21625     {
21626         if (!node.childNodes.length) {
21627                 return;
21628         }
21629         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21630            fn.call(this, node.childNodes[i])
21631         }
21632     },
21633     
21634     
21635     /**
21636      * cleanTableWidths.
21637      *
21638      * Quite often pasting from word etc.. results in tables with column and widths.
21639      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21640      *
21641      */
21642     cleanTableWidths : function(node)
21643     {
21644          
21645          
21646         if (!node) {
21647             this.cleanTableWidths(this.doc.body);
21648             return;
21649         }
21650         
21651         // ignore list...
21652         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21653             return; 
21654         }
21655         Roo.log(node.tagName);
21656         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21657             this.iterateChildren(node, this.cleanTableWidths);
21658             return;
21659         }
21660         if (node.hasAttribute('width')) {
21661             node.removeAttribute('width');
21662         }
21663         
21664          
21665         if (node.hasAttribute("style")) {
21666             // pretty basic...
21667             
21668             var styles = node.getAttribute("style").split(";");
21669             var nstyle = [];
21670             Roo.each(styles, function(s) {
21671                 if (!s.match(/:/)) {
21672                     return;
21673                 }
21674                 var kv = s.split(":");
21675                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21676                     return;
21677                 }
21678                 // what ever is left... we allow.
21679                 nstyle.push(s);
21680             });
21681             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21682             if (!nstyle.length) {
21683                 node.removeAttribute('style');
21684             }
21685         }
21686         
21687         this.iterateChildren(node, this.cleanTableWidths);
21688         
21689         
21690     },
21691     
21692     
21693     
21694     
21695     domToHTML : function(currentElement, depth, nopadtext) {
21696         
21697         depth = depth || 0;
21698         nopadtext = nopadtext || false;
21699     
21700         if (!currentElement) {
21701             return this.domToHTML(this.doc.body);
21702         }
21703         
21704         //Roo.log(currentElement);
21705         var j;
21706         var allText = false;
21707         var nodeName = currentElement.nodeName;
21708         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21709         
21710         if  (nodeName == '#text') {
21711             
21712             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21713         }
21714         
21715         
21716         var ret = '';
21717         if (nodeName != 'BODY') {
21718              
21719             var i = 0;
21720             // Prints the node tagName, such as <A>, <IMG>, etc
21721             if (tagName) {
21722                 var attr = [];
21723                 for(i = 0; i < currentElement.attributes.length;i++) {
21724                     // quoting?
21725                     var aname = currentElement.attributes.item(i).name;
21726                     if (!currentElement.attributes.item(i).value.length) {
21727                         continue;
21728                     }
21729                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21730                 }
21731                 
21732                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21733             } 
21734             else {
21735                 
21736                 // eack
21737             }
21738         } else {
21739             tagName = false;
21740         }
21741         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21742             return ret;
21743         }
21744         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21745             nopadtext = true;
21746         }
21747         
21748         
21749         // Traverse the tree
21750         i = 0;
21751         var currentElementChild = currentElement.childNodes.item(i);
21752         var allText = true;
21753         var innerHTML  = '';
21754         lastnode = '';
21755         while (currentElementChild) {
21756             // Formatting code (indent the tree so it looks nice on the screen)
21757             var nopad = nopadtext;
21758             if (lastnode == 'SPAN') {
21759                 nopad  = true;
21760             }
21761             // text
21762             if  (currentElementChild.nodeName == '#text') {
21763                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21764                 toadd = nopadtext ? toadd : toadd.trim();
21765                 if (!nopad && toadd.length > 80) {
21766                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21767                 }
21768                 innerHTML  += toadd;
21769                 
21770                 i++;
21771                 currentElementChild = currentElement.childNodes.item(i);
21772                 lastNode = '';
21773                 continue;
21774             }
21775             allText = false;
21776             
21777             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21778                 
21779             // Recursively traverse the tree structure of the child node
21780             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21781             lastnode = currentElementChild.nodeName;
21782             i++;
21783             currentElementChild=currentElement.childNodes.item(i);
21784         }
21785         
21786         ret += innerHTML;
21787         
21788         if (!allText) {
21789                 // The remaining code is mostly for formatting the tree
21790             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21791         }
21792         
21793         
21794         if (tagName) {
21795             ret+= "</"+tagName+">";
21796         }
21797         return ret;
21798         
21799     },
21800         
21801     applyBlacklists : function()
21802     {
21803         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21804         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21805         
21806         this.white = [];
21807         this.black = [];
21808         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21809             if (b.indexOf(tag) > -1) {
21810                 return;
21811             }
21812             this.white.push(tag);
21813             
21814         }, this);
21815         
21816         Roo.each(w, function(tag) {
21817             if (b.indexOf(tag) > -1) {
21818                 return;
21819             }
21820             if (this.white.indexOf(tag) > -1) {
21821                 return;
21822             }
21823             this.white.push(tag);
21824             
21825         }, this);
21826         
21827         
21828         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21829             if (w.indexOf(tag) > -1) {
21830                 return;
21831             }
21832             this.black.push(tag);
21833             
21834         }, this);
21835         
21836         Roo.each(b, function(tag) {
21837             if (w.indexOf(tag) > -1) {
21838                 return;
21839             }
21840             if (this.black.indexOf(tag) > -1) {
21841                 return;
21842             }
21843             this.black.push(tag);
21844             
21845         }, this);
21846         
21847         
21848         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21849         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21850         
21851         this.cwhite = [];
21852         this.cblack = [];
21853         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21854             if (b.indexOf(tag) > -1) {
21855                 return;
21856             }
21857             this.cwhite.push(tag);
21858             
21859         }, this);
21860         
21861         Roo.each(w, function(tag) {
21862             if (b.indexOf(tag) > -1) {
21863                 return;
21864             }
21865             if (this.cwhite.indexOf(tag) > -1) {
21866                 return;
21867             }
21868             this.cwhite.push(tag);
21869             
21870         }, this);
21871         
21872         
21873         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21874             if (w.indexOf(tag) > -1) {
21875                 return;
21876             }
21877             this.cblack.push(tag);
21878             
21879         }, this);
21880         
21881         Roo.each(b, function(tag) {
21882             if (w.indexOf(tag) > -1) {
21883                 return;
21884             }
21885             if (this.cblack.indexOf(tag) > -1) {
21886                 return;
21887             }
21888             this.cblack.push(tag);
21889             
21890         }, this);
21891     },
21892     
21893     setStylesheets : function(stylesheets)
21894     {
21895         if(typeof(stylesheets) == 'string'){
21896             Roo.get(this.iframe.contentDocument.head).createChild({
21897                 tag : 'link',
21898                 rel : 'stylesheet',
21899                 type : 'text/css',
21900                 href : stylesheets
21901             });
21902             
21903             return;
21904         }
21905         var _this = this;
21906      
21907         Roo.each(stylesheets, function(s) {
21908             if(!s.length){
21909                 return;
21910             }
21911             
21912             Roo.get(_this.iframe.contentDocument.head).createChild({
21913                 tag : 'link',
21914                 rel : 'stylesheet',
21915                 type : 'text/css',
21916                 href : s
21917             });
21918         });
21919
21920         
21921     },
21922     
21923     removeStylesheets : function()
21924     {
21925         var _this = this;
21926         
21927         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21928             s.remove();
21929         });
21930     }
21931     
21932     // hide stuff that is not compatible
21933     /**
21934      * @event blur
21935      * @hide
21936      */
21937     /**
21938      * @event change
21939      * @hide
21940      */
21941     /**
21942      * @event focus
21943      * @hide
21944      */
21945     /**
21946      * @event specialkey
21947      * @hide
21948      */
21949     /**
21950      * @cfg {String} fieldClass @hide
21951      */
21952     /**
21953      * @cfg {String} focusClass @hide
21954      */
21955     /**
21956      * @cfg {String} autoCreate @hide
21957      */
21958     /**
21959      * @cfg {String} inputType @hide
21960      */
21961     /**
21962      * @cfg {String} invalidClass @hide
21963      */
21964     /**
21965      * @cfg {String} invalidText @hide
21966      */
21967     /**
21968      * @cfg {String} msgFx @hide
21969      */
21970     /**
21971      * @cfg {String} validateOnBlur @hide
21972      */
21973 });
21974
21975 Roo.HtmlEditorCore.white = [
21976         'area', 'br', 'img', 'input', 'hr', 'wbr',
21977         
21978        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21979        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21980        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21981        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21982        'table',   'ul',         'xmp', 
21983        
21984        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21985       'thead',   'tr', 
21986      
21987       'dir', 'menu', 'ol', 'ul', 'dl',
21988        
21989       'embed',  'object'
21990 ];
21991
21992
21993 Roo.HtmlEditorCore.black = [
21994     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21995         'applet', // 
21996         'base',   'basefont', 'bgsound', 'blink',  'body', 
21997         'frame',  'frameset', 'head',    'html',   'ilayer', 
21998         'iframe', 'layer',  'link',     'meta',    'object',   
21999         'script', 'style' ,'title',  'xml' // clean later..
22000 ];
22001 Roo.HtmlEditorCore.clean = [
22002     'script', 'style', 'title', 'xml'
22003 ];
22004 Roo.HtmlEditorCore.remove = [
22005     'font'
22006 ];
22007 // attributes..
22008
22009 Roo.HtmlEditorCore.ablack = [
22010     'on'
22011 ];
22012     
22013 Roo.HtmlEditorCore.aclean = [ 
22014     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22015 ];
22016
22017 // protocols..
22018 Roo.HtmlEditorCore.pwhite= [
22019         'http',  'https',  'mailto'
22020 ];
22021
22022 // white listed style attributes.
22023 Roo.HtmlEditorCore.cwhite= [
22024       //  'text-align', /// default is to allow most things..
22025       
22026          
22027 //        'font-size'//??
22028 ];
22029
22030 // black listed style attributes.
22031 Roo.HtmlEditorCore.cblack= [
22032       //  'font-size' -- this can be set by the project 
22033 ];
22034
22035
22036 Roo.HtmlEditorCore.swapCodes   =[ 
22037     [    8211, "--" ], 
22038     [    8212, "--" ], 
22039     [    8216,  "'" ],  
22040     [    8217, "'" ],  
22041     [    8220, '"' ],  
22042     [    8221, '"' ],  
22043     [    8226, "*" ],  
22044     [    8230, "..." ]
22045 ]; 
22046
22047     /*
22048  * - LGPL
22049  *
22050  * HtmlEditor
22051  * 
22052  */
22053
22054 /**
22055  * @class Roo.bootstrap.HtmlEditor
22056  * @extends Roo.bootstrap.TextArea
22057  * Bootstrap HtmlEditor class
22058
22059  * @constructor
22060  * Create a new HtmlEditor
22061  * @param {Object} config The config object
22062  */
22063
22064 Roo.bootstrap.HtmlEditor = function(config){
22065     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22066     if (!this.toolbars) {
22067         this.toolbars = [];
22068     }
22069     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22070     this.addEvents({
22071             /**
22072              * @event initialize
22073              * Fires when the editor is fully initialized (including the iframe)
22074              * @param {HtmlEditor} this
22075              */
22076             initialize: true,
22077             /**
22078              * @event activate
22079              * Fires when the editor is first receives the focus. Any insertion must wait
22080              * until after this event.
22081              * @param {HtmlEditor} this
22082              */
22083             activate: true,
22084              /**
22085              * @event beforesync
22086              * Fires before the textarea is updated with content from the editor iframe. Return false
22087              * to cancel the sync.
22088              * @param {HtmlEditor} this
22089              * @param {String} html
22090              */
22091             beforesync: true,
22092              /**
22093              * @event beforepush
22094              * Fires before the iframe editor is updated with content from the textarea. Return false
22095              * to cancel the push.
22096              * @param {HtmlEditor} this
22097              * @param {String} html
22098              */
22099             beforepush: true,
22100              /**
22101              * @event sync
22102              * Fires when the textarea is updated with content from the editor iframe.
22103              * @param {HtmlEditor} this
22104              * @param {String} html
22105              */
22106             sync: true,
22107              /**
22108              * @event push
22109              * Fires when the iframe editor is updated with content from the textarea.
22110              * @param {HtmlEditor} this
22111              * @param {String} html
22112              */
22113             push: true,
22114              /**
22115              * @event editmodechange
22116              * Fires when the editor switches edit modes
22117              * @param {HtmlEditor} this
22118              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22119              */
22120             editmodechange: true,
22121             /**
22122              * @event editorevent
22123              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22124              * @param {HtmlEditor} this
22125              */
22126             editorevent: true,
22127             /**
22128              * @event firstfocus
22129              * Fires when on first focus - needed by toolbars..
22130              * @param {HtmlEditor} this
22131              */
22132             firstfocus: true,
22133             /**
22134              * @event autosave
22135              * Auto save the htmlEditor value as a file into Events
22136              * @param {HtmlEditor} this
22137              */
22138             autosave: true,
22139             /**
22140              * @event savedpreview
22141              * preview the saved version of htmlEditor
22142              * @param {HtmlEditor} this
22143              */
22144             savedpreview: true
22145         });
22146 };
22147
22148
22149 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22150     
22151     
22152       /**
22153      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22154      */
22155     toolbars : false,
22156    
22157      /**
22158      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22159      *                        Roo.resizable.
22160      */
22161     resizable : false,
22162      /**
22163      * @cfg {Number} height (in pixels)
22164      */   
22165     height: 300,
22166    /**
22167      * @cfg {Number} width (in pixels)
22168      */   
22169     width: false,
22170     
22171     /**
22172      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22173      * 
22174      */
22175     stylesheets: false,
22176     
22177     // id of frame..
22178     frameId: false,
22179     
22180     // private properties
22181     validationEvent : false,
22182     deferHeight: true,
22183     initialized : false,
22184     activated : false,
22185     
22186     onFocus : Roo.emptyFn,
22187     iframePad:3,
22188     hideMode:'offsets',
22189     
22190     
22191     tbContainer : false,
22192     
22193     toolbarContainer :function() {
22194         return this.wrap.select('.x-html-editor-tb',true).first();
22195     },
22196
22197     /**
22198      * Protected method that will not generally be called directly. It
22199      * is called when the editor creates its toolbar. Override this method if you need to
22200      * add custom toolbar buttons.
22201      * @param {HtmlEditor} editor
22202      */
22203     createToolbar : function(){
22204         
22205         Roo.log("create toolbars");
22206         
22207         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22208         this.toolbars[0].render(this.toolbarContainer());
22209         
22210         return;
22211         
22212 //        if (!editor.toolbars || !editor.toolbars.length) {
22213 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22214 //        }
22215 //        
22216 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22217 //            editor.toolbars[i] = Roo.factory(
22218 //                    typeof(editor.toolbars[i]) == 'string' ?
22219 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22220 //                Roo.bootstrap.HtmlEditor);
22221 //            editor.toolbars[i].init(editor);
22222 //        }
22223     },
22224
22225      
22226     // private
22227     onRender : function(ct, position)
22228     {
22229        // Roo.log("Call onRender: " + this.xtype);
22230         var _t = this;
22231         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22232       
22233         this.wrap = this.inputEl().wrap({
22234             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22235         });
22236         
22237         this.editorcore.onRender(ct, position);
22238          
22239         if (this.resizable) {
22240             this.resizeEl = new Roo.Resizable(this.wrap, {
22241                 pinned : true,
22242                 wrap: true,
22243                 dynamic : true,
22244                 minHeight : this.height,
22245                 height: this.height,
22246                 handles : this.resizable,
22247                 width: this.width,
22248                 listeners : {
22249                     resize : function(r, w, h) {
22250                         _t.onResize(w,h); // -something
22251                     }
22252                 }
22253             });
22254             
22255         }
22256         this.createToolbar(this);
22257        
22258         
22259         if(!this.width && this.resizable){
22260             this.setSize(this.wrap.getSize());
22261         }
22262         if (this.resizeEl) {
22263             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22264             // should trigger onReize..
22265         }
22266         
22267     },
22268
22269     // private
22270     onResize : function(w, h)
22271     {
22272         Roo.log('resize: ' +w + ',' + h );
22273         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22274         var ew = false;
22275         var eh = false;
22276         
22277         if(this.inputEl() ){
22278             if(typeof w == 'number'){
22279                 var aw = w - this.wrap.getFrameWidth('lr');
22280                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22281                 ew = aw;
22282             }
22283             if(typeof h == 'number'){
22284                  var tbh = -11;  // fixme it needs to tool bar size!
22285                 for (var i =0; i < this.toolbars.length;i++) {
22286                     // fixme - ask toolbars for heights?
22287                     tbh += this.toolbars[i].el.getHeight();
22288                     //if (this.toolbars[i].footer) {
22289                     //    tbh += this.toolbars[i].footer.el.getHeight();
22290                     //}
22291                 }
22292               
22293                 
22294                 
22295                 
22296                 
22297                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22298                 ah -= 5; // knock a few pixes off for look..
22299                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22300                 var eh = ah;
22301             }
22302         }
22303         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22304         this.editorcore.onResize(ew,eh);
22305         
22306     },
22307
22308     /**
22309      * Toggles the editor between standard and source edit mode.
22310      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22311      */
22312     toggleSourceEdit : function(sourceEditMode)
22313     {
22314         this.editorcore.toggleSourceEdit(sourceEditMode);
22315         
22316         if(this.editorcore.sourceEditMode){
22317             Roo.log('editor - showing textarea');
22318             
22319 //            Roo.log('in');
22320 //            Roo.log(this.syncValue());
22321             this.syncValue();
22322             this.inputEl().removeClass(['hide', 'x-hidden']);
22323             this.inputEl().dom.removeAttribute('tabIndex');
22324             this.inputEl().focus();
22325         }else{
22326             Roo.log('editor - hiding textarea');
22327 //            Roo.log('out')
22328 //            Roo.log(this.pushValue()); 
22329             this.pushValue();
22330             
22331             this.inputEl().addClass(['hide', 'x-hidden']);
22332             this.inputEl().dom.setAttribute('tabIndex', -1);
22333             //this.deferFocus();
22334         }
22335          
22336         if(this.resizable){
22337             this.setSize(this.wrap.getSize());
22338         }
22339         
22340         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22341     },
22342  
22343     // private (for BoxComponent)
22344     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22345
22346     // private (for BoxComponent)
22347     getResizeEl : function(){
22348         return this.wrap;
22349     },
22350
22351     // private (for BoxComponent)
22352     getPositionEl : function(){
22353         return this.wrap;
22354     },
22355
22356     // private
22357     initEvents : function(){
22358         this.originalValue = this.getValue();
22359     },
22360
22361 //    /**
22362 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22363 //     * @method
22364 //     */
22365 //    markInvalid : Roo.emptyFn,
22366 //    /**
22367 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22368 //     * @method
22369 //     */
22370 //    clearInvalid : Roo.emptyFn,
22371
22372     setValue : function(v){
22373         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22374         this.editorcore.pushValue();
22375     },
22376
22377      
22378     // private
22379     deferFocus : function(){
22380         this.focus.defer(10, this);
22381     },
22382
22383     // doc'ed in Field
22384     focus : function(){
22385         this.editorcore.focus();
22386         
22387     },
22388       
22389
22390     // private
22391     onDestroy : function(){
22392         
22393         
22394         
22395         if(this.rendered){
22396             
22397             for (var i =0; i < this.toolbars.length;i++) {
22398                 // fixme - ask toolbars for heights?
22399                 this.toolbars[i].onDestroy();
22400             }
22401             
22402             this.wrap.dom.innerHTML = '';
22403             this.wrap.remove();
22404         }
22405     },
22406
22407     // private
22408     onFirstFocus : function(){
22409         //Roo.log("onFirstFocus");
22410         this.editorcore.onFirstFocus();
22411          for (var i =0; i < this.toolbars.length;i++) {
22412             this.toolbars[i].onFirstFocus();
22413         }
22414         
22415     },
22416     
22417     // private
22418     syncValue : function()
22419     {   
22420         this.editorcore.syncValue();
22421     },
22422     
22423     pushValue : function()
22424     {   
22425         this.editorcore.pushValue();
22426     }
22427      
22428     
22429     // hide stuff that is not compatible
22430     /**
22431      * @event blur
22432      * @hide
22433      */
22434     /**
22435      * @event change
22436      * @hide
22437      */
22438     /**
22439      * @event focus
22440      * @hide
22441      */
22442     /**
22443      * @event specialkey
22444      * @hide
22445      */
22446     /**
22447      * @cfg {String} fieldClass @hide
22448      */
22449     /**
22450      * @cfg {String} focusClass @hide
22451      */
22452     /**
22453      * @cfg {String} autoCreate @hide
22454      */
22455     /**
22456      * @cfg {String} inputType @hide
22457      */
22458     /**
22459      * @cfg {String} invalidClass @hide
22460      */
22461     /**
22462      * @cfg {String} invalidText @hide
22463      */
22464     /**
22465      * @cfg {String} msgFx @hide
22466      */
22467     /**
22468      * @cfg {String} validateOnBlur @hide
22469      */
22470 });
22471  
22472     
22473    
22474    
22475    
22476       
22477 Roo.namespace('Roo.bootstrap.htmleditor');
22478 /**
22479  * @class Roo.bootstrap.HtmlEditorToolbar1
22480  * Basic Toolbar
22481  * 
22482  * Usage:
22483  *
22484  new Roo.bootstrap.HtmlEditor({
22485     ....
22486     toolbars : [
22487         new Roo.bootstrap.HtmlEditorToolbar1({
22488             disable : { fonts: 1 , format: 1, ..., ... , ...],
22489             btns : [ .... ]
22490         })
22491     }
22492      
22493  * 
22494  * @cfg {Object} disable List of elements to disable..
22495  * @cfg {Array} btns List of additional buttons.
22496  * 
22497  * 
22498  * NEEDS Extra CSS? 
22499  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22500  */
22501  
22502 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22503 {
22504     
22505     Roo.apply(this, config);
22506     
22507     // default disabled, based on 'good practice'..
22508     this.disable = this.disable || {};
22509     Roo.applyIf(this.disable, {
22510         fontSize : true,
22511         colors : true,
22512         specialElements : true
22513     });
22514     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22515     
22516     this.editor = config.editor;
22517     this.editorcore = config.editor.editorcore;
22518     
22519     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22520     
22521     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22522     // dont call parent... till later.
22523 }
22524 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22525      
22526     bar : true,
22527     
22528     editor : false,
22529     editorcore : false,
22530     
22531     
22532     formats : [
22533         "p" ,  
22534         "h1","h2","h3","h4","h5","h6", 
22535         "pre", "code", 
22536         "abbr", "acronym", "address", "cite", "samp", "var",
22537         'div','span'
22538     ],
22539     
22540     onRender : function(ct, position)
22541     {
22542        // Roo.log("Call onRender: " + this.xtype);
22543         
22544        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22545        Roo.log(this.el);
22546        this.el.dom.style.marginBottom = '0';
22547        var _this = this;
22548        var editorcore = this.editorcore;
22549        var editor= this.editor;
22550        
22551        var children = [];
22552        var btn = function(id,cmd , toggle, handler){
22553        
22554             var  event = toggle ? 'toggle' : 'click';
22555        
22556             var a = {
22557                 size : 'sm',
22558                 xtype: 'Button',
22559                 xns: Roo.bootstrap,
22560                 glyphicon : id,
22561                 cmd : id || cmd,
22562                 enableToggle:toggle !== false,
22563                 //html : 'submit'
22564                 pressed : toggle ? false : null,
22565                 listeners : {}
22566             };
22567             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22568                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22569             };
22570             children.push(a);
22571             return a;
22572        }
22573         
22574         var style = {
22575                 xtype: 'Button',
22576                 size : 'sm',
22577                 xns: Roo.bootstrap,
22578                 glyphicon : 'font',
22579                 //html : 'submit'
22580                 menu : {
22581                     xtype: 'Menu',
22582                     xns: Roo.bootstrap,
22583                     items:  []
22584                 }
22585         };
22586         Roo.each(this.formats, function(f) {
22587             style.menu.items.push({
22588                 xtype :'MenuItem',
22589                 xns: Roo.bootstrap,
22590                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22591                 tagname : f,
22592                 listeners : {
22593                     click : function()
22594                     {
22595                         editorcore.insertTag(this.tagname);
22596                         editor.focus();
22597                     }
22598                 }
22599                 
22600             });
22601         });
22602          children.push(style);   
22603             
22604             
22605         btn('bold',false,true);
22606         btn('italic',false,true);
22607         btn('align-left', 'justifyleft',true);
22608         btn('align-center', 'justifycenter',true);
22609         btn('align-right' , 'justifyright',true);
22610         btn('link', false, false, function(btn) {
22611             //Roo.log("create link?");
22612             var url = prompt(this.createLinkText, this.defaultLinkValue);
22613             if(url && url != 'http:/'+'/'){
22614                 this.editorcore.relayCmd('createlink', url);
22615             }
22616         }),
22617         btn('list','insertunorderedlist',true);
22618         btn('pencil', false,true, function(btn){
22619                 Roo.log(this);
22620                 
22621                 this.toggleSourceEdit(btn.pressed);
22622         });
22623         /*
22624         var cog = {
22625                 xtype: 'Button',
22626                 size : 'sm',
22627                 xns: Roo.bootstrap,
22628                 glyphicon : 'cog',
22629                 //html : 'submit'
22630                 menu : {
22631                     xtype: 'Menu',
22632                     xns: Roo.bootstrap,
22633                     items:  []
22634                 }
22635         };
22636         
22637         cog.menu.items.push({
22638             xtype :'MenuItem',
22639             xns: Roo.bootstrap,
22640             html : Clean styles,
22641             tagname : f,
22642             listeners : {
22643                 click : function()
22644                 {
22645                     editorcore.insertTag(this.tagname);
22646                     editor.focus();
22647                 }
22648             }
22649             
22650         });
22651        */
22652         
22653          
22654        this.xtype = 'NavSimplebar';
22655         
22656         for(var i=0;i< children.length;i++) {
22657             
22658             this.buttons.add(this.addxtypeChild(children[i]));
22659             
22660         }
22661         
22662         editor.on('editorevent', this.updateToolbar, this);
22663     },
22664     onBtnClick : function(id)
22665     {
22666        this.editorcore.relayCmd(id);
22667        this.editorcore.focus();
22668     },
22669     
22670     /**
22671      * Protected method that will not generally be called directly. It triggers
22672      * a toolbar update by reading the markup state of the current selection in the editor.
22673      */
22674     updateToolbar: function(){
22675
22676         if(!this.editorcore.activated){
22677             this.editor.onFirstFocus(); // is this neeed?
22678             return;
22679         }
22680
22681         var btns = this.buttons; 
22682         var doc = this.editorcore.doc;
22683         btns.get('bold').setActive(doc.queryCommandState('bold'));
22684         btns.get('italic').setActive(doc.queryCommandState('italic'));
22685         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22686         
22687         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22688         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22689         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22690         
22691         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22692         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22693          /*
22694         
22695         var ans = this.editorcore.getAllAncestors();
22696         if (this.formatCombo) {
22697             
22698             
22699             var store = this.formatCombo.store;
22700             this.formatCombo.setValue("");
22701             for (var i =0; i < ans.length;i++) {
22702                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22703                     // select it..
22704                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22705                     break;
22706                 }
22707             }
22708         }
22709         
22710         
22711         
22712         // hides menus... - so this cant be on a menu...
22713         Roo.bootstrap.MenuMgr.hideAll();
22714         */
22715         Roo.bootstrap.MenuMgr.hideAll();
22716         //this.editorsyncValue();
22717     },
22718     onFirstFocus: function() {
22719         this.buttons.each(function(item){
22720            item.enable();
22721         });
22722     },
22723     toggleSourceEdit : function(sourceEditMode){
22724         
22725           
22726         if(sourceEditMode){
22727             Roo.log("disabling buttons");
22728            this.buttons.each( function(item){
22729                 if(item.cmd != 'pencil'){
22730                     item.disable();
22731                 }
22732             });
22733           
22734         }else{
22735             Roo.log("enabling buttons");
22736             if(this.editorcore.initialized){
22737                 this.buttons.each( function(item){
22738                     item.enable();
22739                 });
22740             }
22741             
22742         }
22743         Roo.log("calling toggole on editor");
22744         // tell the editor that it's been pressed..
22745         this.editor.toggleSourceEdit(sourceEditMode);
22746        
22747     }
22748 });
22749
22750
22751
22752
22753
22754 /**
22755  * @class Roo.bootstrap.Table.AbstractSelectionModel
22756  * @extends Roo.util.Observable
22757  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22758  * implemented by descendant classes.  This class should not be directly instantiated.
22759  * @constructor
22760  */
22761 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22762     this.locked = false;
22763     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22764 };
22765
22766
22767 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22768     /** @ignore Called by the grid automatically. Do not call directly. */
22769     init : function(grid){
22770         this.grid = grid;
22771         this.initEvents();
22772     },
22773
22774     /**
22775      * Locks the selections.
22776      */
22777     lock : function(){
22778         this.locked = true;
22779     },
22780
22781     /**
22782      * Unlocks the selections.
22783      */
22784     unlock : function(){
22785         this.locked = false;
22786     },
22787
22788     /**
22789      * Returns true if the selections are locked.
22790      * @return {Boolean}
22791      */
22792     isLocked : function(){
22793         return this.locked;
22794     }
22795 });
22796 /**
22797  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22798  * @class Roo.bootstrap.Table.RowSelectionModel
22799  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22800  * It supports multiple selections and keyboard selection/navigation. 
22801  * @constructor
22802  * @param {Object} config
22803  */
22804
22805 Roo.bootstrap.Table.RowSelectionModel = function(config){
22806     Roo.apply(this, config);
22807     this.selections = new Roo.util.MixedCollection(false, function(o){
22808         return o.id;
22809     });
22810
22811     this.last = false;
22812     this.lastActive = false;
22813
22814     this.addEvents({
22815         /**
22816              * @event selectionchange
22817              * Fires when the selection changes
22818              * @param {SelectionModel} this
22819              */
22820             "selectionchange" : true,
22821         /**
22822              * @event afterselectionchange
22823              * Fires after the selection changes (eg. by key press or clicking)
22824              * @param {SelectionModel} this
22825              */
22826             "afterselectionchange" : true,
22827         /**
22828              * @event beforerowselect
22829              * Fires when a row is selected being selected, return false to cancel.
22830              * @param {SelectionModel} this
22831              * @param {Number} rowIndex The selected index
22832              * @param {Boolean} keepExisting False if other selections will be cleared
22833              */
22834             "beforerowselect" : true,
22835         /**
22836              * @event rowselect
22837              * Fires when a row is selected.
22838              * @param {SelectionModel} this
22839              * @param {Number} rowIndex The selected index
22840              * @param {Roo.data.Record} r The record
22841              */
22842             "rowselect" : true,
22843         /**
22844              * @event rowdeselect
22845              * Fires when a row is deselected.
22846              * @param {SelectionModel} this
22847              * @param {Number} rowIndex The selected index
22848              */
22849         "rowdeselect" : true
22850     });
22851     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22852     this.locked = false;
22853  };
22854
22855 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22856     /**
22857      * @cfg {Boolean} singleSelect
22858      * True to allow selection of only one row at a time (defaults to false)
22859      */
22860     singleSelect : false,
22861
22862     // private
22863     initEvents : function()
22864     {
22865
22866         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22867         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22868         //}else{ // allow click to work like normal
22869          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22870         //}
22871         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22872         this.grid.on("rowclick", this.handleMouseDown, this);
22873         
22874         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22875             "up" : function(e){
22876                 if(!e.shiftKey){
22877                     this.selectPrevious(e.shiftKey);
22878                 }else if(this.last !== false && this.lastActive !== false){
22879                     var last = this.last;
22880                     this.selectRange(this.last,  this.lastActive-1);
22881                     this.grid.getView().focusRow(this.lastActive);
22882                     if(last !== false){
22883                         this.last = last;
22884                     }
22885                 }else{
22886                     this.selectFirstRow();
22887                 }
22888                 this.fireEvent("afterselectionchange", this);
22889             },
22890             "down" : function(e){
22891                 if(!e.shiftKey){
22892                     this.selectNext(e.shiftKey);
22893                 }else if(this.last !== false && this.lastActive !== false){
22894                     var last = this.last;
22895                     this.selectRange(this.last,  this.lastActive+1);
22896                     this.grid.getView().focusRow(this.lastActive);
22897                     if(last !== false){
22898                         this.last = last;
22899                     }
22900                 }else{
22901                     this.selectFirstRow();
22902                 }
22903                 this.fireEvent("afterselectionchange", this);
22904             },
22905             scope: this
22906         });
22907         this.grid.store.on('load', function(){
22908             this.selections.clear();
22909         },this);
22910         /*
22911         var view = this.grid.view;
22912         view.on("refresh", this.onRefresh, this);
22913         view.on("rowupdated", this.onRowUpdated, this);
22914         view.on("rowremoved", this.onRemove, this);
22915         */
22916     },
22917
22918     // private
22919     onRefresh : function()
22920     {
22921         var ds = this.grid.store, i, v = this.grid.view;
22922         var s = this.selections;
22923         s.each(function(r){
22924             if((i = ds.indexOfId(r.id)) != -1){
22925                 v.onRowSelect(i);
22926             }else{
22927                 s.remove(r);
22928             }
22929         });
22930     },
22931
22932     // private
22933     onRemove : function(v, index, r){
22934         this.selections.remove(r);
22935     },
22936
22937     // private
22938     onRowUpdated : function(v, index, r){
22939         if(this.isSelected(r)){
22940             v.onRowSelect(index);
22941         }
22942     },
22943
22944     /**
22945      * Select records.
22946      * @param {Array} records The records to select
22947      * @param {Boolean} keepExisting (optional) True to keep existing selections
22948      */
22949     selectRecords : function(records, keepExisting)
22950     {
22951         if(!keepExisting){
22952             this.clearSelections();
22953         }
22954             var ds = this.grid.store;
22955         for(var i = 0, len = records.length; i < len; i++){
22956             this.selectRow(ds.indexOf(records[i]), true);
22957         }
22958     },
22959
22960     /**
22961      * Gets the number of selected rows.
22962      * @return {Number}
22963      */
22964     getCount : function(){
22965         return this.selections.length;
22966     },
22967
22968     /**
22969      * Selects the first row in the grid.
22970      */
22971     selectFirstRow : function(){
22972         this.selectRow(0);
22973     },
22974
22975     /**
22976      * Select the last row.
22977      * @param {Boolean} keepExisting (optional) True to keep existing selections
22978      */
22979     selectLastRow : function(keepExisting){
22980         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22981         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22982     },
22983
22984     /**
22985      * Selects the row immediately following the last selected row.
22986      * @param {Boolean} keepExisting (optional) True to keep existing selections
22987      */
22988     selectNext : function(keepExisting)
22989     {
22990             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22991             this.selectRow(this.last+1, keepExisting);
22992             this.grid.getView().focusRow(this.last);
22993         }
22994     },
22995
22996     /**
22997      * Selects the row that precedes the last selected row.
22998      * @param {Boolean} keepExisting (optional) True to keep existing selections
22999      */
23000     selectPrevious : function(keepExisting){
23001         if(this.last){
23002             this.selectRow(this.last-1, keepExisting);
23003             this.grid.getView().focusRow(this.last);
23004         }
23005     },
23006
23007     /**
23008      * Returns the selected records
23009      * @return {Array} Array of selected records
23010      */
23011     getSelections : function(){
23012         return [].concat(this.selections.items);
23013     },
23014
23015     /**
23016      * Returns the first selected record.
23017      * @return {Record}
23018      */
23019     getSelected : function(){
23020         return this.selections.itemAt(0);
23021     },
23022
23023
23024     /**
23025      * Clears all selections.
23026      */
23027     clearSelections : function(fast)
23028     {
23029         if(this.locked) {
23030             return;
23031         }
23032         if(fast !== true){
23033                 var ds = this.grid.store;
23034             var s = this.selections;
23035             s.each(function(r){
23036                 this.deselectRow(ds.indexOfId(r.id));
23037             }, this);
23038             s.clear();
23039         }else{
23040             this.selections.clear();
23041         }
23042         this.last = false;
23043     },
23044
23045
23046     /**
23047      * Selects all rows.
23048      */
23049     selectAll : function(){
23050         if(this.locked) {
23051             return;
23052         }
23053         this.selections.clear();
23054         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23055             this.selectRow(i, true);
23056         }
23057     },
23058
23059     /**
23060      * Returns True if there is a selection.
23061      * @return {Boolean}
23062      */
23063     hasSelection : function(){
23064         return this.selections.length > 0;
23065     },
23066
23067     /**
23068      * Returns True if the specified row is selected.
23069      * @param {Number/Record} record The record or index of the record to check
23070      * @return {Boolean}
23071      */
23072     isSelected : function(index){
23073             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23074         return (r && this.selections.key(r.id) ? true : false);
23075     },
23076
23077     /**
23078      * Returns True if the specified record id is selected.
23079      * @param {String} id The id of record to check
23080      * @return {Boolean}
23081      */
23082     isIdSelected : function(id){
23083         return (this.selections.key(id) ? true : false);
23084     },
23085
23086
23087     // private
23088     handleMouseDBClick : function(e, t){
23089         
23090     },
23091     // private
23092     handleMouseDown : function(e, t)
23093     {
23094             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23095         if(this.isLocked() || rowIndex < 0 ){
23096             return;
23097         };
23098         if(e.shiftKey && this.last !== false){
23099             var last = this.last;
23100             this.selectRange(last, rowIndex, e.ctrlKey);
23101             this.last = last; // reset the last
23102             t.focus();
23103     
23104         }else{
23105             var isSelected = this.isSelected(rowIndex);
23106             //Roo.log("select row:" + rowIndex);
23107             if(isSelected){
23108                 this.deselectRow(rowIndex);
23109             } else {
23110                         this.selectRow(rowIndex, true);
23111             }
23112     
23113             /*
23114                 if(e.button !== 0 && isSelected){
23115                 alert('rowIndex 2: ' + rowIndex);
23116                     view.focusRow(rowIndex);
23117                 }else if(e.ctrlKey && isSelected){
23118                     this.deselectRow(rowIndex);
23119                 }else if(!isSelected){
23120                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23121                     view.focusRow(rowIndex);
23122                 }
23123             */
23124         }
23125         this.fireEvent("afterselectionchange", this);
23126     },
23127     // private
23128     handleDragableRowClick :  function(grid, rowIndex, e) 
23129     {
23130         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23131             this.selectRow(rowIndex, false);
23132             grid.view.focusRow(rowIndex);
23133              this.fireEvent("afterselectionchange", this);
23134         }
23135     },
23136     
23137     /**
23138      * Selects multiple rows.
23139      * @param {Array} rows Array of the indexes of the row to select
23140      * @param {Boolean} keepExisting (optional) True to keep existing selections
23141      */
23142     selectRows : function(rows, keepExisting){
23143         if(!keepExisting){
23144             this.clearSelections();
23145         }
23146         for(var i = 0, len = rows.length; i < len; i++){
23147             this.selectRow(rows[i], true);
23148         }
23149     },
23150
23151     /**
23152      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23153      * @param {Number} startRow The index of the first row in the range
23154      * @param {Number} endRow The index of the last row in the range
23155      * @param {Boolean} keepExisting (optional) True to retain existing selections
23156      */
23157     selectRange : function(startRow, endRow, keepExisting){
23158         if(this.locked) {
23159             return;
23160         }
23161         if(!keepExisting){
23162             this.clearSelections();
23163         }
23164         if(startRow <= endRow){
23165             for(var i = startRow; i <= endRow; i++){
23166                 this.selectRow(i, true);
23167             }
23168         }else{
23169             for(var i = startRow; i >= endRow; i--){
23170                 this.selectRow(i, true);
23171             }
23172         }
23173     },
23174
23175     /**
23176      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23177      * @param {Number} startRow The index of the first row in the range
23178      * @param {Number} endRow The index of the last row in the range
23179      */
23180     deselectRange : function(startRow, endRow, preventViewNotify){
23181         if(this.locked) {
23182             return;
23183         }
23184         for(var i = startRow; i <= endRow; i++){
23185             this.deselectRow(i, preventViewNotify);
23186         }
23187     },
23188
23189     /**
23190      * Selects a row.
23191      * @param {Number} row The index of the row to select
23192      * @param {Boolean} keepExisting (optional) True to keep existing selections
23193      */
23194     selectRow : function(index, keepExisting, preventViewNotify)
23195     {
23196             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23197             return;
23198         }
23199         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23200             if(!keepExisting || this.singleSelect){
23201                 this.clearSelections();
23202             }
23203             
23204             var r = this.grid.store.getAt(index);
23205             //console.log('selectRow - record id :' + r.id);
23206             
23207             this.selections.add(r);
23208             this.last = this.lastActive = index;
23209             if(!preventViewNotify){
23210                 var proxy = new Roo.Element(
23211                                 this.grid.getRowDom(index)
23212                 );
23213                 proxy.addClass('bg-info info');
23214             }
23215             this.fireEvent("rowselect", this, index, r);
23216             this.fireEvent("selectionchange", this);
23217         }
23218     },
23219
23220     /**
23221      * Deselects a row.
23222      * @param {Number} row The index of the row to deselect
23223      */
23224     deselectRow : function(index, preventViewNotify)
23225     {
23226         if(this.locked) {
23227             return;
23228         }
23229         if(this.last == index){
23230             this.last = false;
23231         }
23232         if(this.lastActive == index){
23233             this.lastActive = false;
23234         }
23235         
23236         var r = this.grid.store.getAt(index);
23237         if (!r) {
23238             return;
23239         }
23240         
23241         this.selections.remove(r);
23242         //.console.log('deselectRow - record id :' + r.id);
23243         if(!preventViewNotify){
23244         
23245             var proxy = new Roo.Element(
23246                 this.grid.getRowDom(index)
23247             );
23248             proxy.removeClass('bg-info info');
23249         }
23250         this.fireEvent("rowdeselect", this, index);
23251         this.fireEvent("selectionchange", this);
23252     },
23253
23254     // private
23255     restoreLast : function(){
23256         if(this._last){
23257             this.last = this._last;
23258         }
23259     },
23260
23261     // private
23262     acceptsNav : function(row, col, cm){
23263         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23264     },
23265
23266     // private
23267     onEditorKey : function(field, e){
23268         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23269         if(k == e.TAB){
23270             e.stopEvent();
23271             ed.completeEdit();
23272             if(e.shiftKey){
23273                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23274             }else{
23275                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23276             }
23277         }else if(k == e.ENTER && !e.ctrlKey){
23278             e.stopEvent();
23279             ed.completeEdit();
23280             if(e.shiftKey){
23281                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23282             }else{
23283                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23284             }
23285         }else if(k == e.ESC){
23286             ed.cancelEdit();
23287         }
23288         if(newCell){
23289             g.startEditing(newCell[0], newCell[1]);
23290         }
23291     }
23292 });
23293 /*
23294  * Based on:
23295  * Ext JS Library 1.1.1
23296  * Copyright(c) 2006-2007, Ext JS, LLC.
23297  *
23298  * Originally Released Under LGPL - original licence link has changed is not relivant.
23299  *
23300  * Fork - LGPL
23301  * <script type="text/javascript">
23302  */
23303  
23304 /**
23305  * @class Roo.bootstrap.PagingToolbar
23306  * @extends Roo.bootstrap.NavSimplebar
23307  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23308  * @constructor
23309  * Create a new PagingToolbar
23310  * @param {Object} config The config object
23311  * @param {Roo.data.Store} store
23312  */
23313 Roo.bootstrap.PagingToolbar = function(config)
23314 {
23315     // old args format still supported... - xtype is prefered..
23316         // created from xtype...
23317     
23318     this.ds = config.dataSource;
23319     
23320     if (config.store && !this.ds) {
23321         this.store= Roo.factory(config.store, Roo.data);
23322         this.ds = this.store;
23323         this.ds.xmodule = this.xmodule || false;
23324     }
23325     
23326     this.toolbarItems = [];
23327     if (config.items) {
23328         this.toolbarItems = config.items;
23329     }
23330     
23331     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23332     
23333     this.cursor = 0;
23334     
23335     if (this.ds) { 
23336         this.bind(this.ds);
23337     }
23338     
23339     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23340     
23341 };
23342
23343 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23344     /**
23345      * @cfg {Roo.data.Store} dataSource
23346      * The underlying data store providing the paged data
23347      */
23348     /**
23349      * @cfg {String/HTMLElement/Element} container
23350      * container The id or element that will contain the toolbar
23351      */
23352     /**
23353      * @cfg {Boolean} displayInfo
23354      * True to display the displayMsg (defaults to false)
23355      */
23356     /**
23357      * @cfg {Number} pageSize
23358      * The number of records to display per page (defaults to 20)
23359      */
23360     pageSize: 20,
23361     /**
23362      * @cfg {String} displayMsg
23363      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23364      */
23365     displayMsg : 'Displaying {0} - {1} of {2}',
23366     /**
23367      * @cfg {String} emptyMsg
23368      * The message to display when no records are found (defaults to "No data to display")
23369      */
23370     emptyMsg : 'No data to display',
23371     /**
23372      * Customizable piece of the default paging text (defaults to "Page")
23373      * @type String
23374      */
23375     beforePageText : "Page",
23376     /**
23377      * Customizable piece of the default paging text (defaults to "of %0")
23378      * @type String
23379      */
23380     afterPageText : "of {0}",
23381     /**
23382      * Customizable piece of the default paging text (defaults to "First Page")
23383      * @type String
23384      */
23385     firstText : "First Page",
23386     /**
23387      * Customizable piece of the default paging text (defaults to "Previous Page")
23388      * @type String
23389      */
23390     prevText : "Previous Page",
23391     /**
23392      * Customizable piece of the default paging text (defaults to "Next Page")
23393      * @type String
23394      */
23395     nextText : "Next Page",
23396     /**
23397      * Customizable piece of the default paging text (defaults to "Last Page")
23398      * @type String
23399      */
23400     lastText : "Last Page",
23401     /**
23402      * Customizable piece of the default paging text (defaults to "Refresh")
23403      * @type String
23404      */
23405     refreshText : "Refresh",
23406
23407     buttons : false,
23408     // private
23409     onRender : function(ct, position) 
23410     {
23411         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23412         this.navgroup.parentId = this.id;
23413         this.navgroup.onRender(this.el, null);
23414         // add the buttons to the navgroup
23415         
23416         if(this.displayInfo){
23417             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23418             this.displayEl = this.el.select('.x-paging-info', true).first();
23419 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23420 //            this.displayEl = navel.el.select('span',true).first();
23421         }
23422         
23423         var _this = this;
23424         
23425         if(this.buttons){
23426             Roo.each(_this.buttons, function(e){ // this might need to use render????
23427                Roo.factory(e).onRender(_this.el, null);
23428             });
23429         }
23430             
23431         Roo.each(_this.toolbarItems, function(e) {
23432             _this.navgroup.addItem(e);
23433         });
23434         
23435         
23436         this.first = this.navgroup.addItem({
23437             tooltip: this.firstText,
23438             cls: "prev",
23439             icon : 'fa fa-backward',
23440             disabled: true,
23441             preventDefault: true,
23442             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23443         });
23444         
23445         this.prev =  this.navgroup.addItem({
23446             tooltip: this.prevText,
23447             cls: "prev",
23448             icon : 'fa fa-step-backward',
23449             disabled: true,
23450             preventDefault: true,
23451             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23452         });
23453     //this.addSeparator();
23454         
23455         
23456         var field = this.navgroup.addItem( {
23457             tagtype : 'span',
23458             cls : 'x-paging-position',
23459             
23460             html : this.beforePageText  +
23461                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23462                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23463          } ); //?? escaped?
23464         
23465         this.field = field.el.select('input', true).first();
23466         this.field.on("keydown", this.onPagingKeydown, this);
23467         this.field.on("focus", function(){this.dom.select();});
23468     
23469     
23470         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23471         //this.field.setHeight(18);
23472         //this.addSeparator();
23473         this.next = this.navgroup.addItem({
23474             tooltip: this.nextText,
23475             cls: "next",
23476             html : ' <i class="fa fa-step-forward">',
23477             disabled: true,
23478             preventDefault: true,
23479             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23480         });
23481         this.last = this.navgroup.addItem({
23482             tooltip: this.lastText,
23483             icon : 'fa fa-forward',
23484             cls: "next",
23485             disabled: true,
23486             preventDefault: true,
23487             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23488         });
23489     //this.addSeparator();
23490         this.loading = this.navgroup.addItem({
23491             tooltip: this.refreshText,
23492             icon: 'fa fa-refresh',
23493             preventDefault: true,
23494             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23495         });
23496         
23497     },
23498
23499     // private
23500     updateInfo : function(){
23501         if(this.displayEl){
23502             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23503             var msg = count == 0 ?
23504                 this.emptyMsg :
23505                 String.format(
23506                     this.displayMsg,
23507                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23508                 );
23509             this.displayEl.update(msg);
23510         }
23511     },
23512
23513     // private
23514     onLoad : function(ds, r, o){
23515        this.cursor = o.params ? o.params.start : 0;
23516        var d = this.getPageData(),
23517             ap = d.activePage,
23518             ps = d.pages;
23519         
23520        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23521        this.field.dom.value = ap;
23522        this.first.setDisabled(ap == 1);
23523        this.prev.setDisabled(ap == 1);
23524        this.next.setDisabled(ap == ps);
23525        this.last.setDisabled(ap == ps);
23526        this.loading.enable();
23527        this.updateInfo();
23528     },
23529
23530     // private
23531     getPageData : function(){
23532         var total = this.ds.getTotalCount();
23533         return {
23534             total : total,
23535             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23536             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23537         };
23538     },
23539
23540     // private
23541     onLoadError : function(){
23542         this.loading.enable();
23543     },
23544
23545     // private
23546     onPagingKeydown : function(e){
23547         var k = e.getKey();
23548         var d = this.getPageData();
23549         if(k == e.RETURN){
23550             var v = this.field.dom.value, pageNum;
23551             if(!v || isNaN(pageNum = parseInt(v, 10))){
23552                 this.field.dom.value = d.activePage;
23553                 return;
23554             }
23555             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23556             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23557             e.stopEvent();
23558         }
23559         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))
23560         {
23561           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23562           this.field.dom.value = pageNum;
23563           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23564           e.stopEvent();
23565         }
23566         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23567         {
23568           var v = this.field.dom.value, pageNum; 
23569           var increment = (e.shiftKey) ? 10 : 1;
23570           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23571                 increment *= -1;
23572           }
23573           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23574             this.field.dom.value = d.activePage;
23575             return;
23576           }
23577           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23578           {
23579             this.field.dom.value = parseInt(v, 10) + increment;
23580             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23581             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23582           }
23583           e.stopEvent();
23584         }
23585     },
23586
23587     // private
23588     beforeLoad : function(){
23589         if(this.loading){
23590             this.loading.disable();
23591         }
23592     },
23593
23594     // private
23595     onClick : function(which){
23596         
23597         var ds = this.ds;
23598         if (!ds) {
23599             return;
23600         }
23601         
23602         switch(which){
23603             case "first":
23604                 ds.load({params:{start: 0, limit: this.pageSize}});
23605             break;
23606             case "prev":
23607                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23608             break;
23609             case "next":
23610                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23611             break;
23612             case "last":
23613                 var total = ds.getTotalCount();
23614                 var extra = total % this.pageSize;
23615                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23616                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23617             break;
23618             case "refresh":
23619                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23620             break;
23621         }
23622     },
23623
23624     /**
23625      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23626      * @param {Roo.data.Store} store The data store to unbind
23627      */
23628     unbind : function(ds){
23629         ds.un("beforeload", this.beforeLoad, this);
23630         ds.un("load", this.onLoad, this);
23631         ds.un("loadexception", this.onLoadError, this);
23632         ds.un("remove", this.updateInfo, this);
23633         ds.un("add", this.updateInfo, this);
23634         this.ds = undefined;
23635     },
23636
23637     /**
23638      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23639      * @param {Roo.data.Store} store The data store to bind
23640      */
23641     bind : function(ds){
23642         ds.on("beforeload", this.beforeLoad, this);
23643         ds.on("load", this.onLoad, this);
23644         ds.on("loadexception", this.onLoadError, this);
23645         ds.on("remove", this.updateInfo, this);
23646         ds.on("add", this.updateInfo, this);
23647         this.ds = ds;
23648     }
23649 });/*
23650  * - LGPL
23651  *
23652  * element
23653  * 
23654  */
23655
23656 /**
23657  * @class Roo.bootstrap.MessageBar
23658  * @extends Roo.bootstrap.Component
23659  * Bootstrap MessageBar class
23660  * @cfg {String} html contents of the MessageBar
23661  * @cfg {String} weight (info | success | warning | danger) default info
23662  * @cfg {String} beforeClass insert the bar before the given class
23663  * @cfg {Boolean} closable (true | false) default false
23664  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23665  * 
23666  * @constructor
23667  * Create a new Element
23668  * @param {Object} config The config object
23669  */
23670
23671 Roo.bootstrap.MessageBar = function(config){
23672     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23673 };
23674
23675 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23676     
23677     html: '',
23678     weight: 'info',
23679     closable: false,
23680     fixed: false,
23681     beforeClass: 'bootstrap-sticky-wrap',
23682     
23683     getAutoCreate : function(){
23684         
23685         var cfg = {
23686             tag: 'div',
23687             cls: 'alert alert-dismissable alert-' + this.weight,
23688             cn: [
23689                 {
23690                     tag: 'span',
23691                     cls: 'message',
23692                     html: this.html || ''
23693                 }
23694             ]
23695         };
23696         
23697         if(this.fixed){
23698             cfg.cls += ' alert-messages-fixed';
23699         }
23700         
23701         if(this.closable){
23702             cfg.cn.push({
23703                 tag: 'button',
23704                 cls: 'close',
23705                 html: 'x'
23706             });
23707         }
23708         
23709         return cfg;
23710     },
23711     
23712     onRender : function(ct, position)
23713     {
23714         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23715         
23716         if(!this.el){
23717             var cfg = Roo.apply({},  this.getAutoCreate());
23718             cfg.id = Roo.id();
23719             
23720             if (this.cls) {
23721                 cfg.cls += ' ' + this.cls;
23722             }
23723             if (this.style) {
23724                 cfg.style = this.style;
23725             }
23726             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23727             
23728             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23729         }
23730         
23731         this.el.select('>button.close').on('click', this.hide, this);
23732         
23733     },
23734     
23735     show : function()
23736     {
23737         if (!this.rendered) {
23738             this.render();
23739         }
23740         
23741         this.el.show();
23742         
23743         this.fireEvent('show', this);
23744         
23745     },
23746     
23747     hide : function()
23748     {
23749         if (!this.rendered) {
23750             this.render();
23751         }
23752         
23753         this.el.hide();
23754         
23755         this.fireEvent('hide', this);
23756     },
23757     
23758     update : function()
23759     {
23760 //        var e = this.el.dom.firstChild;
23761 //        
23762 //        if(this.closable){
23763 //            e = e.nextSibling;
23764 //        }
23765 //        
23766 //        e.data = this.html || '';
23767
23768         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23769     }
23770    
23771 });
23772
23773  
23774
23775      /*
23776  * - LGPL
23777  *
23778  * Graph
23779  * 
23780  */
23781
23782
23783 /**
23784  * @class Roo.bootstrap.Graph
23785  * @extends Roo.bootstrap.Component
23786  * Bootstrap Graph class
23787 > Prameters
23788  -sm {number} sm 4
23789  -md {number} md 5
23790  @cfg {String} graphtype  bar | vbar | pie
23791  @cfg {number} g_x coodinator | centre x (pie)
23792  @cfg {number} g_y coodinator | centre y (pie)
23793  @cfg {number} g_r radius (pie)
23794  @cfg {number} g_height height of the chart (respected by all elements in the set)
23795  @cfg {number} g_width width of the chart (respected by all elements in the set)
23796  @cfg {Object} title The title of the chart
23797     
23798  -{Array}  values
23799  -opts (object) options for the chart 
23800      o {
23801      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23802      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23803      o vgutter (number)
23804      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.
23805      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23806      o to
23807      o stretch (boolean)
23808      o }
23809  -opts (object) options for the pie
23810      o{
23811      o cut
23812      o startAngle (number)
23813      o endAngle (number)
23814      } 
23815  *
23816  * @constructor
23817  * Create a new Input
23818  * @param {Object} config The config object
23819  */
23820
23821 Roo.bootstrap.Graph = function(config){
23822     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23823     
23824     this.addEvents({
23825         // img events
23826         /**
23827          * @event click
23828          * The img click event for the img.
23829          * @param {Roo.EventObject} e
23830          */
23831         "click" : true
23832     });
23833 };
23834
23835 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23836     
23837     sm: 4,
23838     md: 5,
23839     graphtype: 'bar',
23840     g_height: 250,
23841     g_width: 400,
23842     g_x: 50,
23843     g_y: 50,
23844     g_r: 30,
23845     opts:{
23846         //g_colors: this.colors,
23847         g_type: 'soft',
23848         g_gutter: '20%'
23849
23850     },
23851     title : false,
23852
23853     getAutoCreate : function(){
23854         
23855         var cfg = {
23856             tag: 'div',
23857             html : null
23858         };
23859         
23860         
23861         return  cfg;
23862     },
23863
23864     onRender : function(ct,position){
23865         
23866         
23867         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23868         
23869         if (typeof(Raphael) == 'undefined') {
23870             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23871             return;
23872         }
23873         
23874         this.raphael = Raphael(this.el.dom);
23875         
23876                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23877                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23878                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23879                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23880                 /*
23881                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23882                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23883                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23884                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23885                 
23886                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23887                 r.barchart(330, 10, 300, 220, data1);
23888                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23889                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23890                 */
23891                 
23892                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23893                 // r.barchart(30, 30, 560, 250,  xdata, {
23894                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23895                 //     axis : "0 0 1 1",
23896                 //     axisxlabels :  xdata
23897                 //     //yvalues : cols,
23898                    
23899                 // });
23900 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23901 //        
23902 //        this.load(null,xdata,{
23903 //                axis : "0 0 1 1",
23904 //                axisxlabels :  xdata
23905 //                });
23906
23907     },
23908
23909     load : function(graphtype,xdata,opts)
23910     {
23911         this.raphael.clear();
23912         if(!graphtype) {
23913             graphtype = this.graphtype;
23914         }
23915         if(!opts){
23916             opts = this.opts;
23917         }
23918         var r = this.raphael,
23919             fin = function () {
23920                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23921             },
23922             fout = function () {
23923                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23924             },
23925             pfin = function() {
23926                 this.sector.stop();
23927                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23928
23929                 if (this.label) {
23930                     this.label[0].stop();
23931                     this.label[0].attr({ r: 7.5 });
23932                     this.label[1].attr({ "font-weight": 800 });
23933                 }
23934             },
23935             pfout = function() {
23936                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23937
23938                 if (this.label) {
23939                     this.label[0].animate({ r: 5 }, 500, "bounce");
23940                     this.label[1].attr({ "font-weight": 400 });
23941                 }
23942             };
23943
23944         switch(graphtype){
23945             case 'bar':
23946                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23947                 break;
23948             case 'hbar':
23949                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23950                 break;
23951             case 'pie':
23952 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23953 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23954 //            
23955                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23956                 
23957                 break;
23958
23959         }
23960         
23961         if(this.title){
23962             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23963         }
23964         
23965     },
23966     
23967     setTitle: function(o)
23968     {
23969         this.title = o;
23970     },
23971     
23972     initEvents: function() {
23973         
23974         if(!this.href){
23975             this.el.on('click', this.onClick, this);
23976         }
23977     },
23978     
23979     onClick : function(e)
23980     {
23981         Roo.log('img onclick');
23982         this.fireEvent('click', this, e);
23983     }
23984    
23985 });
23986
23987  
23988 /*
23989  * - LGPL
23990  *
23991  * numberBox
23992  * 
23993  */
23994 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23995
23996 /**
23997  * @class Roo.bootstrap.dash.NumberBox
23998  * @extends Roo.bootstrap.Component
23999  * Bootstrap NumberBox class
24000  * @cfg {String} headline Box headline
24001  * @cfg {String} content Box content
24002  * @cfg {String} icon Box icon
24003  * @cfg {String} footer Footer text
24004  * @cfg {String} fhref Footer href
24005  * 
24006  * @constructor
24007  * Create a new NumberBox
24008  * @param {Object} config The config object
24009  */
24010
24011
24012 Roo.bootstrap.dash.NumberBox = function(config){
24013     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24014     
24015 };
24016
24017 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24018     
24019     headline : '',
24020     content : '',
24021     icon : '',
24022     footer : '',
24023     fhref : '',
24024     ficon : '',
24025     
24026     getAutoCreate : function(){
24027         
24028         var cfg = {
24029             tag : 'div',
24030             cls : 'small-box ',
24031             cn : [
24032                 {
24033                     tag : 'div',
24034                     cls : 'inner',
24035                     cn :[
24036                         {
24037                             tag : 'h3',
24038                             cls : 'roo-headline',
24039                             html : this.headline
24040                         },
24041                         {
24042                             tag : 'p',
24043                             cls : 'roo-content',
24044                             html : this.content
24045                         }
24046                     ]
24047                 }
24048             ]
24049         };
24050         
24051         if(this.icon){
24052             cfg.cn.push({
24053                 tag : 'div',
24054                 cls : 'icon',
24055                 cn :[
24056                     {
24057                         tag : 'i',
24058                         cls : 'ion ' + this.icon
24059                     }
24060                 ]
24061             });
24062         }
24063         
24064         if(this.footer){
24065             var footer = {
24066                 tag : 'a',
24067                 cls : 'small-box-footer',
24068                 href : this.fhref || '#',
24069                 html : this.footer
24070             };
24071             
24072             cfg.cn.push(footer);
24073             
24074         }
24075         
24076         return  cfg;
24077     },
24078
24079     onRender : function(ct,position){
24080         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24081
24082
24083        
24084                 
24085     },
24086
24087     setHeadline: function (value)
24088     {
24089         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24090     },
24091     
24092     setFooter: function (value, href)
24093     {
24094         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24095         
24096         if(href){
24097             this.el.select('a.small-box-footer',true).first().attr('href', href);
24098         }
24099         
24100     },
24101
24102     setContent: function (value)
24103     {
24104         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24105     },
24106
24107     initEvents: function() 
24108     {   
24109         
24110     }
24111     
24112 });
24113
24114  
24115 /*
24116  * - LGPL
24117  *
24118  * TabBox
24119  * 
24120  */
24121 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24122
24123 /**
24124  * @class Roo.bootstrap.dash.TabBox
24125  * @extends Roo.bootstrap.Component
24126  * Bootstrap TabBox class
24127  * @cfg {String} title Title of the TabBox
24128  * @cfg {String} icon Icon of the TabBox
24129  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24130  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24131  * 
24132  * @constructor
24133  * Create a new TabBox
24134  * @param {Object} config The config object
24135  */
24136
24137
24138 Roo.bootstrap.dash.TabBox = function(config){
24139     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24140     this.addEvents({
24141         // raw events
24142         /**
24143          * @event addpane
24144          * When a pane is added
24145          * @param {Roo.bootstrap.dash.TabPane} pane
24146          */
24147         "addpane" : true,
24148         /**
24149          * @event activatepane
24150          * When a pane is activated
24151          * @param {Roo.bootstrap.dash.TabPane} pane
24152          */
24153         "activatepane" : true
24154         
24155          
24156     });
24157     
24158     this.panes = [];
24159 };
24160
24161 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24162
24163     title : '',
24164     icon : false,
24165     showtabs : true,
24166     tabScrollable : false,
24167     
24168     getChildContainer : function()
24169     {
24170         return this.el.select('.tab-content', true).first();
24171     },
24172     
24173     getAutoCreate : function(){
24174         
24175         var header = {
24176             tag: 'li',
24177             cls: 'pull-left header',
24178             html: this.title,
24179             cn : []
24180         };
24181         
24182         if(this.icon){
24183             header.cn.push({
24184                 tag: 'i',
24185                 cls: 'fa ' + this.icon
24186             });
24187         }
24188         
24189         var h = {
24190             tag: 'ul',
24191             cls: 'nav nav-tabs pull-right',
24192             cn: [
24193                 header
24194             ]
24195         };
24196         
24197         if(this.tabScrollable){
24198             h = {
24199                 tag: 'div',
24200                 cls: 'tab-header',
24201                 cn: [
24202                     {
24203                         tag: 'ul',
24204                         cls: 'nav nav-tabs pull-right',
24205                         cn: [
24206                             header
24207                         ]
24208                     }
24209                 ]
24210             };
24211         }
24212         
24213         var cfg = {
24214             tag: 'div',
24215             cls: 'nav-tabs-custom',
24216             cn: [
24217                 h,
24218                 {
24219                     tag: 'div',
24220                     cls: 'tab-content no-padding',
24221                     cn: []
24222                 }
24223             ]
24224         };
24225
24226         return  cfg;
24227     },
24228     initEvents : function()
24229     {
24230         //Roo.log('add add pane handler');
24231         this.on('addpane', this.onAddPane, this);
24232     },
24233      /**
24234      * Updates the box title
24235      * @param {String} html to set the title to.
24236      */
24237     setTitle : function(value)
24238     {
24239         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24240     },
24241     onAddPane : function(pane)
24242     {
24243         this.panes.push(pane);
24244         //Roo.log('addpane');
24245         //Roo.log(pane);
24246         // tabs are rendere left to right..
24247         if(!this.showtabs){
24248             return;
24249         }
24250         
24251         var ctr = this.el.select('.nav-tabs', true).first();
24252          
24253          
24254         var existing = ctr.select('.nav-tab',true);
24255         var qty = existing.getCount();;
24256         
24257         
24258         var tab = ctr.createChild({
24259             tag : 'li',
24260             cls : 'nav-tab' + (qty ? '' : ' active'),
24261             cn : [
24262                 {
24263                     tag : 'a',
24264                     href:'#',
24265                     html : pane.title
24266                 }
24267             ]
24268         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24269         pane.tab = tab;
24270         
24271         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24272         if (!qty) {
24273             pane.el.addClass('active');
24274         }
24275         
24276                 
24277     },
24278     onTabClick : function(ev,un,ob,pane)
24279     {
24280         //Roo.log('tab - prev default');
24281         ev.preventDefault();
24282         
24283         
24284         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24285         pane.tab.addClass('active');
24286         //Roo.log(pane.title);
24287         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24288         // technically we should have a deactivate event.. but maybe add later.
24289         // and it should not de-activate the selected tab...
24290         this.fireEvent('activatepane', pane);
24291         pane.el.addClass('active');
24292         pane.fireEvent('activate');
24293         
24294         
24295     },
24296     
24297     getActivePane : function()
24298     {
24299         var r = false;
24300         Roo.each(this.panes, function(p) {
24301             if(p.el.hasClass('active')){
24302                 r = p;
24303                 return false;
24304             }
24305             
24306             return;
24307         });
24308         
24309         return r;
24310     }
24311     
24312     
24313 });
24314
24315  
24316 /*
24317  * - LGPL
24318  *
24319  * Tab pane
24320  * 
24321  */
24322 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24323 /**
24324  * @class Roo.bootstrap.TabPane
24325  * @extends Roo.bootstrap.Component
24326  * Bootstrap TabPane class
24327  * @cfg {Boolean} active (false | true) Default false
24328  * @cfg {String} title title of panel
24329
24330  * 
24331  * @constructor
24332  * Create a new TabPane
24333  * @param {Object} config The config object
24334  */
24335
24336 Roo.bootstrap.dash.TabPane = function(config){
24337     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24338     
24339     this.addEvents({
24340         // raw events
24341         /**
24342          * @event activate
24343          * When a pane is activated
24344          * @param {Roo.bootstrap.dash.TabPane} pane
24345          */
24346         "activate" : true
24347          
24348     });
24349 };
24350
24351 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24352     
24353     active : false,
24354     title : '',
24355     
24356     // the tabBox that this is attached to.
24357     tab : false,
24358      
24359     getAutoCreate : function() 
24360     {
24361         var cfg = {
24362             tag: 'div',
24363             cls: 'tab-pane'
24364         };
24365         
24366         if(this.active){
24367             cfg.cls += ' active';
24368         }
24369         
24370         return cfg;
24371     },
24372     initEvents  : function()
24373     {
24374         //Roo.log('trigger add pane handler');
24375         this.parent().fireEvent('addpane', this)
24376     },
24377     
24378      /**
24379      * Updates the tab title 
24380      * @param {String} html to set the title to.
24381      */
24382     setTitle: function(str)
24383     {
24384         if (!this.tab) {
24385             return;
24386         }
24387         this.title = str;
24388         this.tab.select('a', true).first().dom.innerHTML = str;
24389         
24390     }
24391     
24392     
24393     
24394 });
24395
24396  
24397
24398
24399  /*
24400  * - LGPL
24401  *
24402  * menu
24403  * 
24404  */
24405 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24406
24407 /**
24408  * @class Roo.bootstrap.menu.Menu
24409  * @extends Roo.bootstrap.Component
24410  * Bootstrap Menu class - container for Menu
24411  * @cfg {String} html Text of the menu
24412  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24413  * @cfg {String} icon Font awesome icon
24414  * @cfg {String} pos Menu align to (top | bottom) default bottom
24415  * 
24416  * 
24417  * @constructor
24418  * Create a new Menu
24419  * @param {Object} config The config object
24420  */
24421
24422
24423 Roo.bootstrap.menu.Menu = function(config){
24424     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24425     
24426     this.addEvents({
24427         /**
24428          * @event beforeshow
24429          * Fires before this menu is displayed
24430          * @param {Roo.bootstrap.menu.Menu} this
24431          */
24432         beforeshow : true,
24433         /**
24434          * @event beforehide
24435          * Fires before this menu is hidden
24436          * @param {Roo.bootstrap.menu.Menu} this
24437          */
24438         beforehide : true,
24439         /**
24440          * @event show
24441          * Fires after this menu is displayed
24442          * @param {Roo.bootstrap.menu.Menu} this
24443          */
24444         show : true,
24445         /**
24446          * @event hide
24447          * Fires after this menu is hidden
24448          * @param {Roo.bootstrap.menu.Menu} this
24449          */
24450         hide : true,
24451         /**
24452          * @event click
24453          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24454          * @param {Roo.bootstrap.menu.Menu} this
24455          * @param {Roo.EventObject} e
24456          */
24457         click : true
24458     });
24459     
24460 };
24461
24462 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24463     
24464     submenu : false,
24465     html : '',
24466     weight : 'default',
24467     icon : false,
24468     pos : 'bottom',
24469     
24470     
24471     getChildContainer : function() {
24472         if(this.isSubMenu){
24473             return this.el;
24474         }
24475         
24476         return this.el.select('ul.dropdown-menu', true).first();  
24477     },
24478     
24479     getAutoCreate : function()
24480     {
24481         var text = [
24482             {
24483                 tag : 'span',
24484                 cls : 'roo-menu-text',
24485                 html : this.html
24486             }
24487         ];
24488         
24489         if(this.icon){
24490             text.unshift({
24491                 tag : 'i',
24492                 cls : 'fa ' + this.icon
24493             })
24494         }
24495         
24496         
24497         var cfg = {
24498             tag : 'div',
24499             cls : 'btn-group',
24500             cn : [
24501                 {
24502                     tag : 'button',
24503                     cls : 'dropdown-button btn btn-' + this.weight,
24504                     cn : text
24505                 },
24506                 {
24507                     tag : 'button',
24508                     cls : 'dropdown-toggle btn btn-' + this.weight,
24509                     cn : [
24510                         {
24511                             tag : 'span',
24512                             cls : 'caret'
24513                         }
24514                     ]
24515                 },
24516                 {
24517                     tag : 'ul',
24518                     cls : 'dropdown-menu'
24519                 }
24520             ]
24521             
24522         };
24523         
24524         if(this.pos == 'top'){
24525             cfg.cls += ' dropup';
24526         }
24527         
24528         if(this.isSubMenu){
24529             cfg = {
24530                 tag : 'ul',
24531                 cls : 'dropdown-menu'
24532             }
24533         }
24534         
24535         return cfg;
24536     },
24537     
24538     onRender : function(ct, position)
24539     {
24540         this.isSubMenu = ct.hasClass('dropdown-submenu');
24541         
24542         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24543     },
24544     
24545     initEvents : function() 
24546     {
24547         if(this.isSubMenu){
24548             return;
24549         }
24550         
24551         this.hidden = true;
24552         
24553         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24554         this.triggerEl.on('click', this.onTriggerPress, this);
24555         
24556         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24557         this.buttonEl.on('click', this.onClick, this);
24558         
24559     },
24560     
24561     list : function()
24562     {
24563         if(this.isSubMenu){
24564             return this.el;
24565         }
24566         
24567         return this.el.select('ul.dropdown-menu', true).first();
24568     },
24569     
24570     onClick : function(e)
24571     {
24572         this.fireEvent("click", this, e);
24573     },
24574     
24575     onTriggerPress  : function(e)
24576     {   
24577         if (this.isVisible()) {
24578             this.hide();
24579         } else {
24580             this.show();
24581         }
24582     },
24583     
24584     isVisible : function(){
24585         return !this.hidden;
24586     },
24587     
24588     show : function()
24589     {
24590         this.fireEvent("beforeshow", this);
24591         
24592         this.hidden = false;
24593         this.el.addClass('open');
24594         
24595         Roo.get(document).on("mouseup", this.onMouseUp, this);
24596         
24597         this.fireEvent("show", this);
24598         
24599         
24600     },
24601     
24602     hide : function()
24603     {
24604         this.fireEvent("beforehide", this);
24605         
24606         this.hidden = true;
24607         this.el.removeClass('open');
24608         
24609         Roo.get(document).un("mouseup", this.onMouseUp);
24610         
24611         this.fireEvent("hide", this);
24612     },
24613     
24614     onMouseUp : function()
24615     {
24616         this.hide();
24617     }
24618     
24619 });
24620
24621  
24622  /*
24623  * - LGPL
24624  *
24625  * menu item
24626  * 
24627  */
24628 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24629
24630 /**
24631  * @class Roo.bootstrap.menu.Item
24632  * @extends Roo.bootstrap.Component
24633  * Bootstrap MenuItem class
24634  * @cfg {Boolean} submenu (true | false) default false
24635  * @cfg {String} html text of the item
24636  * @cfg {String} href the link
24637  * @cfg {Boolean} disable (true | false) default false
24638  * @cfg {Boolean} preventDefault (true | false) default true
24639  * @cfg {String} icon Font awesome icon
24640  * @cfg {String} pos Submenu align to (left | right) default right 
24641  * 
24642  * 
24643  * @constructor
24644  * Create a new Item
24645  * @param {Object} config The config object
24646  */
24647
24648
24649 Roo.bootstrap.menu.Item = function(config){
24650     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24651     this.addEvents({
24652         /**
24653          * @event mouseover
24654          * Fires when the mouse is hovering over this menu
24655          * @param {Roo.bootstrap.menu.Item} this
24656          * @param {Roo.EventObject} e
24657          */
24658         mouseover : true,
24659         /**
24660          * @event mouseout
24661          * Fires when the mouse exits this menu
24662          * @param {Roo.bootstrap.menu.Item} this
24663          * @param {Roo.EventObject} e
24664          */
24665         mouseout : true,
24666         // raw events
24667         /**
24668          * @event click
24669          * The raw click event for the entire grid.
24670          * @param {Roo.EventObject} e
24671          */
24672         click : true
24673     });
24674 };
24675
24676 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24677     
24678     submenu : false,
24679     href : '',
24680     html : '',
24681     preventDefault: true,
24682     disable : false,
24683     icon : false,
24684     pos : 'right',
24685     
24686     getAutoCreate : function()
24687     {
24688         var text = [
24689             {
24690                 tag : 'span',
24691                 cls : 'roo-menu-item-text',
24692                 html : this.html
24693             }
24694         ];
24695         
24696         if(this.icon){
24697             text.unshift({
24698                 tag : 'i',
24699                 cls : 'fa ' + this.icon
24700             })
24701         }
24702         
24703         var cfg = {
24704             tag : 'li',
24705             cn : [
24706                 {
24707                     tag : 'a',
24708                     href : this.href || '#',
24709                     cn : text
24710                 }
24711             ]
24712         };
24713         
24714         if(this.disable){
24715             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24716         }
24717         
24718         if(this.submenu){
24719             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24720             
24721             if(this.pos == 'left'){
24722                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24723             }
24724         }
24725         
24726         return cfg;
24727     },
24728     
24729     initEvents : function() 
24730     {
24731         this.el.on('mouseover', this.onMouseOver, this);
24732         this.el.on('mouseout', this.onMouseOut, this);
24733         
24734         this.el.select('a', true).first().on('click', this.onClick, this);
24735         
24736     },
24737     
24738     onClick : function(e)
24739     {
24740         if(this.preventDefault){
24741             e.preventDefault();
24742         }
24743         
24744         this.fireEvent("click", this, e);
24745     },
24746     
24747     onMouseOver : function(e)
24748     {
24749         if(this.submenu && this.pos == 'left'){
24750             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24751         }
24752         
24753         this.fireEvent("mouseover", this, e);
24754     },
24755     
24756     onMouseOut : function(e)
24757     {
24758         this.fireEvent("mouseout", this, e);
24759     }
24760 });
24761
24762  
24763
24764  /*
24765  * - LGPL
24766  *
24767  * menu separator
24768  * 
24769  */
24770 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24771
24772 /**
24773  * @class Roo.bootstrap.menu.Separator
24774  * @extends Roo.bootstrap.Component
24775  * Bootstrap Separator class
24776  * 
24777  * @constructor
24778  * Create a new Separator
24779  * @param {Object} config The config object
24780  */
24781
24782
24783 Roo.bootstrap.menu.Separator = function(config){
24784     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24785 };
24786
24787 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24788     
24789     getAutoCreate : function(){
24790         var cfg = {
24791             tag : 'li',
24792             cls: 'divider'
24793         };
24794         
24795         return cfg;
24796     }
24797    
24798 });
24799
24800  
24801
24802  /*
24803  * - LGPL
24804  *
24805  * Tooltip
24806  * 
24807  */
24808
24809 /**
24810  * @class Roo.bootstrap.Tooltip
24811  * Bootstrap Tooltip class
24812  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24813  * to determine which dom element triggers the tooltip.
24814  * 
24815  * It needs to add support for additional attributes like tooltip-position
24816  * 
24817  * @constructor
24818  * Create a new Toolti
24819  * @param {Object} config The config object
24820  */
24821
24822 Roo.bootstrap.Tooltip = function(config){
24823     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24824 };
24825
24826 Roo.apply(Roo.bootstrap.Tooltip, {
24827     /**
24828      * @function init initialize tooltip monitoring.
24829      * @static
24830      */
24831     currentEl : false,
24832     currentTip : false,
24833     currentRegion : false,
24834     
24835     //  init : delay?
24836     
24837     init : function()
24838     {
24839         Roo.get(document).on('mouseover', this.enter ,this);
24840         Roo.get(document).on('mouseout', this.leave, this);
24841          
24842         
24843         this.currentTip = new Roo.bootstrap.Tooltip();
24844     },
24845     
24846     enter : function(ev)
24847     {
24848         var dom = ev.getTarget();
24849         
24850         //Roo.log(['enter',dom]);
24851         var el = Roo.fly(dom);
24852         if (this.currentEl) {
24853             //Roo.log(dom);
24854             //Roo.log(this.currentEl);
24855             //Roo.log(this.currentEl.contains(dom));
24856             if (this.currentEl == el) {
24857                 return;
24858             }
24859             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24860                 return;
24861             }
24862
24863         }
24864         
24865         if (this.currentTip.el) {
24866             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24867         }    
24868         //Roo.log(ev);
24869         
24870         if(!el || el.dom == document){
24871             return;
24872         }
24873         
24874         var bindEl = el;
24875         
24876         // you can not look for children, as if el is the body.. then everythign is the child..
24877         if (!el.attr('tooltip')) { //
24878             if (!el.select("[tooltip]").elements.length) {
24879                 return;
24880             }
24881             // is the mouse over this child...?
24882             bindEl = el.select("[tooltip]").first();
24883             var xy = ev.getXY();
24884             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24885                 //Roo.log("not in region.");
24886                 return;
24887             }
24888             //Roo.log("child element over..");
24889             
24890         }
24891         this.currentEl = bindEl;
24892         this.currentTip.bind(bindEl);
24893         this.currentRegion = Roo.lib.Region.getRegion(dom);
24894         this.currentTip.enter();
24895         
24896     },
24897     leave : function(ev)
24898     {
24899         var dom = ev.getTarget();
24900         //Roo.log(['leave',dom]);
24901         if (!this.currentEl) {
24902             return;
24903         }
24904         
24905         
24906         if (dom != this.currentEl.dom) {
24907             return;
24908         }
24909         var xy = ev.getXY();
24910         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24911             return;
24912         }
24913         // only activate leave if mouse cursor is outside... bounding box..
24914         
24915         
24916         
24917         
24918         if (this.currentTip) {
24919             this.currentTip.leave();
24920         }
24921         //Roo.log('clear currentEl');
24922         this.currentEl = false;
24923         
24924         
24925     },
24926     alignment : {
24927         'left' : ['r-l', [-2,0], 'right'],
24928         'right' : ['l-r', [2,0], 'left'],
24929         'bottom' : ['t-b', [0,2], 'top'],
24930         'top' : [ 'b-t', [0,-2], 'bottom']
24931     }
24932     
24933 });
24934
24935
24936 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24937     
24938     
24939     bindEl : false,
24940     
24941     delay : null, // can be { show : 300 , hide: 500}
24942     
24943     timeout : null,
24944     
24945     hoverState : null, //???
24946     
24947     placement : 'bottom', 
24948     
24949     getAutoCreate : function(){
24950     
24951         var cfg = {
24952            cls : 'tooltip',
24953            role : 'tooltip',
24954            cn : [
24955                 {
24956                     cls : 'tooltip-arrow'
24957                 },
24958                 {
24959                     cls : 'tooltip-inner'
24960                 }
24961            ]
24962         };
24963         
24964         return cfg;
24965     },
24966     bind : function(el)
24967     {
24968         this.bindEl = el;
24969     },
24970       
24971     
24972     enter : function () {
24973        
24974         if (this.timeout != null) {
24975             clearTimeout(this.timeout);
24976         }
24977         
24978         this.hoverState = 'in';
24979          //Roo.log("enter - show");
24980         if (!this.delay || !this.delay.show) {
24981             this.show();
24982             return;
24983         }
24984         var _t = this;
24985         this.timeout = setTimeout(function () {
24986             if (_t.hoverState == 'in') {
24987                 _t.show();
24988             }
24989         }, this.delay.show);
24990     },
24991     leave : function()
24992     {
24993         clearTimeout(this.timeout);
24994     
24995         this.hoverState = 'out';
24996          if (!this.delay || !this.delay.hide) {
24997             this.hide();
24998             return;
24999         }
25000        
25001         var _t = this;
25002         this.timeout = setTimeout(function () {
25003             //Roo.log("leave - timeout");
25004             
25005             if (_t.hoverState == 'out') {
25006                 _t.hide();
25007                 Roo.bootstrap.Tooltip.currentEl = false;
25008             }
25009         }, delay);
25010     },
25011     
25012     show : function ()
25013     {
25014         if (!this.el) {
25015             this.render(document.body);
25016         }
25017         // set content.
25018         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25019         
25020         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25021         
25022         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25023         
25024         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25025         
25026         var placement = typeof this.placement == 'function' ?
25027             this.placement.call(this, this.el, on_el) :
25028             this.placement;
25029             
25030         var autoToken = /\s?auto?\s?/i;
25031         var autoPlace = autoToken.test(placement);
25032         if (autoPlace) {
25033             placement = placement.replace(autoToken, '') || 'top';
25034         }
25035         
25036         //this.el.detach()
25037         //this.el.setXY([0,0]);
25038         this.el.show();
25039         //this.el.dom.style.display='block';
25040         
25041         //this.el.appendTo(on_el);
25042         
25043         var p = this.getPosition();
25044         var box = this.el.getBox();
25045         
25046         if (autoPlace) {
25047             // fixme..
25048         }
25049         
25050         var align = Roo.bootstrap.Tooltip.alignment[placement];
25051         
25052         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25053         
25054         if(placement == 'top' || placement == 'bottom'){
25055             if(xy[0] < 0){
25056                 placement = 'right';
25057             }
25058             
25059             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25060                 placement = 'left';
25061             }
25062             
25063             var scroll = Roo.select('body', true).first().getScroll();
25064             
25065             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25066                 placement = 'top';
25067             }
25068             
25069         }
25070         
25071         align = Roo.bootstrap.Tooltip.alignment[placement];
25072         
25073         this.el.alignTo(this.bindEl, align[0],align[1]);
25074         //var arrow = this.el.select('.arrow',true).first();
25075         //arrow.set(align[2], 
25076         
25077         this.el.addClass(placement);
25078         
25079         this.el.addClass('in fade');
25080         
25081         this.hoverState = null;
25082         
25083         if (this.el.hasClass('fade')) {
25084             // fade it?
25085         }
25086         
25087     },
25088     hide : function()
25089     {
25090          
25091         if (!this.el) {
25092             return;
25093         }
25094         //this.el.setXY([0,0]);
25095         this.el.removeClass('in');
25096         //this.el.hide();
25097         
25098     }
25099     
25100 });
25101  
25102
25103  /*
25104  * - LGPL
25105  *
25106  * Location Picker
25107  * 
25108  */
25109
25110 /**
25111  * @class Roo.bootstrap.LocationPicker
25112  * @extends Roo.bootstrap.Component
25113  * Bootstrap LocationPicker class
25114  * @cfg {Number} latitude Position when init default 0
25115  * @cfg {Number} longitude Position when init default 0
25116  * @cfg {Number} zoom default 15
25117  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25118  * @cfg {Boolean} mapTypeControl default false
25119  * @cfg {Boolean} disableDoubleClickZoom default false
25120  * @cfg {Boolean} scrollwheel default true
25121  * @cfg {Boolean} streetViewControl default false
25122  * @cfg {Number} radius default 0
25123  * @cfg {String} locationName
25124  * @cfg {Boolean} draggable default true
25125  * @cfg {Boolean} enableAutocomplete default false
25126  * @cfg {Boolean} enableReverseGeocode default true
25127  * @cfg {String} markerTitle
25128  * 
25129  * @constructor
25130  * Create a new LocationPicker
25131  * @param {Object} config The config object
25132  */
25133
25134
25135 Roo.bootstrap.LocationPicker = function(config){
25136     
25137     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25138     
25139     this.addEvents({
25140         /**
25141          * @event initial
25142          * Fires when the picker initialized.
25143          * @param {Roo.bootstrap.LocationPicker} this
25144          * @param {Google Location} location
25145          */
25146         initial : true,
25147         /**
25148          * @event positionchanged
25149          * Fires when the picker position changed.
25150          * @param {Roo.bootstrap.LocationPicker} this
25151          * @param {Google Location} location
25152          */
25153         positionchanged : true,
25154         /**
25155          * @event resize
25156          * Fires when the map resize.
25157          * @param {Roo.bootstrap.LocationPicker} this
25158          */
25159         resize : true,
25160         /**
25161          * @event show
25162          * Fires when the map show.
25163          * @param {Roo.bootstrap.LocationPicker} this
25164          */
25165         show : true,
25166         /**
25167          * @event hide
25168          * Fires when the map hide.
25169          * @param {Roo.bootstrap.LocationPicker} this
25170          */
25171         hide : true,
25172         /**
25173          * @event mapClick
25174          * Fires when click the map.
25175          * @param {Roo.bootstrap.LocationPicker} this
25176          * @param {Map event} e
25177          */
25178         mapClick : true,
25179         /**
25180          * @event mapRightClick
25181          * Fires when right click the map.
25182          * @param {Roo.bootstrap.LocationPicker} this
25183          * @param {Map event} e
25184          */
25185         mapRightClick : true,
25186         /**
25187          * @event markerClick
25188          * Fires when click the marker.
25189          * @param {Roo.bootstrap.LocationPicker} this
25190          * @param {Map event} e
25191          */
25192         markerClick : true,
25193         /**
25194          * @event markerRightClick
25195          * Fires when right click the marker.
25196          * @param {Roo.bootstrap.LocationPicker} this
25197          * @param {Map event} e
25198          */
25199         markerRightClick : true,
25200         /**
25201          * @event OverlayViewDraw
25202          * Fires when OverlayView Draw
25203          * @param {Roo.bootstrap.LocationPicker} this
25204          */
25205         OverlayViewDraw : true,
25206         /**
25207          * @event OverlayViewOnAdd
25208          * Fires when OverlayView Draw
25209          * @param {Roo.bootstrap.LocationPicker} this
25210          */
25211         OverlayViewOnAdd : true,
25212         /**
25213          * @event OverlayViewOnRemove
25214          * Fires when OverlayView Draw
25215          * @param {Roo.bootstrap.LocationPicker} this
25216          */
25217         OverlayViewOnRemove : true,
25218         /**
25219          * @event OverlayViewShow
25220          * Fires when OverlayView Draw
25221          * @param {Roo.bootstrap.LocationPicker} this
25222          * @param {Pixel} cpx
25223          */
25224         OverlayViewShow : true,
25225         /**
25226          * @event OverlayViewHide
25227          * Fires when OverlayView Draw
25228          * @param {Roo.bootstrap.LocationPicker} this
25229          */
25230         OverlayViewHide : true,
25231         /**
25232          * @event loadexception
25233          * Fires when load google lib failed.
25234          * @param {Roo.bootstrap.LocationPicker} this
25235          */
25236         loadexception : true
25237     });
25238         
25239 };
25240
25241 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25242     
25243     gMapContext: false,
25244     
25245     latitude: 0,
25246     longitude: 0,
25247     zoom: 15,
25248     mapTypeId: false,
25249     mapTypeControl: false,
25250     disableDoubleClickZoom: false,
25251     scrollwheel: true,
25252     streetViewControl: false,
25253     radius: 0,
25254     locationName: '',
25255     draggable: true,
25256     enableAutocomplete: false,
25257     enableReverseGeocode: true,
25258     markerTitle: '',
25259     
25260     getAutoCreate: function()
25261     {
25262
25263         var cfg = {
25264             tag: 'div',
25265             cls: 'roo-location-picker'
25266         };
25267         
25268         return cfg
25269     },
25270     
25271     initEvents: function(ct, position)
25272     {       
25273         if(!this.el.getWidth() || this.isApplied()){
25274             return;
25275         }
25276         
25277         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25278         
25279         this.initial();
25280     },
25281     
25282     initial: function()
25283     {
25284         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25285             this.fireEvent('loadexception', this);
25286             return;
25287         }
25288         
25289         if(!this.mapTypeId){
25290             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25291         }
25292         
25293         this.gMapContext = this.GMapContext();
25294         
25295         this.initOverlayView();
25296         
25297         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25298         
25299         var _this = this;
25300                 
25301         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25302             _this.setPosition(_this.gMapContext.marker.position);
25303         });
25304         
25305         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25306             _this.fireEvent('mapClick', this, event);
25307             
25308         });
25309
25310         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25311             _this.fireEvent('mapRightClick', this, event);
25312             
25313         });
25314         
25315         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25316             _this.fireEvent('markerClick', this, event);
25317             
25318         });
25319
25320         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25321             _this.fireEvent('markerRightClick', this, event);
25322             
25323         });
25324         
25325         this.setPosition(this.gMapContext.location);
25326         
25327         this.fireEvent('initial', this, this.gMapContext.location);
25328     },
25329     
25330     initOverlayView: function()
25331     {
25332         var _this = this;
25333         
25334         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25335             
25336             draw: function()
25337             {
25338                 _this.fireEvent('OverlayViewDraw', _this);
25339             },
25340             
25341             onAdd: function()
25342             {
25343                 _this.fireEvent('OverlayViewOnAdd', _this);
25344             },
25345             
25346             onRemove: function()
25347             {
25348                 _this.fireEvent('OverlayViewOnRemove', _this);
25349             },
25350             
25351             show: function(cpx)
25352             {
25353                 _this.fireEvent('OverlayViewShow', _this, cpx);
25354             },
25355             
25356             hide: function()
25357             {
25358                 _this.fireEvent('OverlayViewHide', _this);
25359             }
25360             
25361         });
25362     },
25363     
25364     fromLatLngToContainerPixel: function(event)
25365     {
25366         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25367     },
25368     
25369     isApplied: function() 
25370     {
25371         return this.getGmapContext() == false ? false : true;
25372     },
25373     
25374     getGmapContext: function() 
25375     {
25376         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25377     },
25378     
25379     GMapContext: function() 
25380     {
25381         var position = new google.maps.LatLng(this.latitude, this.longitude);
25382         
25383         var _map = new google.maps.Map(this.el.dom, {
25384             center: position,
25385             zoom: this.zoom,
25386             mapTypeId: this.mapTypeId,
25387             mapTypeControl: this.mapTypeControl,
25388             disableDoubleClickZoom: this.disableDoubleClickZoom,
25389             scrollwheel: this.scrollwheel,
25390             streetViewControl: this.streetViewControl,
25391             locationName: this.locationName,
25392             draggable: this.draggable,
25393             enableAutocomplete: this.enableAutocomplete,
25394             enableReverseGeocode: this.enableReverseGeocode
25395         });
25396         
25397         var _marker = new google.maps.Marker({
25398             position: position,
25399             map: _map,
25400             title: this.markerTitle,
25401             draggable: this.draggable
25402         });
25403         
25404         return {
25405             map: _map,
25406             marker: _marker,
25407             circle: null,
25408             location: position,
25409             radius: this.radius,
25410             locationName: this.locationName,
25411             addressComponents: {
25412                 formatted_address: null,
25413                 addressLine1: null,
25414                 addressLine2: null,
25415                 streetName: null,
25416                 streetNumber: null,
25417                 city: null,
25418                 district: null,
25419                 state: null,
25420                 stateOrProvince: null
25421             },
25422             settings: this,
25423             domContainer: this.el.dom,
25424             geodecoder: new google.maps.Geocoder()
25425         };
25426     },
25427     
25428     drawCircle: function(center, radius, options) 
25429     {
25430         if (this.gMapContext.circle != null) {
25431             this.gMapContext.circle.setMap(null);
25432         }
25433         if (radius > 0) {
25434             radius *= 1;
25435             options = Roo.apply({}, options, {
25436                 strokeColor: "#0000FF",
25437                 strokeOpacity: .35,
25438                 strokeWeight: 2,
25439                 fillColor: "#0000FF",
25440                 fillOpacity: .2
25441             });
25442             
25443             options.map = this.gMapContext.map;
25444             options.radius = radius;
25445             options.center = center;
25446             this.gMapContext.circle = new google.maps.Circle(options);
25447             return this.gMapContext.circle;
25448         }
25449         
25450         return null;
25451     },
25452     
25453     setPosition: function(location) 
25454     {
25455         this.gMapContext.location = location;
25456         this.gMapContext.marker.setPosition(location);
25457         this.gMapContext.map.panTo(location);
25458         this.drawCircle(location, this.gMapContext.radius, {});
25459         
25460         var _this = this;
25461         
25462         if (this.gMapContext.settings.enableReverseGeocode) {
25463             this.gMapContext.geodecoder.geocode({
25464                 latLng: this.gMapContext.location
25465             }, function(results, status) {
25466                 
25467                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25468                     _this.gMapContext.locationName = results[0].formatted_address;
25469                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25470                     
25471                     _this.fireEvent('positionchanged', this, location);
25472                 }
25473             });
25474             
25475             return;
25476         }
25477         
25478         this.fireEvent('positionchanged', this, location);
25479     },
25480     
25481     resize: function()
25482     {
25483         google.maps.event.trigger(this.gMapContext.map, "resize");
25484         
25485         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25486         
25487         this.fireEvent('resize', this);
25488     },
25489     
25490     setPositionByLatLng: function(latitude, longitude)
25491     {
25492         this.setPosition(new google.maps.LatLng(latitude, longitude));
25493     },
25494     
25495     getCurrentPosition: function() 
25496     {
25497         return {
25498             latitude: this.gMapContext.location.lat(),
25499             longitude: this.gMapContext.location.lng()
25500         };
25501     },
25502     
25503     getAddressName: function() 
25504     {
25505         return this.gMapContext.locationName;
25506     },
25507     
25508     getAddressComponents: function() 
25509     {
25510         return this.gMapContext.addressComponents;
25511     },
25512     
25513     address_component_from_google_geocode: function(address_components) 
25514     {
25515         var result = {};
25516         
25517         for (var i = 0; i < address_components.length; i++) {
25518             var component = address_components[i];
25519             if (component.types.indexOf("postal_code") >= 0) {
25520                 result.postalCode = component.short_name;
25521             } else if (component.types.indexOf("street_number") >= 0) {
25522                 result.streetNumber = component.short_name;
25523             } else if (component.types.indexOf("route") >= 0) {
25524                 result.streetName = component.short_name;
25525             } else if (component.types.indexOf("neighborhood") >= 0) {
25526                 result.city = component.short_name;
25527             } else if (component.types.indexOf("locality") >= 0) {
25528                 result.city = component.short_name;
25529             } else if (component.types.indexOf("sublocality") >= 0) {
25530                 result.district = component.short_name;
25531             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25532                 result.stateOrProvince = component.short_name;
25533             } else if (component.types.indexOf("country") >= 0) {
25534                 result.country = component.short_name;
25535             }
25536         }
25537         
25538         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25539         result.addressLine2 = "";
25540         return result;
25541     },
25542     
25543     setZoomLevel: function(zoom)
25544     {
25545         this.gMapContext.map.setZoom(zoom);
25546     },
25547     
25548     show: function()
25549     {
25550         if(!this.el){
25551             return;
25552         }
25553         
25554         this.el.show();
25555         
25556         this.resize();
25557         
25558         this.fireEvent('show', this);
25559     },
25560     
25561     hide: function()
25562     {
25563         if(!this.el){
25564             return;
25565         }
25566         
25567         this.el.hide();
25568         
25569         this.fireEvent('hide', this);
25570     }
25571     
25572 });
25573
25574 Roo.apply(Roo.bootstrap.LocationPicker, {
25575     
25576     OverlayView : function(map, options)
25577     {
25578         options = options || {};
25579         
25580         this.setMap(map);
25581     }
25582     
25583     
25584 });/*
25585  * - LGPL
25586  *
25587  * Alert
25588  * 
25589  */
25590
25591 /**
25592  * @class Roo.bootstrap.Alert
25593  * @extends Roo.bootstrap.Component
25594  * Bootstrap Alert class
25595  * @cfg {String} title The title of alert
25596  * @cfg {String} html The content of alert
25597  * @cfg {String} weight (  success | info | warning | danger )
25598  * @cfg {String} faicon font-awesomeicon
25599  * 
25600  * @constructor
25601  * Create a new alert
25602  * @param {Object} config The config object
25603  */
25604
25605
25606 Roo.bootstrap.Alert = function(config){
25607     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25608     
25609 };
25610
25611 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25612     
25613     title: '',
25614     html: '',
25615     weight: false,
25616     faicon: false,
25617     
25618     getAutoCreate : function()
25619     {
25620         
25621         var cfg = {
25622             tag : 'div',
25623             cls : 'alert',
25624             cn : [
25625                 {
25626                     tag : 'i',
25627                     cls : 'roo-alert-icon'
25628                     
25629                 },
25630                 {
25631                     tag : 'b',
25632                     cls : 'roo-alert-title',
25633                     html : this.title
25634                 },
25635                 {
25636                     tag : 'span',
25637                     cls : 'roo-alert-text',
25638                     html : this.html
25639                 }
25640             ]
25641         };
25642         
25643         if(this.faicon){
25644             cfg.cn[0].cls += ' fa ' + this.faicon;
25645         }
25646         
25647         if(this.weight){
25648             cfg.cls += ' alert-' + this.weight;
25649         }
25650         
25651         return cfg;
25652     },
25653     
25654     initEvents: function() 
25655     {
25656         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25657     },
25658     
25659     setTitle : function(str)
25660     {
25661         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25662     },
25663     
25664     setText : function(str)
25665     {
25666         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25667     },
25668     
25669     setWeight : function(weight)
25670     {
25671         if(this.weight){
25672             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25673         }
25674         
25675         this.weight = weight;
25676         
25677         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25678     },
25679     
25680     setIcon : function(icon)
25681     {
25682         if(this.faicon){
25683             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25684         }
25685         
25686         this.faicon = icon;
25687         
25688         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25689     },
25690     
25691     hide: function() 
25692     {
25693         this.el.hide();   
25694     },
25695     
25696     show: function() 
25697     {  
25698         this.el.show();   
25699     }
25700     
25701 });
25702
25703  
25704 /*
25705 * Licence: LGPL
25706 */
25707
25708 /**
25709  * @class Roo.bootstrap.UploadCropbox
25710  * @extends Roo.bootstrap.Component
25711  * Bootstrap UploadCropbox class
25712  * @cfg {String} emptyText show when image has been loaded
25713  * @cfg {String} rotateNotify show when image too small to rotate
25714  * @cfg {Number} errorTimeout default 3000
25715  * @cfg {Number} minWidth default 300
25716  * @cfg {Number} minHeight default 300
25717  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25718  * @cfg {Boolean} isDocument (true|false) default false
25719  * @cfg {String} url action url
25720  * @cfg {String} paramName default 'imageUpload'
25721  * @cfg {String} method default POST
25722  * @cfg {Boolean} loadMask (true|false) default true
25723  * @cfg {Boolean} loadingText default 'Loading...'
25724  * 
25725  * @constructor
25726  * Create a new UploadCropbox
25727  * @param {Object} config The config object
25728  */
25729
25730 Roo.bootstrap.UploadCropbox = function(config){
25731     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25732     
25733     this.addEvents({
25734         /**
25735          * @event beforeselectfile
25736          * Fire before select file
25737          * @param {Roo.bootstrap.UploadCropbox} this
25738          */
25739         "beforeselectfile" : true,
25740         /**
25741          * @event initial
25742          * Fire after initEvent
25743          * @param {Roo.bootstrap.UploadCropbox} this
25744          */
25745         "initial" : true,
25746         /**
25747          * @event crop
25748          * Fire after initEvent
25749          * @param {Roo.bootstrap.UploadCropbox} this
25750          * @param {String} data
25751          */
25752         "crop" : true,
25753         /**
25754          * @event prepare
25755          * Fire when preparing the file data
25756          * @param {Roo.bootstrap.UploadCropbox} this
25757          * @param {Object} file
25758          */
25759         "prepare" : true,
25760         /**
25761          * @event exception
25762          * Fire when get exception
25763          * @param {Roo.bootstrap.UploadCropbox} this
25764          * @param {XMLHttpRequest} xhr
25765          */
25766         "exception" : true,
25767         /**
25768          * @event beforeloadcanvas
25769          * Fire before load the canvas
25770          * @param {Roo.bootstrap.UploadCropbox} this
25771          * @param {String} src
25772          */
25773         "beforeloadcanvas" : true,
25774         /**
25775          * @event trash
25776          * Fire when trash image
25777          * @param {Roo.bootstrap.UploadCropbox} this
25778          */
25779         "trash" : true,
25780         /**
25781          * @event download
25782          * Fire when download the image
25783          * @param {Roo.bootstrap.UploadCropbox} this
25784          */
25785         "download" : true,
25786         /**
25787          * @event footerbuttonclick
25788          * Fire when footerbuttonclick
25789          * @param {Roo.bootstrap.UploadCropbox} this
25790          * @param {String} type
25791          */
25792         "footerbuttonclick" : true,
25793         /**
25794          * @event resize
25795          * Fire when resize
25796          * @param {Roo.bootstrap.UploadCropbox} this
25797          */
25798         "resize" : true,
25799         /**
25800          * @event rotate
25801          * Fire when rotate the image
25802          * @param {Roo.bootstrap.UploadCropbox} this
25803          * @param {String} pos
25804          */
25805         "rotate" : true,
25806         /**
25807          * @event inspect
25808          * Fire when inspect the file
25809          * @param {Roo.bootstrap.UploadCropbox} this
25810          * @param {Object} file
25811          */
25812         "inspect" : true,
25813         /**
25814          * @event upload
25815          * Fire when xhr upload the file
25816          * @param {Roo.bootstrap.UploadCropbox} this
25817          * @param {Object} data
25818          */
25819         "upload" : true,
25820         /**
25821          * @event arrange
25822          * Fire when arrange the file data
25823          * @param {Roo.bootstrap.UploadCropbox} this
25824          * @param {Object} formData
25825          */
25826         "arrange" : true
25827     });
25828     
25829     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25830 };
25831
25832 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25833     
25834     emptyText : 'Click to upload image',
25835     rotateNotify : 'Image is too small to rotate',
25836     errorTimeout : 3000,
25837     scale : 0,
25838     baseScale : 1,
25839     rotate : 0,
25840     dragable : false,
25841     pinching : false,
25842     mouseX : 0,
25843     mouseY : 0,
25844     cropData : false,
25845     minWidth : 300,
25846     minHeight : 300,
25847     file : false,
25848     exif : {},
25849     baseRotate : 1,
25850     cropType : 'image/jpeg',
25851     buttons : false,
25852     canvasLoaded : false,
25853     isDocument : false,
25854     method : 'POST',
25855     paramName : 'imageUpload',
25856     loadMask : true,
25857     loadingText : 'Loading...',
25858     maskEl : false,
25859     
25860     getAutoCreate : function()
25861     {
25862         var cfg = {
25863             tag : 'div',
25864             cls : 'roo-upload-cropbox',
25865             cn : [
25866                 {
25867                     tag : 'input',
25868                     cls : 'roo-upload-cropbox-selector',
25869                     type : 'file'
25870                 },
25871                 {
25872                     tag : 'div',
25873                     cls : 'roo-upload-cropbox-body',
25874                     style : 'cursor:pointer',
25875                     cn : [
25876                         {
25877                             tag : 'div',
25878                             cls : 'roo-upload-cropbox-preview'
25879                         },
25880                         {
25881                             tag : 'div',
25882                             cls : 'roo-upload-cropbox-thumb'
25883                         },
25884                         {
25885                             tag : 'div',
25886                             cls : 'roo-upload-cropbox-empty-notify',
25887                             html : this.emptyText
25888                         },
25889                         {
25890                             tag : 'div',
25891                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25892                             html : this.rotateNotify
25893                         }
25894                     ]
25895                 },
25896                 {
25897                     tag : 'div',
25898                     cls : 'roo-upload-cropbox-footer',
25899                     cn : {
25900                         tag : 'div',
25901                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25902                         cn : []
25903                     }
25904                 }
25905             ]
25906         };
25907         
25908         return cfg;
25909     },
25910     
25911     onRender : function(ct, position)
25912     {
25913         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25914         
25915         if (this.buttons.length) {
25916             
25917             Roo.each(this.buttons, function(bb) {
25918                 
25919                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25920                 
25921                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25922                 
25923             }, this);
25924         }
25925         
25926         if(this.loadMask){
25927             this.maskEl = this.el;
25928         }
25929     },
25930     
25931     initEvents : function()
25932     {
25933         this.urlAPI = (window.createObjectURL && window) || 
25934                                 (window.URL && URL.revokeObjectURL && URL) || 
25935                                 (window.webkitURL && webkitURL);
25936                         
25937         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25938         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25939         
25940         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25941         this.selectorEl.hide();
25942         
25943         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25944         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25945         
25946         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25947         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25948         this.thumbEl.hide();
25949         
25950         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25951         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25952         
25953         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25954         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25955         this.errorEl.hide();
25956         
25957         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25958         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25959         this.footerEl.hide();
25960         
25961         this.setThumbBoxSize();
25962         
25963         this.bind();
25964         
25965         this.resize();
25966         
25967         this.fireEvent('initial', this);
25968     },
25969
25970     bind : function()
25971     {
25972         var _this = this;
25973         
25974         window.addEventListener("resize", function() { _this.resize(); } );
25975         
25976         this.bodyEl.on('click', this.beforeSelectFile, this);
25977         
25978         if(Roo.isTouch){
25979             this.bodyEl.on('touchstart', this.onTouchStart, this);
25980             this.bodyEl.on('touchmove', this.onTouchMove, this);
25981             this.bodyEl.on('touchend', this.onTouchEnd, this);
25982         }
25983         
25984         if(!Roo.isTouch){
25985             this.bodyEl.on('mousedown', this.onMouseDown, this);
25986             this.bodyEl.on('mousemove', this.onMouseMove, this);
25987             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25988             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25989             Roo.get(document).on('mouseup', this.onMouseUp, this);
25990         }
25991         
25992         this.selectorEl.on('change', this.onFileSelected, this);
25993     },
25994     
25995     reset : function()
25996     {    
25997         this.scale = 0;
25998         this.baseScale = 1;
25999         this.rotate = 0;
26000         this.baseRotate = 1;
26001         this.dragable = false;
26002         this.pinching = false;
26003         this.mouseX = 0;
26004         this.mouseY = 0;
26005         this.cropData = false;
26006         this.notifyEl.dom.innerHTML = this.emptyText;
26007         
26008         this.selectorEl.dom.value = '';
26009         
26010     },
26011     
26012     resize : function()
26013     {
26014         if(this.fireEvent('resize', this) != false){
26015             this.setThumbBoxPosition();
26016             this.setCanvasPosition();
26017         }
26018     },
26019     
26020     onFooterButtonClick : function(e, el, o, type)
26021     {
26022         switch (type) {
26023             case 'rotate-left' :
26024                 this.onRotateLeft(e);
26025                 break;
26026             case 'rotate-right' :
26027                 this.onRotateRight(e);
26028                 break;
26029             case 'picture' :
26030                 this.beforeSelectFile(e);
26031                 break;
26032             case 'trash' :
26033                 this.trash(e);
26034                 break;
26035             case 'crop' :
26036                 this.crop(e);
26037                 break;
26038             case 'download' :
26039                 this.download(e);
26040                 break;
26041             default :
26042                 break;
26043         }
26044         
26045         this.fireEvent('footerbuttonclick', this, type);
26046     },
26047     
26048     beforeSelectFile : function(e)
26049     {
26050         e.preventDefault();
26051         
26052         if(this.fireEvent('beforeselectfile', this) != false){
26053             this.selectorEl.dom.click();
26054         }
26055     },
26056     
26057     onFileSelected : function(e)
26058     {
26059         e.preventDefault();
26060         
26061         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26062             return;
26063         }
26064         
26065         var file = this.selectorEl.dom.files[0];
26066         
26067         if(this.fireEvent('inspect', this, file) != false){
26068             this.prepare(file);
26069         }
26070         
26071     },
26072     
26073     trash : function(e)
26074     {
26075         this.fireEvent('trash', this);
26076     },
26077     
26078     download : function(e)
26079     {
26080         this.fireEvent('download', this);
26081     },
26082     
26083     loadCanvas : function(src)
26084     {   
26085         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26086             
26087             this.reset();
26088             
26089             this.imageEl = document.createElement('img');
26090             
26091             var _this = this;
26092             
26093             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26094             
26095             this.imageEl.src = src;
26096         }
26097     },
26098     
26099     onLoadCanvas : function()
26100     {   
26101         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26102         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26103         
26104         this.bodyEl.un('click', this.beforeSelectFile, this);
26105         
26106         this.notifyEl.hide();
26107         this.thumbEl.show();
26108         this.footerEl.show();
26109         
26110         this.baseRotateLevel();
26111         
26112         if(this.isDocument){
26113             this.setThumbBoxSize();
26114         }
26115         
26116         this.setThumbBoxPosition();
26117         
26118         this.baseScaleLevel();
26119         
26120         this.draw();
26121         
26122         this.resize();
26123         
26124         this.canvasLoaded = true;
26125         
26126         if(this.loadMask){
26127             this.maskEl.unmask();
26128         }
26129         
26130     },
26131     
26132     setCanvasPosition : function()
26133     {   
26134         if(!this.canvasEl){
26135             return;
26136         }
26137         
26138         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26139         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26140         
26141         this.previewEl.setLeft(pw);
26142         this.previewEl.setTop(ph);
26143         
26144     },
26145     
26146     onMouseDown : function(e)
26147     {   
26148         e.stopEvent();
26149         
26150         this.dragable = true;
26151         this.pinching = false;
26152         
26153         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26154             this.dragable = false;
26155             return;
26156         }
26157         
26158         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26159         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26160         
26161     },
26162     
26163     onMouseMove : function(e)
26164     {   
26165         e.stopEvent();
26166         
26167         if(!this.canvasLoaded){
26168             return;
26169         }
26170         
26171         if (!this.dragable){
26172             return;
26173         }
26174         
26175         var minX = Math.ceil(this.thumbEl.getLeft(true));
26176         var minY = Math.ceil(this.thumbEl.getTop(true));
26177         
26178         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26179         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26180         
26181         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26182         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26183         
26184         x = x - this.mouseX;
26185         y = y - this.mouseY;
26186         
26187         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26188         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26189         
26190         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26191         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26192         
26193         this.previewEl.setLeft(bgX);
26194         this.previewEl.setTop(bgY);
26195         
26196         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26197         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26198     },
26199     
26200     onMouseUp : function(e)
26201     {   
26202         e.stopEvent();
26203         
26204         this.dragable = false;
26205     },
26206     
26207     onMouseWheel : function(e)
26208     {   
26209         e.stopEvent();
26210         
26211         this.startScale = this.scale;
26212         
26213         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26214         
26215         if(!this.zoomable()){
26216             this.scale = this.startScale;
26217             return;
26218         }
26219         
26220         this.draw();
26221         
26222         return;
26223     },
26224     
26225     zoomable : function()
26226     {
26227         var minScale = this.thumbEl.getWidth() / this.minWidth;
26228         
26229         if(this.minWidth < this.minHeight){
26230             minScale = this.thumbEl.getHeight() / this.minHeight;
26231         }
26232         
26233         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26234         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26235         
26236         if(
26237                 this.isDocument &&
26238                 (this.rotate == 0 || this.rotate == 180) && 
26239                 (
26240                     width > this.imageEl.OriginWidth || 
26241                     height > this.imageEl.OriginHeight ||
26242                     (width < this.minWidth && height < this.minHeight)
26243                 )
26244         ){
26245             return false;
26246         }
26247         
26248         if(
26249                 this.isDocument &&
26250                 (this.rotate == 90 || this.rotate == 270) && 
26251                 (
26252                     width > this.imageEl.OriginWidth || 
26253                     height > this.imageEl.OriginHeight ||
26254                     (width < this.minHeight && height < this.minWidth)
26255                 )
26256         ){
26257             return false;
26258         }
26259         
26260         if(
26261                 !this.isDocument &&
26262                 (this.rotate == 0 || this.rotate == 180) && 
26263                 (
26264                     width < this.minWidth || 
26265                     width > this.imageEl.OriginWidth || 
26266                     height < this.minHeight || 
26267                     height > this.imageEl.OriginHeight
26268                 )
26269         ){
26270             return false;
26271         }
26272         
26273         if(
26274                 !this.isDocument &&
26275                 (this.rotate == 90 || this.rotate == 270) && 
26276                 (
26277                     width < this.minHeight || 
26278                     width > this.imageEl.OriginWidth || 
26279                     height < this.minWidth || 
26280                     height > this.imageEl.OriginHeight
26281                 )
26282         ){
26283             return false;
26284         }
26285         
26286         return true;
26287         
26288     },
26289     
26290     onRotateLeft : function(e)
26291     {   
26292         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26293             
26294             var minScale = this.thumbEl.getWidth() / this.minWidth;
26295             
26296             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26297             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26298             
26299             this.startScale = this.scale;
26300             
26301             while (this.getScaleLevel() < minScale){
26302             
26303                 this.scale = this.scale + 1;
26304                 
26305                 if(!this.zoomable()){
26306                     break;
26307                 }
26308                 
26309                 if(
26310                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26311                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26312                 ){
26313                     continue;
26314                 }
26315                 
26316                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26317
26318                 this.draw();
26319                 
26320                 return;
26321             }
26322             
26323             this.scale = this.startScale;
26324             
26325             this.onRotateFail();
26326             
26327             return false;
26328         }
26329         
26330         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26331
26332         if(this.isDocument){
26333             this.setThumbBoxSize();
26334             this.setThumbBoxPosition();
26335             this.setCanvasPosition();
26336         }
26337         
26338         this.draw();
26339         
26340         this.fireEvent('rotate', this, 'left');
26341         
26342     },
26343     
26344     onRotateRight : function(e)
26345     {
26346         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26347             
26348             var minScale = this.thumbEl.getWidth() / this.minWidth;
26349         
26350             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26351             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26352             
26353             this.startScale = this.scale;
26354             
26355             while (this.getScaleLevel() < minScale){
26356             
26357                 this.scale = this.scale + 1;
26358                 
26359                 if(!this.zoomable()){
26360                     break;
26361                 }
26362                 
26363                 if(
26364                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26365                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26366                 ){
26367                     continue;
26368                 }
26369                 
26370                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26371
26372                 this.draw();
26373                 
26374                 return;
26375             }
26376             
26377             this.scale = this.startScale;
26378             
26379             this.onRotateFail();
26380             
26381             return false;
26382         }
26383         
26384         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26385
26386         if(this.isDocument){
26387             this.setThumbBoxSize();
26388             this.setThumbBoxPosition();
26389             this.setCanvasPosition();
26390         }
26391         
26392         this.draw();
26393         
26394         this.fireEvent('rotate', this, 'right');
26395     },
26396     
26397     onRotateFail : function()
26398     {
26399         this.errorEl.show(true);
26400         
26401         var _this = this;
26402         
26403         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26404     },
26405     
26406     draw : function()
26407     {
26408         this.previewEl.dom.innerHTML = '';
26409         
26410         var canvasEl = document.createElement("canvas");
26411         
26412         var contextEl = canvasEl.getContext("2d");
26413         
26414         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26415         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26416         var center = this.imageEl.OriginWidth / 2;
26417         
26418         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26419             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26420             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26421             center = this.imageEl.OriginHeight / 2;
26422         }
26423         
26424         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26425         
26426         contextEl.translate(center, center);
26427         contextEl.rotate(this.rotate * Math.PI / 180);
26428
26429         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26430         
26431         this.canvasEl = document.createElement("canvas");
26432         
26433         this.contextEl = this.canvasEl.getContext("2d");
26434         
26435         switch (this.rotate) {
26436             case 0 :
26437                 
26438                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26439                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26440                 
26441                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26442                 
26443                 break;
26444             case 90 : 
26445                 
26446                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26447                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26448                 
26449                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26450                     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);
26451                     break;
26452                 }
26453                 
26454                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26455                 
26456                 break;
26457             case 180 :
26458                 
26459                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26460                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26461                 
26462                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26463                     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);
26464                     break;
26465                 }
26466                 
26467                 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);
26468                 
26469                 break;
26470             case 270 :
26471                 
26472                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26473                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26474         
26475                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26476                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26477                     break;
26478                 }
26479                 
26480                 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);
26481                 
26482                 break;
26483             default : 
26484                 break;
26485         }
26486         
26487         this.previewEl.appendChild(this.canvasEl);
26488         
26489         this.setCanvasPosition();
26490     },
26491     
26492     crop : function()
26493     {
26494         if(!this.canvasLoaded){
26495             return;
26496         }
26497         
26498         var imageCanvas = document.createElement("canvas");
26499         
26500         var imageContext = imageCanvas.getContext("2d");
26501         
26502         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26503         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26504         
26505         var center = imageCanvas.width / 2;
26506         
26507         imageContext.translate(center, center);
26508         
26509         imageContext.rotate(this.rotate * Math.PI / 180);
26510         
26511         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26512         
26513         var canvas = document.createElement("canvas");
26514         
26515         var context = canvas.getContext("2d");
26516                 
26517         canvas.width = this.minWidth;
26518         canvas.height = this.minHeight;
26519
26520         switch (this.rotate) {
26521             case 0 :
26522                 
26523                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26524                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26525                 
26526                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26527                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26528                 
26529                 var targetWidth = this.minWidth - 2 * x;
26530                 var targetHeight = this.minHeight - 2 * y;
26531                 
26532                 var scale = 1;
26533                 
26534                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26535                     scale = targetWidth / width;
26536                 }
26537                 
26538                 if(x > 0 && y == 0){
26539                     scale = targetHeight / height;
26540                 }
26541                 
26542                 if(x > 0 && y > 0){
26543                     scale = targetWidth / width;
26544                     
26545                     if(width < height){
26546                         scale = targetHeight / height;
26547                     }
26548                 }
26549                 
26550                 context.scale(scale, scale);
26551                 
26552                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26553                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26554
26555                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26556                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26557
26558                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26559                 
26560                 break;
26561             case 90 : 
26562                 
26563                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26564                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (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) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26599                 
26600                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26601                 
26602                 break;
26603             case 180 :
26604                 
26605                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26606                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26607                 
26608                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26609                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26610                 
26611                 var targetWidth = this.minWidth - 2 * x;
26612                 var targetHeight = this.minHeight - 2 * y;
26613                 
26614                 var scale = 1;
26615                 
26616                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26617                     scale = targetWidth / width;
26618                 }
26619                 
26620                 if(x > 0 && y == 0){
26621                     scale = targetHeight / height;
26622                 }
26623                 
26624                 if(x > 0 && y > 0){
26625                     scale = targetWidth / width;
26626                     
26627                     if(width < height){
26628                         scale = targetHeight / height;
26629                     }
26630                 }
26631                 
26632                 context.scale(scale, scale);
26633                 
26634                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26635                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26636
26637                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26638                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26639
26640                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26641                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26642                 
26643                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26644                 
26645                 break;
26646             case 270 :
26647                 
26648                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26649                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26650                 
26651                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26652                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26653                 
26654                 var targetWidth = this.minWidth - 2 * x;
26655                 var targetHeight = this.minHeight - 2 * y;
26656                 
26657                 var scale = 1;
26658                 
26659                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26660                     scale = targetWidth / width;
26661                 }
26662                 
26663                 if(x > 0 && y == 0){
26664                     scale = targetHeight / height;
26665                 }
26666                 
26667                 if(x > 0 && y > 0){
26668                     scale = targetWidth / width;
26669                     
26670                     if(width < height){
26671                         scale = targetHeight / height;
26672                     }
26673                 }
26674                 
26675                 context.scale(scale, scale);
26676                 
26677                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26678                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26679
26680                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26681                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26682                 
26683                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26684                 
26685                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26686                 
26687                 break;
26688             default : 
26689                 break;
26690         }
26691         
26692         this.cropData = canvas.toDataURL(this.cropType);
26693         
26694         if(this.fireEvent('crop', this, this.cropData) !== false){
26695             this.process(this.file, this.cropData);
26696         }
26697         
26698         return;
26699         
26700     },
26701     
26702     setThumbBoxSize : function()
26703     {
26704         var width, height;
26705         
26706         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26707             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26708             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26709             
26710             this.minWidth = width;
26711             this.minHeight = height;
26712             
26713             if(this.rotate == 90 || this.rotate == 270){
26714                 this.minWidth = height;
26715                 this.minHeight = width;
26716             }
26717         }
26718         
26719         height = 300;
26720         width = Math.ceil(this.minWidth * height / this.minHeight);
26721         
26722         if(this.minWidth > this.minHeight){
26723             width = 300;
26724             height = Math.ceil(this.minHeight * width / this.minWidth);
26725         }
26726         
26727         this.thumbEl.setStyle({
26728             width : width + 'px',
26729             height : height + 'px'
26730         });
26731
26732         return;
26733             
26734     },
26735     
26736     setThumbBoxPosition : function()
26737     {
26738         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26739         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26740         
26741         this.thumbEl.setLeft(x);
26742         this.thumbEl.setTop(y);
26743         
26744     },
26745     
26746     baseRotateLevel : function()
26747     {
26748         this.baseRotate = 1;
26749         
26750         if(
26751                 typeof(this.exif) != 'undefined' &&
26752                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26753                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26754         ){
26755             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26756         }
26757         
26758         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26759         
26760     },
26761     
26762     baseScaleLevel : function()
26763     {
26764         var width, height;
26765         
26766         if(this.isDocument){
26767             
26768             if(this.baseRotate == 6 || this.baseRotate == 8){
26769             
26770                 height = this.thumbEl.getHeight();
26771                 this.baseScale = height / this.imageEl.OriginWidth;
26772
26773                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26774                     width = this.thumbEl.getWidth();
26775                     this.baseScale = width / this.imageEl.OriginHeight;
26776                 }
26777
26778                 return;
26779             }
26780
26781             height = this.thumbEl.getHeight();
26782             this.baseScale = height / this.imageEl.OriginHeight;
26783
26784             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26785                 width = this.thumbEl.getWidth();
26786                 this.baseScale = width / this.imageEl.OriginWidth;
26787             }
26788
26789             return;
26790         }
26791         
26792         if(this.baseRotate == 6 || this.baseRotate == 8){
26793             
26794             width = this.thumbEl.getHeight();
26795             this.baseScale = width / this.imageEl.OriginHeight;
26796             
26797             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26798                 height = this.thumbEl.getWidth();
26799                 this.baseScale = height / this.imageEl.OriginHeight;
26800             }
26801             
26802             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26803                 height = this.thumbEl.getWidth();
26804                 this.baseScale = height / this.imageEl.OriginHeight;
26805                 
26806                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26807                     width = this.thumbEl.getHeight();
26808                     this.baseScale = width / this.imageEl.OriginWidth;
26809                 }
26810             }
26811             
26812             return;
26813         }
26814         
26815         width = this.thumbEl.getWidth();
26816         this.baseScale = width / this.imageEl.OriginWidth;
26817         
26818         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26819             height = this.thumbEl.getHeight();
26820             this.baseScale = height / this.imageEl.OriginHeight;
26821         }
26822         
26823         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26824             
26825             height = this.thumbEl.getHeight();
26826             this.baseScale = height / this.imageEl.OriginHeight;
26827             
26828             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26829                 width = this.thumbEl.getWidth();
26830                 this.baseScale = width / this.imageEl.OriginWidth;
26831             }
26832             
26833         }
26834         
26835         return;
26836     },
26837     
26838     getScaleLevel : function()
26839     {
26840         return this.baseScale * Math.pow(1.1, this.scale);
26841     },
26842     
26843     onTouchStart : function(e)
26844     {
26845         if(!this.canvasLoaded){
26846             this.beforeSelectFile(e);
26847             return;
26848         }
26849         
26850         var touches = e.browserEvent.touches;
26851         
26852         if(!touches){
26853             return;
26854         }
26855         
26856         if(touches.length == 1){
26857             this.onMouseDown(e);
26858             return;
26859         }
26860         
26861         if(touches.length != 2){
26862             return;
26863         }
26864         
26865         var coords = [];
26866         
26867         for(var i = 0, finger; finger = touches[i]; i++){
26868             coords.push(finger.pageX, finger.pageY);
26869         }
26870         
26871         var x = Math.pow(coords[0] - coords[2], 2);
26872         var y = Math.pow(coords[1] - coords[3], 2);
26873         
26874         this.startDistance = Math.sqrt(x + y);
26875         
26876         this.startScale = this.scale;
26877         
26878         this.pinching = true;
26879         this.dragable = false;
26880         
26881     },
26882     
26883     onTouchMove : function(e)
26884     {
26885         if(!this.pinching && !this.dragable){
26886             return;
26887         }
26888         
26889         var touches = e.browserEvent.touches;
26890         
26891         if(!touches){
26892             return;
26893         }
26894         
26895         if(this.dragable){
26896             this.onMouseMove(e);
26897             return;
26898         }
26899         
26900         var coords = [];
26901         
26902         for(var i = 0, finger; finger = touches[i]; i++){
26903             coords.push(finger.pageX, finger.pageY);
26904         }
26905         
26906         var x = Math.pow(coords[0] - coords[2], 2);
26907         var y = Math.pow(coords[1] - coords[3], 2);
26908         
26909         this.endDistance = Math.sqrt(x + y);
26910         
26911         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26912         
26913         if(!this.zoomable()){
26914             this.scale = this.startScale;
26915             return;
26916         }
26917         
26918         this.draw();
26919         
26920     },
26921     
26922     onTouchEnd : function(e)
26923     {
26924         this.pinching = false;
26925         this.dragable = false;
26926         
26927     },
26928     
26929     process : function(file, crop)
26930     {
26931         if(this.loadMask){
26932             this.maskEl.mask(this.loadingText);
26933         }
26934         
26935         this.xhr = new XMLHttpRequest();
26936         
26937         file.xhr = this.xhr;
26938
26939         this.xhr.open(this.method, this.url, true);
26940         
26941         var headers = {
26942             "Accept": "application/json",
26943             "Cache-Control": "no-cache",
26944             "X-Requested-With": "XMLHttpRequest"
26945         };
26946         
26947         for (var headerName in headers) {
26948             var headerValue = headers[headerName];
26949             if (headerValue) {
26950                 this.xhr.setRequestHeader(headerName, headerValue);
26951             }
26952         }
26953         
26954         var _this = this;
26955         
26956         this.xhr.onload = function()
26957         {
26958             _this.xhrOnLoad(_this.xhr);
26959         }
26960         
26961         this.xhr.onerror = function()
26962         {
26963             _this.xhrOnError(_this.xhr);
26964         }
26965         
26966         var formData = new FormData();
26967
26968         formData.append('returnHTML', 'NO');
26969         
26970         if(crop){
26971             formData.append('crop', crop);
26972         }
26973         
26974         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26975             formData.append(this.paramName, file, file.name);
26976         }
26977         
26978         if(typeof(file.filename) != 'undefined'){
26979             formData.append('filename', file.filename);
26980         }
26981         
26982         if(typeof(file.mimetype) != 'undefined'){
26983             formData.append('mimetype', file.mimetype);
26984         }
26985         
26986         if(this.fireEvent('arrange', this, formData) != false){
26987             this.xhr.send(formData);
26988         };
26989     },
26990     
26991     xhrOnLoad : function(xhr)
26992     {
26993         if(this.loadMask){
26994             this.maskEl.unmask();
26995         }
26996         
26997         if (xhr.readyState !== 4) {
26998             this.fireEvent('exception', this, xhr);
26999             return;
27000         }
27001
27002         var response = Roo.decode(xhr.responseText);
27003         
27004         if(!response.success){
27005             this.fireEvent('exception', this, xhr);
27006             return;
27007         }
27008         
27009         var response = Roo.decode(xhr.responseText);
27010         
27011         this.fireEvent('upload', this, response);
27012         
27013     },
27014     
27015     xhrOnError : function()
27016     {
27017         if(this.loadMask){
27018             this.maskEl.unmask();
27019         }
27020         
27021         Roo.log('xhr on error');
27022         
27023         var response = Roo.decode(xhr.responseText);
27024           
27025         Roo.log(response);
27026         
27027     },
27028     
27029     prepare : function(file)
27030     {   
27031         if(this.loadMask){
27032             this.maskEl.mask(this.loadingText);
27033         }
27034         
27035         this.file = false;
27036         this.exif = {};
27037         
27038         if(typeof(file) === 'string'){
27039             this.loadCanvas(file);
27040             return;
27041         }
27042         
27043         if(!file || !this.urlAPI){
27044             return;
27045         }
27046         
27047         this.file = file;
27048         this.cropType = file.type;
27049         
27050         var _this = this;
27051         
27052         if(this.fireEvent('prepare', this, this.file) != false){
27053             
27054             var reader = new FileReader();
27055             
27056             reader.onload = function (e) {
27057                 if (e.target.error) {
27058                     Roo.log(e.target.error);
27059                     return;
27060                 }
27061                 
27062                 var buffer = e.target.result,
27063                     dataView = new DataView(buffer),
27064                     offset = 2,
27065                     maxOffset = dataView.byteLength - 4,
27066                     markerBytes,
27067                     markerLength;
27068                 
27069                 if (dataView.getUint16(0) === 0xffd8) {
27070                     while (offset < maxOffset) {
27071                         markerBytes = dataView.getUint16(offset);
27072                         
27073                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27074                             markerLength = dataView.getUint16(offset + 2) + 2;
27075                             if (offset + markerLength > dataView.byteLength) {
27076                                 Roo.log('Invalid meta data: Invalid segment size.');
27077                                 break;
27078                             }
27079                             
27080                             if(markerBytes == 0xffe1){
27081                                 _this.parseExifData(
27082                                     dataView,
27083                                     offset,
27084                                     markerLength
27085                                 );
27086                             }
27087                             
27088                             offset += markerLength;
27089                             
27090                             continue;
27091                         }
27092                         
27093                         break;
27094                     }
27095                     
27096                 }
27097                 
27098                 var url = _this.urlAPI.createObjectURL(_this.file);
27099                 
27100                 _this.loadCanvas(url);
27101                 
27102                 return;
27103             }
27104             
27105             reader.readAsArrayBuffer(this.file);
27106             
27107         }
27108         
27109     },
27110     
27111     parseExifData : function(dataView, offset, length)
27112     {
27113         var tiffOffset = offset + 10,
27114             littleEndian,
27115             dirOffset;
27116     
27117         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27118             // No Exif data, might be XMP data instead
27119             return;
27120         }
27121         
27122         // Check for the ASCII code for "Exif" (0x45786966):
27123         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27124             // No Exif data, might be XMP data instead
27125             return;
27126         }
27127         if (tiffOffset + 8 > dataView.byteLength) {
27128             Roo.log('Invalid Exif data: Invalid segment size.');
27129             return;
27130         }
27131         // Check for the two null bytes:
27132         if (dataView.getUint16(offset + 8) !== 0x0000) {
27133             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27134             return;
27135         }
27136         // Check the byte alignment:
27137         switch (dataView.getUint16(tiffOffset)) {
27138         case 0x4949:
27139             littleEndian = true;
27140             break;
27141         case 0x4D4D:
27142             littleEndian = false;
27143             break;
27144         default:
27145             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27146             return;
27147         }
27148         // Check for the TIFF tag marker (0x002A):
27149         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27150             Roo.log('Invalid Exif data: Missing TIFF marker.');
27151             return;
27152         }
27153         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27154         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27155         
27156         this.parseExifTags(
27157             dataView,
27158             tiffOffset,
27159             tiffOffset + dirOffset,
27160             littleEndian
27161         );
27162     },
27163     
27164     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27165     {
27166         var tagsNumber,
27167             dirEndOffset,
27168             i;
27169         if (dirOffset + 6 > dataView.byteLength) {
27170             Roo.log('Invalid Exif data: Invalid directory offset.');
27171             return;
27172         }
27173         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27174         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27175         if (dirEndOffset + 4 > dataView.byteLength) {
27176             Roo.log('Invalid Exif data: Invalid directory size.');
27177             return;
27178         }
27179         for (i = 0; i < tagsNumber; i += 1) {
27180             this.parseExifTag(
27181                 dataView,
27182                 tiffOffset,
27183                 dirOffset + 2 + 12 * i, // tag offset
27184                 littleEndian
27185             );
27186         }
27187         // Return the offset to the next directory:
27188         return dataView.getUint32(dirEndOffset, littleEndian);
27189     },
27190     
27191     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27192     {
27193         var tag = dataView.getUint16(offset, littleEndian);
27194         
27195         this.exif[tag] = this.getExifValue(
27196             dataView,
27197             tiffOffset,
27198             offset,
27199             dataView.getUint16(offset + 2, littleEndian), // tag type
27200             dataView.getUint32(offset + 4, littleEndian), // tag length
27201             littleEndian
27202         );
27203     },
27204     
27205     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27206     {
27207         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27208             tagSize,
27209             dataOffset,
27210             values,
27211             i,
27212             str,
27213             c;
27214     
27215         if (!tagType) {
27216             Roo.log('Invalid Exif data: Invalid tag type.');
27217             return;
27218         }
27219         
27220         tagSize = tagType.size * length;
27221         // Determine if the value is contained in the dataOffset bytes,
27222         // or if the value at the dataOffset is a pointer to the actual data:
27223         dataOffset = tagSize > 4 ?
27224                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27225         if (dataOffset + tagSize > dataView.byteLength) {
27226             Roo.log('Invalid Exif data: Invalid data offset.');
27227             return;
27228         }
27229         if (length === 1) {
27230             return tagType.getValue(dataView, dataOffset, littleEndian);
27231         }
27232         values = [];
27233         for (i = 0; i < length; i += 1) {
27234             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27235         }
27236         
27237         if (tagType.ascii) {
27238             str = '';
27239             // Concatenate the chars:
27240             for (i = 0; i < values.length; i += 1) {
27241                 c = values[i];
27242                 // Ignore the terminating NULL byte(s):
27243                 if (c === '\u0000') {
27244                     break;
27245                 }
27246                 str += c;
27247             }
27248             return str;
27249         }
27250         return values;
27251     }
27252     
27253 });
27254
27255 Roo.apply(Roo.bootstrap.UploadCropbox, {
27256     tags : {
27257         'Orientation': 0x0112
27258     },
27259     
27260     Orientation: {
27261             1: 0, //'top-left',
27262 //            2: 'top-right',
27263             3: 180, //'bottom-right',
27264 //            4: 'bottom-left',
27265 //            5: 'left-top',
27266             6: 90, //'right-top',
27267 //            7: 'right-bottom',
27268             8: 270 //'left-bottom'
27269     },
27270     
27271     exifTagTypes : {
27272         // byte, 8-bit unsigned int:
27273         1: {
27274             getValue: function (dataView, dataOffset) {
27275                 return dataView.getUint8(dataOffset);
27276             },
27277             size: 1
27278         },
27279         // ascii, 8-bit byte:
27280         2: {
27281             getValue: function (dataView, dataOffset) {
27282                 return String.fromCharCode(dataView.getUint8(dataOffset));
27283             },
27284             size: 1,
27285             ascii: true
27286         },
27287         // short, 16 bit int:
27288         3: {
27289             getValue: function (dataView, dataOffset, littleEndian) {
27290                 return dataView.getUint16(dataOffset, littleEndian);
27291             },
27292             size: 2
27293         },
27294         // long, 32 bit int:
27295         4: {
27296             getValue: function (dataView, dataOffset, littleEndian) {
27297                 return dataView.getUint32(dataOffset, littleEndian);
27298             },
27299             size: 4
27300         },
27301         // rational = two long values, first is numerator, second is denominator:
27302         5: {
27303             getValue: function (dataView, dataOffset, littleEndian) {
27304                 return dataView.getUint32(dataOffset, littleEndian) /
27305                     dataView.getUint32(dataOffset + 4, littleEndian);
27306             },
27307             size: 8
27308         },
27309         // slong, 32 bit signed int:
27310         9: {
27311             getValue: function (dataView, dataOffset, littleEndian) {
27312                 return dataView.getInt32(dataOffset, littleEndian);
27313             },
27314             size: 4
27315         },
27316         // srational, two slongs, first is numerator, second is denominator:
27317         10: {
27318             getValue: function (dataView, dataOffset, littleEndian) {
27319                 return dataView.getInt32(dataOffset, littleEndian) /
27320                     dataView.getInt32(dataOffset + 4, littleEndian);
27321             },
27322             size: 8
27323         }
27324     },
27325     
27326     footer : {
27327         STANDARD : [
27328             {
27329                 tag : 'div',
27330                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27331                 action : 'rotate-left',
27332                 cn : [
27333                     {
27334                         tag : 'button',
27335                         cls : 'btn btn-default',
27336                         html : '<i class="fa fa-undo"></i>'
27337                     }
27338                 ]
27339             },
27340             {
27341                 tag : 'div',
27342                 cls : 'btn-group roo-upload-cropbox-picture',
27343                 action : 'picture',
27344                 cn : [
27345                     {
27346                         tag : 'button',
27347                         cls : 'btn btn-default',
27348                         html : '<i class="fa fa-picture-o"></i>'
27349                     }
27350                 ]
27351             },
27352             {
27353                 tag : 'div',
27354                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27355                 action : 'rotate-right',
27356                 cn : [
27357                     {
27358                         tag : 'button',
27359                         cls : 'btn btn-default',
27360                         html : '<i class="fa fa-repeat"></i>'
27361                     }
27362                 ]
27363             }
27364         ],
27365         DOCUMENT : [
27366             {
27367                 tag : 'div',
27368                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27369                 action : 'rotate-left',
27370                 cn : [
27371                     {
27372                         tag : 'button',
27373                         cls : 'btn btn-default',
27374                         html : '<i class="fa fa-undo"></i>'
27375                     }
27376                 ]
27377             },
27378             {
27379                 tag : 'div',
27380                 cls : 'btn-group roo-upload-cropbox-download',
27381                 action : 'download',
27382                 cn : [
27383                     {
27384                         tag : 'button',
27385                         cls : 'btn btn-default',
27386                         html : '<i class="fa fa-download"></i>'
27387                     }
27388                 ]
27389             },
27390             {
27391                 tag : 'div',
27392                 cls : 'btn-group roo-upload-cropbox-crop',
27393                 action : 'crop',
27394                 cn : [
27395                     {
27396                         tag : 'button',
27397                         cls : 'btn btn-default',
27398                         html : '<i class="fa fa-crop"></i>'
27399                     }
27400                 ]
27401             },
27402             {
27403                 tag : 'div',
27404                 cls : 'btn-group roo-upload-cropbox-trash',
27405                 action : 'trash',
27406                 cn : [
27407                     {
27408                         tag : 'button',
27409                         cls : 'btn btn-default',
27410                         html : '<i class="fa fa-trash"></i>'
27411                     }
27412                 ]
27413             },
27414             {
27415                 tag : 'div',
27416                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27417                 action : 'rotate-right',
27418                 cn : [
27419                     {
27420                         tag : 'button',
27421                         cls : 'btn btn-default',
27422                         html : '<i class="fa fa-repeat"></i>'
27423                     }
27424                 ]
27425             }
27426         ],
27427         ROTATOR : [
27428             {
27429                 tag : 'div',
27430                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27431                 action : 'rotate-left',
27432                 cn : [
27433                     {
27434                         tag : 'button',
27435                         cls : 'btn btn-default',
27436                         html : '<i class="fa fa-undo"></i>'
27437                     }
27438                 ]
27439             },
27440             {
27441                 tag : 'div',
27442                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27443                 action : 'rotate-right',
27444                 cn : [
27445                     {
27446                         tag : 'button',
27447                         cls : 'btn btn-default',
27448                         html : '<i class="fa fa-repeat"></i>'
27449                     }
27450                 ]
27451             }
27452         ]
27453     }
27454 });
27455
27456 /*
27457 * Licence: LGPL
27458 */
27459
27460 /**
27461  * @class Roo.bootstrap.DocumentManager
27462  * @extends Roo.bootstrap.Component
27463  * Bootstrap DocumentManager class
27464  * @cfg {String} paramName default 'imageUpload'
27465  * @cfg {String} toolTipName default 'filename'
27466  * @cfg {String} method default POST
27467  * @cfg {String} url action url
27468  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27469  * @cfg {Boolean} multiple multiple upload default true
27470  * @cfg {Number} thumbSize default 300
27471  * @cfg {String} fieldLabel
27472  * @cfg {Number} labelWidth default 4
27473  * @cfg {String} labelAlign (left|top) default left
27474  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27475  * 
27476  * @constructor
27477  * Create a new DocumentManager
27478  * @param {Object} config The config object
27479  */
27480
27481 Roo.bootstrap.DocumentManager = function(config){
27482     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27483     
27484     this.files = [];
27485     this.delegates = [];
27486     
27487     this.addEvents({
27488         /**
27489          * @event initial
27490          * Fire when initial the DocumentManager
27491          * @param {Roo.bootstrap.DocumentManager} this
27492          */
27493         "initial" : true,
27494         /**
27495          * @event inspect
27496          * inspect selected file
27497          * @param {Roo.bootstrap.DocumentManager} this
27498          * @param {File} file
27499          */
27500         "inspect" : true,
27501         /**
27502          * @event exception
27503          * Fire when xhr load exception
27504          * @param {Roo.bootstrap.DocumentManager} this
27505          * @param {XMLHttpRequest} xhr
27506          */
27507         "exception" : true,
27508         /**
27509          * @event afterupload
27510          * Fire when xhr load exception
27511          * @param {Roo.bootstrap.DocumentManager} this
27512          * @param {XMLHttpRequest} xhr
27513          */
27514         "afterupload" : true,
27515         /**
27516          * @event prepare
27517          * prepare the form data
27518          * @param {Roo.bootstrap.DocumentManager} this
27519          * @param {Object} formData
27520          */
27521         "prepare" : true,
27522         /**
27523          * @event remove
27524          * Fire when remove the file
27525          * @param {Roo.bootstrap.DocumentManager} this
27526          * @param {Object} file
27527          */
27528         "remove" : true,
27529         /**
27530          * @event refresh
27531          * Fire after refresh the file
27532          * @param {Roo.bootstrap.DocumentManager} this
27533          */
27534         "refresh" : true,
27535         /**
27536          * @event click
27537          * Fire after click the image
27538          * @param {Roo.bootstrap.DocumentManager} this
27539          * @param {Object} file
27540          */
27541         "click" : true,
27542         /**
27543          * @event edit
27544          * Fire when upload a image and editable set to true
27545          * @param {Roo.bootstrap.DocumentManager} this
27546          * @param {Object} file
27547          */
27548         "edit" : true,
27549         /**
27550          * @event beforeselectfile
27551          * Fire before select file
27552          * @param {Roo.bootstrap.DocumentManager} this
27553          */
27554         "beforeselectfile" : true,
27555         /**
27556          * @event process
27557          * Fire before process file
27558          * @param {Roo.bootstrap.DocumentManager} this
27559          * @param {Object} file
27560          */
27561         "process" : true
27562         
27563     });
27564 };
27565
27566 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27567     
27568     boxes : 0,
27569     inputName : '',
27570     thumbSize : 300,
27571     multiple : true,
27572     files : false,
27573     method : 'POST',
27574     url : '',
27575     paramName : 'imageUpload',
27576     toolTipName : 'filename',
27577     fieldLabel : '',
27578     labelWidth : 4,
27579     labelAlign : 'left',
27580     editable : true,
27581     delegates : false,
27582     xhr : false, 
27583     
27584     getAutoCreate : function()
27585     {   
27586         var managerWidget = {
27587             tag : 'div',
27588             cls : 'roo-document-manager',
27589             cn : [
27590                 {
27591                     tag : 'input',
27592                     cls : 'roo-document-manager-selector',
27593                     type : 'file'
27594                 },
27595                 {
27596                     tag : 'div',
27597                     cls : 'roo-document-manager-uploader',
27598                     cn : [
27599                         {
27600                             tag : 'div',
27601                             cls : 'roo-document-manager-upload-btn',
27602                             html : '<i class="fa fa-plus"></i>'
27603                         }
27604                     ]
27605                     
27606                 }
27607             ]
27608         };
27609         
27610         var content = [
27611             {
27612                 tag : 'div',
27613                 cls : 'column col-md-12',
27614                 cn : managerWidget
27615             }
27616         ];
27617         
27618         if(this.fieldLabel.length){
27619             
27620             content = [
27621                 {
27622                     tag : 'div',
27623                     cls : 'column col-md-12',
27624                     html : this.fieldLabel
27625                 },
27626                 {
27627                     tag : 'div',
27628                     cls : 'column col-md-12',
27629                     cn : managerWidget
27630                 }
27631             ];
27632
27633             if(this.labelAlign == 'left'){
27634                 content = [
27635                     {
27636                         tag : 'div',
27637                         cls : 'column col-md-' + this.labelWidth,
27638                         html : this.fieldLabel
27639                     },
27640                     {
27641                         tag : 'div',
27642                         cls : 'column col-md-' + (12 - this.labelWidth),
27643                         cn : managerWidget
27644                     }
27645                 ];
27646                 
27647             }
27648         }
27649         
27650         var cfg = {
27651             tag : 'div',
27652             cls : 'row clearfix',
27653             cn : content
27654         };
27655         
27656         return cfg;
27657         
27658     },
27659     
27660     initEvents : function()
27661     {
27662         this.managerEl = this.el.select('.roo-document-manager', true).first();
27663         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27664         
27665         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27666         this.selectorEl.hide();
27667         
27668         if(this.multiple){
27669             this.selectorEl.attr('multiple', 'multiple');
27670         }
27671         
27672         this.selectorEl.on('change', this.onFileSelected, this);
27673         
27674         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27675         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27676         
27677         this.uploader.on('click', this.onUploaderClick, this);
27678         
27679         this.renderProgressDialog();
27680         
27681         var _this = this;
27682         
27683         window.addEventListener("resize", function() { _this.refresh(); } );
27684         
27685         this.fireEvent('initial', this);
27686     },
27687     
27688     renderProgressDialog : function()
27689     {
27690         var _this = this;
27691         
27692         this.progressDialog = new Roo.bootstrap.Modal({
27693             cls : 'roo-document-manager-progress-dialog',
27694             allow_close : false,
27695             title : '',
27696             buttons : [
27697                 {
27698                     name  :'cancel',
27699                     weight : 'danger',
27700                     html : 'Cancel'
27701                 }
27702             ], 
27703             listeners : { 
27704                 btnclick : function() {
27705                     _this.uploadCancel();
27706                     this.hide();
27707                 }
27708             }
27709         });
27710          
27711         this.progressDialog.render(Roo.get(document.body));
27712          
27713         this.progress = new Roo.bootstrap.Progress({
27714             cls : 'roo-document-manager-progress',
27715             active : true,
27716             striped : true
27717         });
27718         
27719         this.progress.render(this.progressDialog.getChildContainer());
27720         
27721         this.progressBar = new Roo.bootstrap.ProgressBar({
27722             cls : 'roo-document-manager-progress-bar',
27723             aria_valuenow : 0,
27724             aria_valuemin : 0,
27725             aria_valuemax : 12,
27726             panel : 'success'
27727         });
27728         
27729         this.progressBar.render(this.progress.getChildContainer());
27730     },
27731     
27732     onUploaderClick : function(e)
27733     {
27734         e.preventDefault();
27735      
27736         if(this.fireEvent('beforeselectfile', this) != false){
27737             this.selectorEl.dom.click();
27738         }
27739         
27740     },
27741     
27742     onFileSelected : function(e)
27743     {
27744         e.preventDefault();
27745         
27746         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27747             return;
27748         }
27749         
27750         Roo.each(this.selectorEl.dom.files, function(file){
27751             if(this.fireEvent('inspect', this, file) != false){
27752                 this.files.push(file);
27753             }
27754         }, this);
27755         
27756         this.queue();
27757         
27758     },
27759     
27760     queue : function()
27761     {
27762         this.selectorEl.dom.value = '';
27763         
27764         if(!this.files.length){
27765             return;
27766         }
27767         
27768         if(this.boxes > 0 && this.files.length > this.boxes){
27769             this.files = this.files.slice(0, this.boxes);
27770         }
27771         
27772         this.uploader.show();
27773         
27774         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27775             this.uploader.hide();
27776         }
27777         
27778         var _this = this;
27779         
27780         var files = [];
27781         
27782         var docs = [];
27783         
27784         Roo.each(this.files, function(file){
27785             
27786             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27787                 var f = this.renderPreview(file);
27788                 files.push(f);
27789                 return;
27790             }
27791             
27792             if(file.type.indexOf('image') != -1){
27793                 this.delegates.push(
27794                     (function(){
27795                         _this.process(file);
27796                     }).createDelegate(this)
27797                 );
27798         
27799                 return;
27800             }
27801             
27802             docs.push(
27803                 (function(){
27804                     _this.process(file);
27805                 }).createDelegate(this)
27806             );
27807             
27808         }, this);
27809         
27810         this.files = files;
27811         
27812         this.delegates = this.delegates.concat(docs);
27813         
27814         if(!this.delegates.length){
27815             this.refresh();
27816             return;
27817         }
27818         
27819         this.progressBar.aria_valuemax = this.delegates.length;
27820         
27821         this.arrange();
27822         
27823         return;
27824     },
27825     
27826     arrange : function()
27827     {
27828         if(!this.delegates.length){
27829             this.progressDialog.hide();
27830             this.refresh();
27831             return;
27832         }
27833         
27834         var delegate = this.delegates.shift();
27835         
27836         this.progressDialog.show();
27837         
27838         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27839         
27840         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27841         
27842         delegate();
27843     },
27844     
27845     refresh : function()
27846     {
27847         this.uploader.show();
27848         
27849         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27850             this.uploader.hide();
27851         }
27852         
27853         Roo.isTouch ? this.closable(false) : this.closable(true);
27854         
27855         this.fireEvent('refresh', this);
27856     },
27857     
27858     onRemove : function(e, el, o)
27859     {
27860         e.preventDefault();
27861         
27862         this.fireEvent('remove', this, o);
27863         
27864     },
27865     
27866     remove : function(o)
27867     {
27868         var files = [];
27869         
27870         Roo.each(this.files, function(file){
27871             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27872                 files.push(file);
27873                 return;
27874             }
27875
27876             o.target.remove();
27877
27878         }, this);
27879         
27880         this.files = files;
27881         
27882         this.refresh();
27883     },
27884     
27885     clear : function()
27886     {
27887         Roo.each(this.files, function(file){
27888             if(!file.target){
27889                 return;
27890             }
27891             
27892             file.target.remove();
27893
27894         }, this);
27895         
27896         this.files = [];
27897         
27898         this.refresh();
27899     },
27900     
27901     onClick : function(e, el, o)
27902     {
27903         e.preventDefault();
27904         
27905         this.fireEvent('click', this, o);
27906         
27907     },
27908     
27909     closable : function(closable)
27910     {
27911         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27912             
27913             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27914             
27915             if(closable){
27916                 el.show();
27917                 return;
27918             }
27919             
27920             el.hide();
27921             
27922         }, this);
27923     },
27924     
27925     xhrOnLoad : function(xhr)
27926     {
27927         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27928             el.remove();
27929         }, this);
27930         
27931         if (xhr.readyState !== 4) {
27932             this.arrange();
27933             this.fireEvent('exception', this, xhr);
27934             return;
27935         }
27936
27937         var response = Roo.decode(xhr.responseText);
27938         
27939         if(!response.success){
27940             this.arrange();
27941             this.fireEvent('exception', this, xhr);
27942             return;
27943         }
27944         
27945         var file = this.renderPreview(response.data);
27946         
27947         this.files.push(file);
27948         
27949         this.arrange();
27950         
27951         this.fireEvent('afterupload', this, xhr);
27952         
27953     },
27954     
27955     xhrOnError : function(xhr)
27956     {
27957         Roo.log('xhr on error');
27958         
27959         var response = Roo.decode(xhr.responseText);
27960           
27961         Roo.log(response);
27962         
27963         this.arrange();
27964     },
27965     
27966     process : function(file)
27967     {
27968         if(this.fireEvent('process', this, file) !== false){
27969             if(this.editable && file.type.indexOf('image') != -1){
27970                 this.fireEvent('edit', this, file);
27971                 return;
27972             }
27973
27974             this.uploadStart(file, false);
27975
27976             return;
27977         }
27978         
27979     },
27980     
27981     uploadStart : function(file, crop)
27982     {
27983         this.xhr = new XMLHttpRequest();
27984         
27985         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27986             this.arrange();
27987             return;
27988         }
27989         
27990         file.xhr = this.xhr;
27991             
27992         this.managerEl.createChild({
27993             tag : 'div',
27994             cls : 'roo-document-manager-loading',
27995             cn : [
27996                 {
27997                     tag : 'div',
27998                     tooltip : file.name,
27999                     cls : 'roo-document-manager-thumb',
28000                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28001                 }
28002             ]
28003
28004         });
28005
28006         this.xhr.open(this.method, this.url, true);
28007         
28008         var headers = {
28009             "Accept": "application/json",
28010             "Cache-Control": "no-cache",
28011             "X-Requested-With": "XMLHttpRequest"
28012         };
28013         
28014         for (var headerName in headers) {
28015             var headerValue = headers[headerName];
28016             if (headerValue) {
28017                 this.xhr.setRequestHeader(headerName, headerValue);
28018             }
28019         }
28020         
28021         var _this = this;
28022         
28023         this.xhr.onload = function()
28024         {
28025             _this.xhrOnLoad(_this.xhr);
28026         }
28027         
28028         this.xhr.onerror = function()
28029         {
28030             _this.xhrOnError(_this.xhr);
28031         }
28032         
28033         var formData = new FormData();
28034
28035         formData.append('returnHTML', 'NO');
28036         
28037         if(crop){
28038             formData.append('crop', crop);
28039         }
28040         
28041         formData.append(this.paramName, file, file.name);
28042         
28043         var options = {
28044             file : file, 
28045             manually : false
28046         };
28047         
28048         if(this.fireEvent('prepare', this, formData, options) != false){
28049             
28050             if(options.manually){
28051                 return;
28052             }
28053             
28054             this.xhr.send(formData);
28055             return;
28056         };
28057         
28058         this.uploadCancel();
28059     },
28060     
28061     uploadCancel : function()
28062     {
28063         if (this.xhr) {
28064             this.xhr.abort();
28065         }
28066         
28067         this.delegates = [];
28068         
28069         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28070             el.remove();
28071         }, this);
28072         
28073         this.arrange();
28074     },
28075     
28076     renderPreview : function(file)
28077     {
28078         if(typeof(file.target) != 'undefined' && file.target){
28079             return file;
28080         }
28081         
28082         var previewEl = this.managerEl.createChild({
28083             tag : 'div',
28084             cls : 'roo-document-manager-preview',
28085             cn : [
28086                 {
28087                     tag : 'div',
28088                     tooltip : file[this.toolTipName],
28089                     cls : 'roo-document-manager-thumb',
28090                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28091                 },
28092                 {
28093                     tag : 'button',
28094                     cls : 'close',
28095                     html : '<i class="fa fa-times-circle"></i>'
28096                 }
28097             ]
28098         });
28099
28100         var close = previewEl.select('button.close', true).first();
28101
28102         close.on('click', this.onRemove, this, file);
28103
28104         file.target = previewEl;
28105
28106         var image = previewEl.select('img', true).first();
28107         
28108         var _this = this;
28109         
28110         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28111         
28112         image.on('click', this.onClick, this, file);
28113         
28114         return file;
28115         
28116     },
28117     
28118     onPreviewLoad : function(file, image)
28119     {
28120         if(typeof(file.target) == 'undefined' || !file.target){
28121             return;
28122         }
28123         
28124         var width = image.dom.naturalWidth || image.dom.width;
28125         var height = image.dom.naturalHeight || image.dom.height;
28126         
28127         if(width > height){
28128             file.target.addClass('wide');
28129             return;
28130         }
28131         
28132         file.target.addClass('tall');
28133         return;
28134         
28135     },
28136     
28137     uploadFromSource : function(file, crop)
28138     {
28139         this.xhr = new XMLHttpRequest();
28140         
28141         this.managerEl.createChild({
28142             tag : 'div',
28143             cls : 'roo-document-manager-loading',
28144             cn : [
28145                 {
28146                     tag : 'div',
28147                     tooltip : file.name,
28148                     cls : 'roo-document-manager-thumb',
28149                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28150                 }
28151             ]
28152
28153         });
28154
28155         this.xhr.open(this.method, this.url, true);
28156         
28157         var headers = {
28158             "Accept": "application/json",
28159             "Cache-Control": "no-cache",
28160             "X-Requested-With": "XMLHttpRequest"
28161         };
28162         
28163         for (var headerName in headers) {
28164             var headerValue = headers[headerName];
28165             if (headerValue) {
28166                 this.xhr.setRequestHeader(headerName, headerValue);
28167             }
28168         }
28169         
28170         var _this = this;
28171         
28172         this.xhr.onload = function()
28173         {
28174             _this.xhrOnLoad(_this.xhr);
28175         }
28176         
28177         this.xhr.onerror = function()
28178         {
28179             _this.xhrOnError(_this.xhr);
28180         }
28181         
28182         var formData = new FormData();
28183
28184         formData.append('returnHTML', 'NO');
28185         
28186         formData.append('crop', crop);
28187         
28188         if(typeof(file.filename) != 'undefined'){
28189             formData.append('filename', file.filename);
28190         }
28191         
28192         if(typeof(file.mimetype) != 'undefined'){
28193             formData.append('mimetype', file.mimetype);
28194         }
28195         
28196         if(this.fireEvent('prepare', this, formData) != false){
28197             this.xhr.send(formData);
28198         };
28199     }
28200 });
28201
28202 /*
28203 * Licence: LGPL
28204 */
28205
28206 /**
28207  * @class Roo.bootstrap.DocumentViewer
28208  * @extends Roo.bootstrap.Component
28209  * Bootstrap DocumentViewer class
28210  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28211  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28212  * 
28213  * @constructor
28214  * Create a new DocumentViewer
28215  * @param {Object} config The config object
28216  */
28217
28218 Roo.bootstrap.DocumentViewer = function(config){
28219     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28220     
28221     this.addEvents({
28222         /**
28223          * @event initial
28224          * Fire after initEvent
28225          * @param {Roo.bootstrap.DocumentViewer} this
28226          */
28227         "initial" : true,
28228         /**
28229          * @event click
28230          * Fire after click
28231          * @param {Roo.bootstrap.DocumentViewer} this
28232          */
28233         "click" : true,
28234         /**
28235          * @event download
28236          * Fire after download button
28237          * @param {Roo.bootstrap.DocumentViewer} this
28238          */
28239         "download" : true,
28240         /**
28241          * @event trash
28242          * Fire after trash button
28243          * @param {Roo.bootstrap.DocumentViewer} this
28244          */
28245         "trash" : true
28246         
28247     });
28248 };
28249
28250 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28251     
28252     showDownload : true,
28253     
28254     showTrash : true,
28255     
28256     getAutoCreate : function()
28257     {
28258         var cfg = {
28259             tag : 'div',
28260             cls : 'roo-document-viewer',
28261             cn : [
28262                 {
28263                     tag : 'div',
28264                     cls : 'roo-document-viewer-body',
28265                     cn : [
28266                         {
28267                             tag : 'div',
28268                             cls : 'roo-document-viewer-thumb',
28269                             cn : [
28270                                 {
28271                                     tag : 'img',
28272                                     cls : 'roo-document-viewer-image'
28273                                 }
28274                             ]
28275                         }
28276                     ]
28277                 },
28278                 {
28279                     tag : 'div',
28280                     cls : 'roo-document-viewer-footer',
28281                     cn : {
28282                         tag : 'div',
28283                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28284                         cn : [
28285                             {
28286                                 tag : 'div',
28287                                 cls : 'btn-group roo-document-viewer-download',
28288                                 cn : [
28289                                     {
28290                                         tag : 'button',
28291                                         cls : 'btn btn-default',
28292                                         html : '<i class="fa fa-download"></i>'
28293                                     }
28294                                 ]
28295                             },
28296                             {
28297                                 tag : 'div',
28298                                 cls : 'btn-group roo-document-viewer-trash',
28299                                 cn : [
28300                                     {
28301                                         tag : 'button',
28302                                         cls : 'btn btn-default',
28303                                         html : '<i class="fa fa-trash"></i>'
28304                                     }
28305                                 ]
28306                             }
28307                         ]
28308                     }
28309                 }
28310             ]
28311         };
28312         
28313         return cfg;
28314     },
28315     
28316     initEvents : function()
28317     {
28318         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28319         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28320         
28321         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28322         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28323         
28324         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28325         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28326         
28327         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28328         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28329         
28330         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28331         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28332         
28333         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28334         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28335         
28336         this.bodyEl.on('click', this.onClick, this);
28337         this.downloadBtn.on('click', this.onDownload, this);
28338         this.trashBtn.on('click', this.onTrash, this);
28339         
28340         this.downloadBtn.hide();
28341         this.trashBtn.hide();
28342         
28343         if(this.showDownload){
28344             this.downloadBtn.show();
28345         }
28346         
28347         if(this.showTrash){
28348             this.trashBtn.show();
28349         }
28350         
28351         if(!this.showDownload && !this.showTrash) {
28352             this.footerEl.hide();
28353         }
28354         
28355     },
28356     
28357     initial : function()
28358     {
28359         this.fireEvent('initial', this);
28360         
28361     },
28362     
28363     onClick : function(e)
28364     {
28365         e.preventDefault();
28366         
28367         this.fireEvent('click', this);
28368     },
28369     
28370     onDownload : function(e)
28371     {
28372         e.preventDefault();
28373         
28374         this.fireEvent('download', this);
28375     },
28376     
28377     onTrash : function(e)
28378     {
28379         e.preventDefault();
28380         
28381         this.fireEvent('trash', this);
28382     }
28383     
28384 });
28385 /*
28386  * - LGPL
28387  *
28388  * nav progress bar
28389  * 
28390  */
28391
28392 /**
28393  * @class Roo.bootstrap.NavProgressBar
28394  * @extends Roo.bootstrap.Component
28395  * Bootstrap NavProgressBar class
28396  * 
28397  * @constructor
28398  * Create a new nav progress bar
28399  * @param {Object} config The config object
28400  */
28401
28402 Roo.bootstrap.NavProgressBar = function(config){
28403     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28404
28405     this.bullets = this.bullets || [];
28406    
28407 //    Roo.bootstrap.NavProgressBar.register(this);
28408      this.addEvents({
28409         /**
28410              * @event changed
28411              * Fires when the active item changes
28412              * @param {Roo.bootstrap.NavProgressBar} this
28413              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28414              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28415          */
28416         'changed': true
28417      });
28418     
28419 };
28420
28421 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28422     
28423     bullets : [],
28424     barItems : [],
28425     
28426     getAutoCreate : function()
28427     {
28428         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28429         
28430         cfg = {
28431             tag : 'div',
28432             cls : 'roo-navigation-bar-group',
28433             cn : [
28434                 {
28435                     tag : 'div',
28436                     cls : 'roo-navigation-top-bar'
28437                 },
28438                 {
28439                     tag : 'div',
28440                     cls : 'roo-navigation-bullets-bar',
28441                     cn : [
28442                         {
28443                             tag : 'ul',
28444                             cls : 'roo-navigation-bar'
28445                         }
28446                     ]
28447                 },
28448                 
28449                 {
28450                     tag : 'div',
28451                     cls : 'roo-navigation-bottom-bar'
28452                 }
28453             ]
28454             
28455         };
28456         
28457         return cfg;
28458         
28459     },
28460     
28461     initEvents: function() 
28462     {
28463         
28464     },
28465     
28466     onRender : function(ct, position) 
28467     {
28468         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28469         
28470         if(this.bullets.length){
28471             Roo.each(this.bullets, function(b){
28472                this.addItem(b);
28473             }, this);
28474         }
28475         
28476         this.format();
28477         
28478     },
28479     
28480     addItem : function(cfg)
28481     {
28482         var item = new Roo.bootstrap.NavProgressItem(cfg);
28483         
28484         item.parentId = this.id;
28485         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28486         
28487         if(cfg.html){
28488             var top = new Roo.bootstrap.Element({
28489                 tag : 'div',
28490                 cls : 'roo-navigation-bar-text'
28491             });
28492             
28493             var bottom = new Roo.bootstrap.Element({
28494                 tag : 'div',
28495                 cls : 'roo-navigation-bar-text'
28496             });
28497             
28498             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28499             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28500             
28501             var topText = new Roo.bootstrap.Element({
28502                 tag : 'span',
28503                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28504             });
28505             
28506             var bottomText = new Roo.bootstrap.Element({
28507                 tag : 'span',
28508                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28509             });
28510             
28511             topText.onRender(top.el, null);
28512             bottomText.onRender(bottom.el, null);
28513             
28514             item.topEl = top;
28515             item.bottomEl = bottom;
28516         }
28517         
28518         this.barItems.push(item);
28519         
28520         return item;
28521     },
28522     
28523     getActive : function()
28524     {
28525         var active = false;
28526         
28527         Roo.each(this.barItems, function(v){
28528             
28529             if (!v.isActive()) {
28530                 return;
28531             }
28532             
28533             active = v;
28534             return false;
28535             
28536         });
28537         
28538         return active;
28539     },
28540     
28541     setActiveItem : function(item)
28542     {
28543         var prev = false;
28544         
28545         Roo.each(this.barItems, function(v){
28546             if (v.rid == item.rid) {
28547                 return ;
28548             }
28549             
28550             if (v.isActive()) {
28551                 v.setActive(false);
28552                 prev = v;
28553             }
28554         });
28555
28556         item.setActive(true);
28557         
28558         this.fireEvent('changed', this, item, prev);
28559     },
28560     
28561     getBarItem: function(rid)
28562     {
28563         var ret = false;
28564         
28565         Roo.each(this.barItems, function(e) {
28566             if (e.rid != rid) {
28567                 return;
28568             }
28569             
28570             ret =  e;
28571             return false;
28572         });
28573         
28574         return ret;
28575     },
28576     
28577     indexOfItem : function(item)
28578     {
28579         var index = false;
28580         
28581         Roo.each(this.barItems, function(v, i){
28582             
28583             if (v.rid != item.rid) {
28584                 return;
28585             }
28586             
28587             index = i;
28588             return false
28589         });
28590         
28591         return index;
28592     },
28593     
28594     setActiveNext : function()
28595     {
28596         var i = this.indexOfItem(this.getActive());
28597         
28598         if (i > this.barItems.length) {
28599             return;
28600         }
28601         
28602         this.setActiveItem(this.barItems[i+1]);
28603     },
28604     
28605     setActivePrev : function()
28606     {
28607         var i = this.indexOfItem(this.getActive());
28608         
28609         if (i  < 1) {
28610             return;
28611         }
28612         
28613         this.setActiveItem(this.barItems[i-1]);
28614     },
28615     
28616     format : function()
28617     {
28618         if(!this.barItems.length){
28619             return;
28620         }
28621      
28622         var width = 100 / this.barItems.length;
28623         
28624         Roo.each(this.barItems, function(i){
28625             i.el.setStyle('width', width + '%');
28626             i.topEl.el.setStyle('width', width + '%');
28627             i.bottomEl.el.setStyle('width', width + '%');
28628         }, this);
28629         
28630     }
28631     
28632 });
28633 /*
28634  * - LGPL
28635  *
28636  * Nav Progress Item
28637  * 
28638  */
28639
28640 /**
28641  * @class Roo.bootstrap.NavProgressItem
28642  * @extends Roo.bootstrap.Component
28643  * Bootstrap NavProgressItem class
28644  * @cfg {String} rid the reference id
28645  * @cfg {Boolean} active (true|false) Is item active default false
28646  * @cfg {Boolean} disabled (true|false) Is item active default false
28647  * @cfg {String} html
28648  * @cfg {String} position (top|bottom) text position default bottom
28649  * @cfg {String} icon show icon instead of number
28650  * 
28651  * @constructor
28652  * Create a new NavProgressItem
28653  * @param {Object} config The config object
28654  */
28655 Roo.bootstrap.NavProgressItem = function(config){
28656     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28657     this.addEvents({
28658         // raw events
28659         /**
28660          * @event click
28661          * The raw click event for the entire grid.
28662          * @param {Roo.bootstrap.NavProgressItem} this
28663          * @param {Roo.EventObject} e
28664          */
28665         "click" : true
28666     });
28667    
28668 };
28669
28670 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28671     
28672     rid : '',
28673     active : false,
28674     disabled : false,
28675     html : '',
28676     position : 'bottom',
28677     icon : false,
28678     
28679     getAutoCreate : function()
28680     {
28681         var iconCls = 'roo-navigation-bar-item-icon';
28682         
28683         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28684         
28685         var cfg = {
28686             tag: 'li',
28687             cls: 'roo-navigation-bar-item',
28688             cn : [
28689                 {
28690                     tag : 'i',
28691                     cls : iconCls
28692                 }
28693             ]
28694         };
28695         
28696         if(this.active){
28697             cfg.cls += ' active';
28698         }
28699         if(this.disabled){
28700             cfg.cls += ' disabled';
28701         }
28702         
28703         return cfg;
28704     },
28705     
28706     disable : function()
28707     {
28708         this.setDisabled(true);
28709     },
28710     
28711     enable : function()
28712     {
28713         this.setDisabled(false);
28714     },
28715     
28716     initEvents: function() 
28717     {
28718         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28719         
28720         this.iconEl.on('click', this.onClick, this);
28721     },
28722     
28723     onClick : function(e)
28724     {
28725         e.preventDefault();
28726         
28727         if(this.disabled){
28728             return;
28729         }
28730         
28731         if(this.fireEvent('click', this, e) === false){
28732             return;
28733         };
28734         
28735         this.parent().setActiveItem(this);
28736     },
28737     
28738     isActive: function () 
28739     {
28740         return this.active;
28741     },
28742     
28743     setActive : function(state)
28744     {
28745         if(this.active == state){
28746             return;
28747         }
28748         
28749         this.active = state;
28750         
28751         if (state) {
28752             this.el.addClass('active');
28753             return;
28754         }
28755         
28756         this.el.removeClass('active');
28757         
28758         return;
28759     },
28760     
28761     setDisabled : function(state)
28762     {
28763         if(this.disabled == state){
28764             return;
28765         }
28766         
28767         this.disabled = state;
28768         
28769         if (state) {
28770             this.el.addClass('disabled');
28771             return;
28772         }
28773         
28774         this.el.removeClass('disabled');
28775     },
28776     
28777     tooltipEl : function()
28778     {
28779         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28780     }
28781 });
28782  
28783
28784  /*
28785  * - LGPL
28786  *
28787  * FieldLabel
28788  * 
28789  */
28790
28791 /**
28792  * @class Roo.bootstrap.FieldLabel
28793  * @extends Roo.bootstrap.Component
28794  * Bootstrap FieldLabel class
28795  * @cfg {String} html contents of the element
28796  * @cfg {String} tag tag of the element default label
28797  * @cfg {String} cls class of the element
28798  * @cfg {String} target label target 
28799  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28800  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28801  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28802  * @cfg {String} iconTooltip default "This field is required"
28803  * 
28804  * @constructor
28805  * Create a new FieldLabel
28806  * @param {Object} config The config object
28807  */
28808
28809 Roo.bootstrap.FieldLabel = function(config){
28810     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28811     
28812     this.addEvents({
28813             /**
28814              * @event invalid
28815              * Fires after the field has been marked as invalid.
28816              * @param {Roo.form.FieldLabel} this
28817              * @param {String} msg The validation message
28818              */
28819             invalid : true,
28820             /**
28821              * @event valid
28822              * Fires after the field has been validated with no errors.
28823              * @param {Roo.form.FieldLabel} this
28824              */
28825             valid : true
28826         });
28827 };
28828
28829 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28830     
28831     tag: 'label',
28832     cls: '',
28833     html: '',
28834     target: '',
28835     allowBlank : true,
28836     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28837     validClass : 'text-success fa fa-lg fa-check',
28838     iconTooltip : 'This field is required',
28839     
28840     getAutoCreate : function(){
28841         
28842         var cfg = {
28843             tag : this.tag,
28844             cls : 'roo-bootstrap-field-label ' + this.cls,
28845             for : this.target,
28846             cn : [
28847                 {
28848                     tag : 'i',
28849                     cls : '',
28850                     tooltip : this.iconTooltip
28851                 },
28852                 {
28853                     tag : 'span',
28854                     html : this.html
28855                 }
28856             ] 
28857         };
28858         
28859         return cfg;
28860     },
28861     
28862     initEvents: function() 
28863     {
28864         Roo.bootstrap.Element.superclass.initEvents.call(this);
28865         
28866         this.iconEl = this.el.select('i', true).first();
28867         
28868         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28869         
28870         Roo.bootstrap.FieldLabel.register(this);
28871     },
28872     
28873     /**
28874      * Mark this field as valid
28875      */
28876     markValid : function()
28877     {
28878         this.iconEl.show();
28879         
28880         this.iconEl.removeClass(this.invalidClass);
28881         
28882         this.iconEl.addClass(this.validClass);
28883         
28884         this.fireEvent('valid', this);
28885     },
28886     
28887     /**
28888      * Mark this field as invalid
28889      * @param {String} msg The validation message
28890      */
28891     markInvalid : function(msg)
28892     {
28893         this.iconEl.show();
28894         
28895         this.iconEl.removeClass(this.validClass);
28896         
28897         this.iconEl.addClass(this.invalidClass);
28898         
28899         this.fireEvent('invalid', this, msg);
28900     }
28901     
28902    
28903 });
28904
28905 Roo.apply(Roo.bootstrap.FieldLabel, {
28906     
28907     groups: {},
28908     
28909      /**
28910     * register a FieldLabel Group
28911     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28912     */
28913     register : function(label)
28914     {
28915         if(this.groups.hasOwnProperty(label.target)){
28916             return;
28917         }
28918      
28919         this.groups[label.target] = label;
28920         
28921     },
28922     /**
28923     * fetch a FieldLabel Group based on the target
28924     * @param {string} target
28925     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28926     */
28927     get: function(target) {
28928         if (typeof(this.groups[target]) == 'undefined') {
28929             return false;
28930         }
28931         
28932         return this.groups[target] ;
28933     }
28934 });
28935
28936  
28937
28938  /*
28939  * - LGPL
28940  *
28941  * page DateSplitField.
28942  * 
28943  */
28944
28945
28946 /**
28947  * @class Roo.bootstrap.DateSplitField
28948  * @extends Roo.bootstrap.Component
28949  * Bootstrap DateSplitField class
28950  * @cfg {string} fieldLabel - the label associated
28951  * @cfg {Number} labelWidth set the width of label (0-12)
28952  * @cfg {String} labelAlign (top|left)
28953  * @cfg {Boolean} dayAllowBlank (true|false) default false
28954  * @cfg {Boolean} monthAllowBlank (true|false) default false
28955  * @cfg {Boolean} yearAllowBlank (true|false) default false
28956  * @cfg {string} dayPlaceholder 
28957  * @cfg {string} monthPlaceholder
28958  * @cfg {string} yearPlaceholder
28959  * @cfg {string} dayFormat default 'd'
28960  * @cfg {string} monthFormat default 'm'
28961  * @cfg {string} yearFormat default 'Y'
28962
28963  *     
28964  * @constructor
28965  * Create a new DateSplitField
28966  * @param {Object} config The config object
28967  */
28968
28969 Roo.bootstrap.DateSplitField = function(config){
28970     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28971     
28972     this.addEvents({
28973         // raw events
28974          /**
28975          * @event years
28976          * getting the data of years
28977          * @param {Roo.bootstrap.DateSplitField} this
28978          * @param {Object} years
28979          */
28980         "years" : true,
28981         /**
28982          * @event days
28983          * getting the data of days
28984          * @param {Roo.bootstrap.DateSplitField} this
28985          * @param {Object} days
28986          */
28987         "days" : true,
28988         /**
28989          * @event invalid
28990          * Fires after the field has been marked as invalid.
28991          * @param {Roo.form.Field} this
28992          * @param {String} msg The validation message
28993          */
28994         invalid : true,
28995        /**
28996          * @event valid
28997          * Fires after the field has been validated with no errors.
28998          * @param {Roo.form.Field} this
28999          */
29000         valid : true
29001     });
29002 };
29003
29004 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29005     
29006     fieldLabel : '',
29007     labelAlign : 'top',
29008     labelWidth : 3,
29009     dayAllowBlank : false,
29010     monthAllowBlank : false,
29011     yearAllowBlank : false,
29012     dayPlaceholder : '',
29013     monthPlaceholder : '',
29014     yearPlaceholder : '',
29015     dayFormat : 'd',
29016     monthFormat : 'm',
29017     yearFormat : 'Y',
29018     isFormField : true,
29019     
29020     getAutoCreate : function()
29021     {
29022         var cfg = {
29023             tag : 'div',
29024             cls : 'row roo-date-split-field-group',
29025             cn : [
29026                 {
29027                     tag : 'input',
29028                     type : 'hidden',
29029                     cls : 'form-hidden-field roo-date-split-field-group-value',
29030                     name : this.name
29031                 }
29032             ]
29033         };
29034         
29035         if(this.fieldLabel){
29036             cfg.cn.push({
29037                 tag : 'div',
29038                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29039                 cn : [
29040                     {
29041                         tag : 'label',
29042                         html : this.fieldLabel
29043                     }
29044                 ]
29045             });
29046         }
29047         
29048         Roo.each(['day', 'month', 'year'], function(t){
29049             cfg.cn.push({
29050                 tag : 'div',
29051                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29052             });
29053         }, this);
29054         
29055         return cfg;
29056     },
29057     
29058     inputEl: function ()
29059     {
29060         return this.el.select('.roo-date-split-field-group-value', true).first();
29061     },
29062     
29063     onRender : function(ct, position) 
29064     {
29065         var _this = this;
29066         
29067         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29068         
29069         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29070         
29071         this.dayField = new Roo.bootstrap.ComboBox({
29072             allowBlank : this.dayAllowBlank,
29073             alwaysQuery : true,
29074             displayField : 'value',
29075             editable : false,
29076             fieldLabel : '',
29077             forceSelection : true,
29078             mode : 'local',
29079             placeholder : this.dayPlaceholder,
29080             selectOnFocus : true,
29081             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29082             triggerAction : 'all',
29083             typeAhead : true,
29084             valueField : 'value',
29085             store : new Roo.data.SimpleStore({
29086                 data : (function() {    
29087                     var days = [];
29088                     _this.fireEvent('days', _this, days);
29089                     return days;
29090                 })(),
29091                 fields : [ 'value' ]
29092             }),
29093             listeners : {
29094                 select : function (_self, record, index)
29095                 {
29096                     _this.setValue(_this.getValue());
29097                 }
29098             }
29099         });
29100
29101         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29102         
29103         this.monthField = new Roo.bootstrap.MonthField({
29104             after : '<i class=\"fa fa-calendar\"></i>',
29105             allowBlank : this.monthAllowBlank,
29106             placeholder : this.monthPlaceholder,
29107             readOnly : true,
29108             listeners : {
29109                 render : function (_self)
29110                 {
29111                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29112                         e.preventDefault();
29113                         _self.focus();
29114                     });
29115                 },
29116                 select : function (_self, oldvalue, newvalue)
29117                 {
29118                     _this.setValue(_this.getValue());
29119                 }
29120             }
29121         });
29122         
29123         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29124         
29125         this.yearField = new Roo.bootstrap.ComboBox({
29126             allowBlank : this.yearAllowBlank,
29127             alwaysQuery : true,
29128             displayField : 'value',
29129             editable : false,
29130             fieldLabel : '',
29131             forceSelection : true,
29132             mode : 'local',
29133             placeholder : this.yearPlaceholder,
29134             selectOnFocus : true,
29135             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29136             triggerAction : 'all',
29137             typeAhead : true,
29138             valueField : 'value',
29139             store : new Roo.data.SimpleStore({
29140                 data : (function() {
29141                     var years = [];
29142                     _this.fireEvent('years', _this, years);
29143                     return years;
29144                 })(),
29145                 fields : [ 'value' ]
29146             }),
29147             listeners : {
29148                 select : function (_self, record, index)
29149                 {
29150                     _this.setValue(_this.getValue());
29151                 }
29152             }
29153         });
29154
29155         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29156     },
29157     
29158     setValue : function(v, format)
29159     {
29160         this.inputEl.dom.value = v;
29161         
29162         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29163         
29164         var d = Date.parseDate(v, f);
29165         
29166         if(!d){
29167             this.validate();
29168             return;
29169         }
29170         
29171         this.setDay(d.format(this.dayFormat));
29172         this.setMonth(d.format(this.monthFormat));
29173         this.setYear(d.format(this.yearFormat));
29174         
29175         this.validate();
29176         
29177         return;
29178     },
29179     
29180     setDay : function(v)
29181     {
29182         this.dayField.setValue(v);
29183         this.inputEl.dom.value = this.getValue();
29184         this.validate();
29185         return;
29186     },
29187     
29188     setMonth : function(v)
29189     {
29190         this.monthField.setValue(v, true);
29191         this.inputEl.dom.value = this.getValue();
29192         this.validate();
29193         return;
29194     },
29195     
29196     setYear : function(v)
29197     {
29198         this.yearField.setValue(v);
29199         this.inputEl.dom.value = this.getValue();
29200         this.validate();
29201         return;
29202     },
29203     
29204     getDay : function()
29205     {
29206         return this.dayField.getValue();
29207     },
29208     
29209     getMonth : function()
29210     {
29211         return this.monthField.getValue();
29212     },
29213     
29214     getYear : function()
29215     {
29216         return this.yearField.getValue();
29217     },
29218     
29219     getValue : function()
29220     {
29221         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29222         
29223         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29224         
29225         return date;
29226     },
29227     
29228     reset : function()
29229     {
29230         this.setDay('');
29231         this.setMonth('');
29232         this.setYear('');
29233         this.inputEl.dom.value = '';
29234         this.validate();
29235         return;
29236     },
29237     
29238     validate : function()
29239     {
29240         var d = this.dayField.validate();
29241         var m = this.monthField.validate();
29242         var y = this.yearField.validate();
29243         
29244         var valid = true;
29245         
29246         if(
29247                 (!this.dayAllowBlank && !d) ||
29248                 (!this.monthAllowBlank && !m) ||
29249                 (!this.yearAllowBlank && !y)
29250         ){
29251             valid = false;
29252         }
29253         
29254         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29255             return valid;
29256         }
29257         
29258         if(valid){
29259             this.markValid();
29260             return valid;
29261         }
29262         
29263         this.markInvalid();
29264         
29265         return valid;
29266     },
29267     
29268     markValid : function()
29269     {
29270         
29271         var label = this.el.select('label', true).first();
29272         var icon = this.el.select('i.fa-star', true).first();
29273
29274         if(label && icon){
29275             icon.remove();
29276         }
29277         
29278         this.fireEvent('valid', this);
29279     },
29280     
29281      /**
29282      * Mark this field as invalid
29283      * @param {String} msg The validation message
29284      */
29285     markInvalid : function(msg)
29286     {
29287         
29288         var label = this.el.select('label', true).first();
29289         var icon = this.el.select('i.fa-star', true).first();
29290
29291         if(label && !icon){
29292             this.el.select('.roo-date-split-field-label', true).createChild({
29293                 tag : 'i',
29294                 cls : 'text-danger fa fa-lg fa-star',
29295                 tooltip : 'This field is required',
29296                 style : 'margin-right:5px;'
29297             }, label, true);
29298         }
29299         
29300         this.fireEvent('invalid', this, msg);
29301     },
29302     
29303     clearInvalid : function()
29304     {
29305         var label = this.el.select('label', true).first();
29306         var icon = this.el.select('i.fa-star', true).first();
29307
29308         if(label && icon){
29309             icon.remove();
29310         }
29311         
29312         this.fireEvent('valid', this);
29313     },
29314     
29315     getName: function()
29316     {
29317         return this.name;
29318     }
29319     
29320 });
29321
29322  /**
29323  *
29324  * This is based on 
29325  * http://masonry.desandro.com
29326  *
29327  * The idea is to render all the bricks based on vertical width...
29328  *
29329  * The original code extends 'outlayer' - we might need to use that....
29330  * 
29331  */
29332
29333
29334 /**
29335  * @class Roo.bootstrap.LayoutMasonry
29336  * @extends Roo.bootstrap.Component
29337  * Bootstrap Layout Masonry class
29338  * 
29339  * @constructor
29340  * Create a new Element
29341  * @param {Object} config The config object
29342  */
29343
29344 Roo.bootstrap.LayoutMasonry = function(config){
29345     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29346     
29347     this.bricks = [];
29348     
29349 };
29350
29351 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29352     
29353     /**
29354      * @cfg {Boolean} isLayoutInstant = no animation?
29355      */   
29356     isLayoutInstant : false, // needed?
29357    
29358     /**
29359      * @cfg {Number} boxWidth  width of the columns
29360      */   
29361     boxWidth : 450,
29362     
29363       /**
29364      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29365      */   
29366     boxHeight : 0,
29367     
29368     /**
29369      * @cfg {Number} padWidth padding below box..
29370      */   
29371     padWidth : 10, 
29372     
29373     /**
29374      * @cfg {Number} gutter gutter width..
29375      */   
29376     gutter : 10,
29377     
29378      /**
29379      * @cfg {Number} maxCols maximum number of columns
29380      */   
29381     
29382     maxCols: 0,
29383     
29384     /**
29385      * @cfg {Boolean} isAutoInitial defalut true
29386      */   
29387     isAutoInitial : true, 
29388     
29389     containerWidth: 0,
29390     
29391     /**
29392      * @cfg {Boolean} isHorizontal defalut false
29393      */   
29394     isHorizontal : false, 
29395
29396     currentSize : null,
29397     
29398     tag: 'div',
29399     
29400     cls: '',
29401     
29402     bricks: null, //CompositeElement
29403     
29404     cols : 1,
29405     
29406     _isLayoutInited : false,
29407     
29408 //    isAlternative : false, // only use for vertical layout...
29409     
29410     /**
29411      * @cfg {Number} alternativePadWidth padding below box..
29412      */   
29413     alternativePadWidth : 50, 
29414     
29415     getAutoCreate : function(){
29416         
29417         var cfg = {
29418             tag: this.tag,
29419             cls: 'blog-masonary-wrapper ' + this.cls,
29420             cn : {
29421                 cls : 'mas-boxes masonary'
29422             }
29423         };
29424         
29425         return cfg;
29426     },
29427     
29428     getChildContainer: function( )
29429     {
29430         if (this.boxesEl) {
29431             return this.boxesEl;
29432         }
29433         
29434         this.boxesEl = this.el.select('.mas-boxes').first();
29435         
29436         return this.boxesEl;
29437     },
29438     
29439     
29440     initEvents : function()
29441     {
29442         var _this = this;
29443         
29444         if(this.isAutoInitial){
29445             Roo.log('hook children rendered');
29446             this.on('childrenrendered', function() {
29447                 Roo.log('children rendered');
29448                 _this.initial();
29449             } ,this);
29450         }
29451     },
29452     
29453     initial : function()
29454     {
29455         this.currentSize = this.el.getBox(true);
29456         
29457         Roo.EventManager.onWindowResize(this.resize, this); 
29458
29459         if(!this.isAutoInitial){
29460             this.layout();
29461             return;
29462         }
29463         
29464         this.layout();
29465         
29466         return;
29467         //this.layout.defer(500,this);
29468         
29469     },
29470     
29471     resize : function()
29472     {
29473         Roo.log('resize');
29474         
29475         var cs = this.el.getBox(true);
29476         
29477         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29478             Roo.log("no change in with or X");
29479             return;
29480         }
29481         
29482         this.currentSize = cs;
29483         
29484         this.layout();
29485         
29486     },
29487     
29488     layout : function()
29489     {   
29490         this._resetLayout();
29491         
29492         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29493         
29494         this.layoutItems( isInstant );
29495       
29496         this._isLayoutInited = true;
29497         
29498     },
29499     
29500     _resetLayout : function()
29501     {
29502         if(this.isHorizontal){
29503             this.horizontalMeasureColumns();
29504             return;
29505         }
29506         
29507         this.verticalMeasureColumns();
29508         
29509     },
29510     
29511     verticalMeasureColumns : function()
29512     {
29513         this.getContainerWidth();
29514         
29515 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29516 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29517 //            return;
29518 //        }
29519         
29520         var boxWidth = this.boxWidth + this.padWidth;
29521         
29522         if(this.containerWidth < this.boxWidth){
29523             boxWidth = this.containerWidth
29524         }
29525         
29526         var containerWidth = this.containerWidth;
29527         
29528         var cols = Math.floor(containerWidth / boxWidth);
29529         
29530         this.cols = Math.max( cols, 1 );
29531         
29532         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29533         
29534         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29535         
29536         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29537         
29538         this.colWidth = boxWidth + avail - this.padWidth;
29539         
29540         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29541         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29542     },
29543     
29544     horizontalMeasureColumns : function()
29545     {
29546         this.getContainerWidth();
29547         
29548         var boxWidth = this.boxWidth;
29549         
29550         if(this.containerWidth < boxWidth){
29551             boxWidth = this.containerWidth;
29552         }
29553         
29554         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29555         
29556         this.el.setHeight(boxWidth);
29557         
29558     },
29559     
29560     getContainerWidth : function()
29561     {
29562         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29563     },
29564     
29565     layoutItems : function( isInstant )
29566     {
29567         var items = Roo.apply([], this.bricks);
29568         
29569         if(this.isHorizontal){
29570             this._horizontalLayoutItems( items , isInstant );
29571             return;
29572         }
29573         
29574 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29575 //            this._verticalAlternativeLayoutItems( items , isInstant );
29576 //            return;
29577 //        }
29578         
29579         this._verticalLayoutItems( items , isInstant );
29580         
29581     },
29582     
29583     _verticalLayoutItems : function ( items , isInstant)
29584     {
29585         if ( !items || !items.length ) {
29586             return;
29587         }
29588         
29589         var standard = [
29590             ['xs', 'xs', 'xs', 'tall'],
29591             ['xs', 'xs', 'tall'],
29592             ['xs', 'xs', 'sm'],
29593             ['xs', 'xs', 'xs'],
29594             ['xs', 'tall'],
29595             ['xs', 'sm'],
29596             ['xs', 'xs'],
29597             ['xs'],
29598             
29599             ['sm', 'xs', 'xs'],
29600             ['sm', 'xs'],
29601             ['sm'],
29602             
29603             ['tall', 'xs', 'xs', 'xs'],
29604             ['tall', 'xs', 'xs'],
29605             ['tall', 'xs'],
29606             ['tall']
29607             
29608         ];
29609         
29610         var queue = [];
29611         
29612         var boxes = [];
29613         
29614         var box = [];
29615         
29616         Roo.each(items, function(item, k){
29617             
29618             switch (item.size) {
29619                 // these layouts take up a full box,
29620                 case 'md' :
29621                 case 'md-left' :
29622                 case 'md-right' :
29623                 case 'wide' :
29624                     
29625                     if(box.length){
29626                         boxes.push(box);
29627                         box = [];
29628                     }
29629                     
29630                     boxes.push([item]);
29631                     
29632                     break;
29633                     
29634                 case 'xs' :
29635                 case 'sm' :
29636                 case 'tall' :
29637                     
29638                     box.push(item);
29639                     
29640                     break;
29641                 default :
29642                     break;
29643                     
29644             }
29645             
29646         }, this);
29647         
29648         if(box.length){
29649             boxes.push(box);
29650             box = [];
29651         }
29652         
29653         var filterPattern = function(box, length)
29654         {
29655             if(!box.length){
29656                 return;
29657             }
29658             
29659             var match = false;
29660             
29661             var pattern = box.slice(0, length);
29662             
29663             var format = [];
29664             
29665             Roo.each(pattern, function(i){
29666                 format.push(i.size);
29667             }, this);
29668             
29669             Roo.each(standard, function(s){
29670                 
29671                 if(String(s) != String(format)){
29672                     return;
29673                 }
29674                 
29675                 match = true;
29676                 return false;
29677                 
29678             }, this);
29679             
29680             if(!match && length == 1){
29681                 return;
29682             }
29683             
29684             if(!match){
29685                 filterPattern(box, length - 1);
29686                 return;
29687             }
29688                 
29689             queue.push(pattern);
29690
29691             box = box.slice(length, box.length);
29692
29693             filterPattern(box, 4);
29694
29695             return;
29696             
29697         }
29698         
29699         Roo.each(boxes, function(box, k){
29700             
29701             if(!box.length){
29702                 return;
29703             }
29704             
29705             if(box.length == 1){
29706                 queue.push(box);
29707                 return;
29708             }
29709             
29710             filterPattern(box, 4);
29711             
29712         }, this);
29713         
29714         this._processVerticalLayoutQueue( queue, isInstant );
29715         
29716     },
29717     
29718 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29719 //    {
29720 //        if ( !items || !items.length ) {
29721 //            return;
29722 //        }
29723 //
29724 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29725 //        
29726 //    },
29727     
29728     _horizontalLayoutItems : function ( items , isInstant)
29729     {
29730         if ( !items || !items.length || items.length < 3) {
29731             return;
29732         }
29733         
29734         items.reverse();
29735         
29736         var eItems = items.slice(0, 3);
29737         
29738         items = items.slice(3, items.length);
29739         
29740         var standard = [
29741             ['xs', 'xs', 'xs', 'wide'],
29742             ['xs', 'xs', 'wide'],
29743             ['xs', 'xs', 'sm'],
29744             ['xs', 'xs', 'xs'],
29745             ['xs', 'wide'],
29746             ['xs', 'sm'],
29747             ['xs', 'xs'],
29748             ['xs'],
29749             
29750             ['sm', 'xs', 'xs'],
29751             ['sm', 'xs'],
29752             ['sm'],
29753             
29754             ['wide', 'xs', 'xs', 'xs'],
29755             ['wide', 'xs', 'xs'],
29756             ['wide', 'xs'],
29757             ['wide'],
29758             
29759             ['wide-thin']
29760         ];
29761         
29762         var queue = [];
29763         
29764         var boxes = [];
29765         
29766         var box = [];
29767         
29768         Roo.each(items, function(item, k){
29769             
29770             switch (item.size) {
29771                 case 'md' :
29772                 case 'md-left' :
29773                 case 'md-right' :
29774                 case 'tall' :
29775                     
29776                     if(box.length){
29777                         boxes.push(box);
29778                         box = [];
29779                     }
29780                     
29781                     boxes.push([item]);
29782                     
29783                     break;
29784                     
29785                 case 'xs' :
29786                 case 'sm' :
29787                 case 'wide' :
29788                 case 'wide-thin' :
29789                     
29790                     box.push(item);
29791                     
29792                     break;
29793                 default :
29794                     break;
29795                     
29796             }
29797             
29798         }, this);
29799         
29800         if(box.length){
29801             boxes.push(box);
29802             box = [];
29803         }
29804         
29805         var filterPattern = function(box, length)
29806         {
29807             if(!box.length){
29808                 return;
29809             }
29810             
29811             var match = false;
29812             
29813             var pattern = box.slice(0, length);
29814             
29815             var format = [];
29816             
29817             Roo.each(pattern, function(i){
29818                 format.push(i.size);
29819             }, this);
29820             
29821             Roo.each(standard, function(s){
29822                 
29823                 if(String(s) != String(format)){
29824                     return;
29825                 }
29826                 
29827                 match = true;
29828                 return false;
29829                 
29830             }, this);
29831             
29832             if(!match && length == 1){
29833                 return;
29834             }
29835             
29836             if(!match){
29837                 filterPattern(box, length - 1);
29838                 return;
29839             }
29840                 
29841             queue.push(pattern);
29842
29843             box = box.slice(length, box.length);
29844
29845             filterPattern(box, 4);
29846
29847             return;
29848             
29849         }
29850         
29851         Roo.each(boxes, function(box, k){
29852             
29853             if(!box.length){
29854                 return;
29855             }
29856             
29857             if(box.length == 1){
29858                 queue.push(box);
29859                 return;
29860             }
29861             
29862             filterPattern(box, 4);
29863             
29864         }, this);
29865         
29866         
29867         var prune = [];
29868         
29869         var pos = this.el.getBox(true);
29870         
29871         var minX = pos.x;
29872         
29873         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29874         
29875         var hit_end = false;
29876         
29877         Roo.each(queue, function(box){
29878             
29879             if(hit_end){
29880                 
29881                 Roo.each(box, function(b){
29882                 
29883                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29884                     b.el.hide();
29885
29886                 }, this);
29887
29888                 return;
29889             }
29890             
29891             var mx = 0;
29892             
29893             Roo.each(box, function(b){
29894                 
29895                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29896                 b.el.show();
29897
29898                 mx = Math.max(mx, b.x);
29899                 
29900             }, this);
29901             
29902             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29903             
29904             if(maxX < minX){
29905                 
29906                 Roo.each(box, function(b){
29907                 
29908                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29909                     b.el.hide();
29910                     
29911                 }, this);
29912                 
29913                 hit_end = true;
29914                 
29915                 return;
29916             }
29917             
29918             prune.push(box);
29919             
29920         }, this);
29921         
29922         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29923     },
29924     
29925     /** Sets position of item in DOM
29926     * @param {Element} item
29927     * @param {Number} x - horizontal position
29928     * @param {Number} y - vertical position
29929     * @param {Boolean} isInstant - disables transitions
29930     */
29931     _processVerticalLayoutQueue : function( queue, isInstant )
29932     {
29933         var pos = this.el.getBox(true);
29934         var x = pos.x;
29935         var y = pos.y;
29936         var maxY = [];
29937         
29938         for (var i = 0; i < this.cols; i++){
29939             maxY[i] = pos.y;
29940         }
29941         
29942         Roo.each(queue, function(box, k){
29943             
29944             var col = k % this.cols;
29945             
29946             Roo.each(box, function(b,kk){
29947                 
29948                 b.el.position('absolute');
29949                 
29950                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29951                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29952                 
29953                 if(b.size == 'md-left' || b.size == 'md-right'){
29954                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29955                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29956                 }
29957                 
29958                 b.el.setWidth(width);
29959                 b.el.setHeight(height);
29960                 // iframe?
29961                 b.el.select('iframe',true).setSize(width,height);
29962                 
29963             }, this);
29964             
29965             for (var i = 0; i < this.cols; i++){
29966                 
29967                 if(maxY[i] < maxY[col]){
29968                     col = i;
29969                     continue;
29970                 }
29971                 
29972                 col = Math.min(col, i);
29973                 
29974             }
29975             
29976             x = pos.x + col * (this.colWidth + this.padWidth);
29977             
29978             y = maxY[col];
29979             
29980             var positions = [];
29981             
29982             switch (box.length){
29983                 case 1 :
29984                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29985                     break;
29986                 case 2 :
29987                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29988                     break;
29989                 case 3 :
29990                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29991                     break;
29992                 case 4 :
29993                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29994                     break;
29995                 default :
29996                     break;
29997             }
29998             
29999             Roo.each(box, function(b,kk){
30000                 
30001                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30002                 
30003                 var sz = b.el.getSize();
30004                 
30005                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30006                 
30007             }, this);
30008             
30009         }, this);
30010         
30011         var mY = 0;
30012         
30013         for (var i = 0; i < this.cols; i++){
30014             mY = Math.max(mY, maxY[i]);
30015         }
30016         
30017         this.el.setHeight(mY - pos.y);
30018         
30019     },
30020     
30021 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30022 //    {
30023 //        var pos = this.el.getBox(true);
30024 //        var x = pos.x;
30025 //        var y = pos.y;
30026 //        var maxX = pos.right;
30027 //        
30028 //        var maxHeight = 0;
30029 //        
30030 //        Roo.each(items, function(item, k){
30031 //            
30032 //            var c = k % 2;
30033 //            
30034 //            item.el.position('absolute');
30035 //                
30036 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30037 //
30038 //            item.el.setWidth(width);
30039 //
30040 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30041 //
30042 //            item.el.setHeight(height);
30043 //            
30044 //            if(c == 0){
30045 //                item.el.setXY([x, y], isInstant ? false : true);
30046 //            } else {
30047 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30048 //            }
30049 //            
30050 //            y = y + height + this.alternativePadWidth;
30051 //            
30052 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30053 //            
30054 //        }, this);
30055 //        
30056 //        this.el.setHeight(maxHeight);
30057 //        
30058 //    },
30059     
30060     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30061     {
30062         var pos = this.el.getBox(true);
30063         
30064         var minX = pos.x;
30065         var minY = pos.y;
30066         
30067         var maxX = pos.right;
30068         
30069         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30070         
30071         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30072         
30073         Roo.each(queue, function(box, k){
30074             
30075             Roo.each(box, function(b, kk){
30076                 
30077                 b.el.position('absolute');
30078                 
30079                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30080                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30081                 
30082                 if(b.size == 'md-left' || b.size == 'md-right'){
30083                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30084                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30085                 }
30086                 
30087                 b.el.setWidth(width);
30088                 b.el.setHeight(height);
30089                 
30090             }, this);
30091             
30092             if(!box.length){
30093                 return;
30094             }
30095             
30096             var positions = [];
30097             
30098             switch (box.length){
30099                 case 1 :
30100                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30101                     break;
30102                 case 2 :
30103                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30104                     break;
30105                 case 3 :
30106                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30107                     break;
30108                 case 4 :
30109                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30110                     break;
30111                 default :
30112                     break;
30113             }
30114             
30115             Roo.each(box, function(b,kk){
30116                 
30117                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30118                 
30119                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30120                 
30121             }, this);
30122             
30123         }, this);
30124         
30125     },
30126     
30127     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30128     {
30129         Roo.each(eItems, function(b,k){
30130             
30131             b.size = (k == 0) ? 'sm' : 'xs';
30132             b.x = (k == 0) ? 2 : 1;
30133             b.y = (k == 0) ? 2 : 1;
30134             
30135             b.el.position('absolute');
30136             
30137             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30138                 
30139             b.el.setWidth(width);
30140             
30141             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30142             
30143             b.el.setHeight(height);
30144             
30145         }, this);
30146
30147         var positions = [];
30148         
30149         positions.push({
30150             x : maxX - this.unitWidth * 2 - this.gutter,
30151             y : minY
30152         });
30153         
30154         positions.push({
30155             x : maxX - this.unitWidth,
30156             y : minY + (this.unitWidth + this.gutter) * 2
30157         });
30158         
30159         positions.push({
30160             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30161             y : minY
30162         });
30163         
30164         Roo.each(eItems, function(b,k){
30165             
30166             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30167
30168         }, this);
30169         
30170     },
30171     
30172     getVerticalOneBoxColPositions : function(x, y, box)
30173     {
30174         var pos = [];
30175         
30176         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30177         
30178         if(box[0].size == 'md-left'){
30179             rand = 0;
30180         }
30181         
30182         if(box[0].size == 'md-right'){
30183             rand = 1;
30184         }
30185         
30186         pos.push({
30187             x : x + (this.unitWidth + this.gutter) * rand,
30188             y : y
30189         });
30190         
30191         return pos;
30192     },
30193     
30194     getVerticalTwoBoxColPositions : function(x, y, box)
30195     {
30196         var pos = [];
30197         
30198         if(box[0].size == 'xs'){
30199             
30200             pos.push({
30201                 x : x,
30202                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30203             });
30204
30205             pos.push({
30206                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30207                 y : y
30208             });
30209             
30210             return pos;
30211             
30212         }
30213         
30214         pos.push({
30215             x : x,
30216             y : y
30217         });
30218
30219         pos.push({
30220             x : x + (this.unitWidth + this.gutter) * 2,
30221             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30222         });
30223         
30224         return pos;
30225         
30226     },
30227     
30228     getVerticalThreeBoxColPositions : function(x, y, box)
30229     {
30230         var pos = [];
30231         
30232         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30233             
30234             pos.push({
30235                 x : x,
30236                 y : y
30237             });
30238
30239             pos.push({
30240                 x : x + (this.unitWidth + this.gutter) * 1,
30241                 y : y
30242             });
30243             
30244             pos.push({
30245                 x : x + (this.unitWidth + this.gutter) * 2,
30246                 y : y
30247             });
30248             
30249             return pos;
30250             
30251         }
30252         
30253         if(box[0].size == 'xs' && box[1].size == 'xs'){
30254             
30255             pos.push({
30256                 x : x,
30257                 y : y
30258             });
30259
30260             pos.push({
30261                 x : x,
30262                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30263             });
30264             
30265             pos.push({
30266                 x : x + (this.unitWidth + this.gutter) * 1,
30267                 y : y
30268             });
30269             
30270             return pos;
30271             
30272         }
30273         
30274         pos.push({
30275             x : x,
30276             y : y
30277         });
30278
30279         pos.push({
30280             x : x + (this.unitWidth + this.gutter) * 2,
30281             y : y
30282         });
30283
30284         pos.push({
30285             x : x + (this.unitWidth + this.gutter) * 2,
30286             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30287         });
30288             
30289         return pos;
30290         
30291     },
30292     
30293     getVerticalFourBoxColPositions : function(x, y, box)
30294     {
30295         var pos = [];
30296         
30297         if(box[0].size == 'xs'){
30298             
30299             pos.push({
30300                 x : x,
30301                 y : y
30302             });
30303
30304             pos.push({
30305                 x : x,
30306                 y : y + (this.unitHeight + this.gutter) * 1
30307             });
30308             
30309             pos.push({
30310                 x : x,
30311                 y : y + (this.unitHeight + this.gutter) * 2
30312             });
30313             
30314             pos.push({
30315                 x : x + (this.unitWidth + this.gutter) * 1,
30316                 y : y
30317             });
30318             
30319             return pos;
30320             
30321         }
30322         
30323         pos.push({
30324             x : x,
30325             y : y
30326         });
30327
30328         pos.push({
30329             x : x + (this.unitWidth + this.gutter) * 2,
30330             y : y
30331         });
30332
30333         pos.push({
30334             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30335             y : y + (this.unitHeight + this.gutter) * 1
30336         });
30337
30338         pos.push({
30339             x : x + (this.unitWidth + this.gutter) * 2,
30340             y : y + (this.unitWidth + this.gutter) * 2
30341         });
30342
30343         return pos;
30344         
30345     },
30346     
30347     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30348     {
30349         var pos = [];
30350         
30351         if(box[0].size == 'md-left'){
30352             pos.push({
30353                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30354                 y : minY
30355             });
30356             
30357             return pos;
30358         }
30359         
30360         if(box[0].size == 'md-right'){
30361             pos.push({
30362                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30363                 y : minY + (this.unitWidth + this.gutter) * 1
30364             });
30365             
30366             return pos;
30367         }
30368         
30369         var rand = Math.floor(Math.random() * (4 - box[0].y));
30370         
30371         pos.push({
30372             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30373             y : minY + (this.unitWidth + this.gutter) * rand
30374         });
30375         
30376         return pos;
30377         
30378     },
30379     
30380     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30381     {
30382         var pos = [];
30383         
30384         if(box[0].size == 'xs'){
30385             
30386             pos.push({
30387                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30388                 y : minY
30389             });
30390
30391             pos.push({
30392                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30393                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30394             });
30395             
30396             return pos;
30397             
30398         }
30399         
30400         pos.push({
30401             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30402             y : minY
30403         });
30404
30405         pos.push({
30406             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30407             y : minY + (this.unitWidth + this.gutter) * 2
30408         });
30409         
30410         return pos;
30411         
30412     },
30413     
30414     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30415     {
30416         var pos = [];
30417         
30418         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30419             
30420             pos.push({
30421                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30422                 y : minY
30423             });
30424
30425             pos.push({
30426                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30427                 y : minY + (this.unitWidth + this.gutter) * 1
30428             });
30429             
30430             pos.push({
30431                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30432                 y : minY + (this.unitWidth + this.gutter) * 2
30433             });
30434             
30435             return pos;
30436             
30437         }
30438         
30439         if(box[0].size == 'xs' && box[1].size == 'xs'){
30440             
30441             pos.push({
30442                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30443                 y : minY
30444             });
30445
30446             pos.push({
30447                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30448                 y : minY
30449             });
30450             
30451             pos.push({
30452                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30453                 y : minY + (this.unitWidth + this.gutter) * 1
30454             });
30455             
30456             return pos;
30457             
30458         }
30459         
30460         pos.push({
30461             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30462             y : minY
30463         });
30464
30465         pos.push({
30466             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30467             y : minY + (this.unitWidth + this.gutter) * 2
30468         });
30469
30470         pos.push({
30471             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30472             y : minY + (this.unitWidth + this.gutter) * 2
30473         });
30474             
30475         return pos;
30476         
30477     },
30478     
30479     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30480     {
30481         var pos = [];
30482         
30483         if(box[0].size == 'xs'){
30484             
30485             pos.push({
30486                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30487                 y : minY
30488             });
30489
30490             pos.push({
30491                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30492                 y : minY
30493             });
30494             
30495             pos.push({
30496                 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),
30497                 y : minY
30498             });
30499             
30500             pos.push({
30501                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30502                 y : minY + (this.unitWidth + this.gutter) * 1
30503             });
30504             
30505             return pos;
30506             
30507         }
30508         
30509         pos.push({
30510             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30511             y : minY
30512         });
30513         
30514         pos.push({
30515             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30516             y : minY + (this.unitWidth + this.gutter) * 2
30517         });
30518         
30519         pos.push({
30520             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30521             y : minY + (this.unitWidth + this.gutter) * 2
30522         });
30523         
30524         pos.push({
30525             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),
30526             y : minY + (this.unitWidth + this.gutter) * 2
30527         });
30528
30529         return pos;
30530         
30531     }
30532     
30533 });
30534
30535  
30536
30537  /**
30538  *
30539  * This is based on 
30540  * http://masonry.desandro.com
30541  *
30542  * The idea is to render all the bricks based on vertical width...
30543  *
30544  * The original code extends 'outlayer' - we might need to use that....
30545  * 
30546  */
30547
30548
30549 /**
30550  * @class Roo.bootstrap.LayoutMasonryAuto
30551  * @extends Roo.bootstrap.Component
30552  * Bootstrap Layout Masonry class
30553  * 
30554  * @constructor
30555  * Create a new Element
30556  * @param {Object} config The config object
30557  */
30558
30559 Roo.bootstrap.LayoutMasonryAuto = function(config){
30560     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30561 };
30562
30563 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30564     
30565       /**
30566      * @cfg {Boolean} isFitWidth  - resize the width..
30567      */   
30568     isFitWidth : false,  // options..
30569     /**
30570      * @cfg {Boolean} isOriginLeft = left align?
30571      */   
30572     isOriginLeft : true,
30573     /**
30574      * @cfg {Boolean} isOriginTop = top align?
30575      */   
30576     isOriginTop : false,
30577     /**
30578      * @cfg {Boolean} isLayoutInstant = no animation?
30579      */   
30580     isLayoutInstant : false, // needed?
30581     /**
30582      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30583      */   
30584     isResizingContainer : true,
30585     /**
30586      * @cfg {Number} columnWidth  width of the columns 
30587      */   
30588     
30589     columnWidth : 0,
30590     
30591     /**
30592      * @cfg {Number} maxCols maximum number of columns
30593      */   
30594     
30595     maxCols: 0,
30596     /**
30597      * @cfg {Number} padHeight padding below box..
30598      */   
30599     
30600     padHeight : 10, 
30601     
30602     /**
30603      * @cfg {Boolean} isAutoInitial defalut true
30604      */   
30605     
30606     isAutoInitial : true, 
30607     
30608     // private?
30609     gutter : 0,
30610     
30611     containerWidth: 0,
30612     initialColumnWidth : 0,
30613     currentSize : null,
30614     
30615     colYs : null, // array.
30616     maxY : 0,
30617     padWidth: 10,
30618     
30619     
30620     tag: 'div',
30621     cls: '',
30622     bricks: null, //CompositeElement
30623     cols : 0, // array?
30624     // element : null, // wrapped now this.el
30625     _isLayoutInited : null, 
30626     
30627     
30628     getAutoCreate : function(){
30629         
30630         var cfg = {
30631             tag: this.tag,
30632             cls: 'blog-masonary-wrapper ' + this.cls,
30633             cn : {
30634                 cls : 'mas-boxes masonary'
30635             }
30636         };
30637         
30638         return cfg;
30639     },
30640     
30641     getChildContainer: function( )
30642     {
30643         if (this.boxesEl) {
30644             return this.boxesEl;
30645         }
30646         
30647         this.boxesEl = this.el.select('.mas-boxes').first();
30648         
30649         return this.boxesEl;
30650     },
30651     
30652     
30653     initEvents : function()
30654     {
30655         var _this = this;
30656         
30657         if(this.isAutoInitial){
30658             Roo.log('hook children rendered');
30659             this.on('childrenrendered', function() {
30660                 Roo.log('children rendered');
30661                 _this.initial();
30662             } ,this);
30663         }
30664         
30665     },
30666     
30667     initial : function()
30668     {
30669         this.reloadItems();
30670
30671         this.currentSize = this.el.getBox(true);
30672
30673         /// was window resize... - let's see if this works..
30674         Roo.EventManager.onWindowResize(this.resize, this); 
30675
30676         if(!this.isAutoInitial){
30677             this.layout();
30678             return;
30679         }
30680         
30681         this.layout.defer(500,this);
30682     },
30683     
30684     reloadItems: function()
30685     {
30686         this.bricks = this.el.select('.masonry-brick', true);
30687         
30688         this.bricks.each(function(b) {
30689             //Roo.log(b.getSize());
30690             if (!b.attr('originalwidth')) {
30691                 b.attr('originalwidth',  b.getSize().width);
30692             }
30693             
30694         });
30695         
30696         Roo.log(this.bricks.elements.length);
30697     },
30698     
30699     resize : function()
30700     {
30701         Roo.log('resize');
30702         var cs = this.el.getBox(true);
30703         
30704         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30705             Roo.log("no change in with or X");
30706             return;
30707         }
30708         this.currentSize = cs;
30709         this.layout();
30710     },
30711     
30712     layout : function()
30713     {
30714          Roo.log('layout');
30715         this._resetLayout();
30716         //this._manageStamps();
30717       
30718         // don't animate first layout
30719         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30720         this.layoutItems( isInstant );
30721       
30722         // flag for initalized
30723         this._isLayoutInited = true;
30724     },
30725     
30726     layoutItems : function( isInstant )
30727     {
30728         //var items = this._getItemsForLayout( this.items );
30729         // original code supports filtering layout items.. we just ignore it..
30730         
30731         this._layoutItems( this.bricks , isInstant );
30732       
30733         this._postLayout();
30734     },
30735     _layoutItems : function ( items , isInstant)
30736     {
30737        //this.fireEvent( 'layout', this, items );
30738     
30739
30740         if ( !items || !items.elements.length ) {
30741           // no items, emit event with empty array
30742             return;
30743         }
30744
30745         var queue = [];
30746         items.each(function(item) {
30747             Roo.log("layout item");
30748             Roo.log(item);
30749             // get x/y object from method
30750             var position = this._getItemLayoutPosition( item );
30751             // enqueue
30752             position.item = item;
30753             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30754             queue.push( position );
30755         }, this);
30756       
30757         this._processLayoutQueue( queue );
30758     },
30759     /** Sets position of item in DOM
30760     * @param {Element} item
30761     * @param {Number} x - horizontal position
30762     * @param {Number} y - vertical position
30763     * @param {Boolean} isInstant - disables transitions
30764     */
30765     _processLayoutQueue : function( queue )
30766     {
30767         for ( var i=0, len = queue.length; i < len; i++ ) {
30768             var obj = queue[i];
30769             obj.item.position('absolute');
30770             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30771         }
30772     },
30773       
30774     
30775     /**
30776     * Any logic you want to do after each layout,
30777     * i.e. size the container
30778     */
30779     _postLayout : function()
30780     {
30781         this.resizeContainer();
30782     },
30783     
30784     resizeContainer : function()
30785     {
30786         if ( !this.isResizingContainer ) {
30787             return;
30788         }
30789         var size = this._getContainerSize();
30790         if ( size ) {
30791             this.el.setSize(size.width,size.height);
30792             this.boxesEl.setSize(size.width,size.height);
30793         }
30794     },
30795     
30796     
30797     
30798     _resetLayout : function()
30799     {
30800         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30801         this.colWidth = this.el.getWidth();
30802         //this.gutter = this.el.getWidth(); 
30803         
30804         this.measureColumns();
30805
30806         // reset column Y
30807         var i = this.cols;
30808         this.colYs = [];
30809         while (i--) {
30810             this.colYs.push( 0 );
30811         }
30812     
30813         this.maxY = 0;
30814     },
30815
30816     measureColumns : function()
30817     {
30818         this.getContainerWidth();
30819       // if columnWidth is 0, default to outerWidth of first item
30820         if ( !this.columnWidth ) {
30821             var firstItem = this.bricks.first();
30822             Roo.log(firstItem);
30823             this.columnWidth  = this.containerWidth;
30824             if (firstItem && firstItem.attr('originalwidth') ) {
30825                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30826             }
30827             // columnWidth fall back to item of first element
30828             Roo.log("set column width?");
30829                         this.initialColumnWidth = this.columnWidth  ;
30830
30831             // if first elem has no width, default to size of container
30832             
30833         }
30834         
30835         
30836         if (this.initialColumnWidth) {
30837             this.columnWidth = this.initialColumnWidth;
30838         }
30839         
30840         
30841             
30842         // column width is fixed at the top - however if container width get's smaller we should
30843         // reduce it...
30844         
30845         // this bit calcs how man columns..
30846             
30847         var columnWidth = this.columnWidth += this.gutter;
30848       
30849         // calculate columns
30850         var containerWidth = this.containerWidth + this.gutter;
30851         
30852         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30853         // fix rounding errors, typically with gutters
30854         var excess = columnWidth - containerWidth % columnWidth;
30855         
30856         
30857         // if overshoot is less than a pixel, round up, otherwise floor it
30858         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30859         cols = Math[ mathMethod ]( cols );
30860         this.cols = Math.max( cols, 1 );
30861         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30862         
30863          // padding positioning..
30864         var totalColWidth = this.cols * this.columnWidth;
30865         var padavail = this.containerWidth - totalColWidth;
30866         // so for 2 columns - we need 3 'pads'
30867         
30868         var padNeeded = (1+this.cols) * this.padWidth;
30869         
30870         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30871         
30872         this.columnWidth += padExtra
30873         //this.padWidth = Math.floor(padavail /  ( this.cols));
30874         
30875         // adjust colum width so that padding is fixed??
30876         
30877         // we have 3 columns ... total = width * 3
30878         // we have X left over... that should be used by 
30879         
30880         //if (this.expandC) {
30881             
30882         //}
30883         
30884         
30885         
30886     },
30887     
30888     getContainerWidth : function()
30889     {
30890        /* // container is parent if fit width
30891         var container = this.isFitWidth ? this.element.parentNode : this.element;
30892         // check that this.size and size are there
30893         // IE8 triggers resize on body size change, so they might not be
30894         
30895         var size = getSize( container );  //FIXME
30896         this.containerWidth = size && size.innerWidth; //FIXME
30897         */
30898          
30899         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30900         
30901     },
30902     
30903     _getItemLayoutPosition : function( item )  // what is item?
30904     {
30905         // we resize the item to our columnWidth..
30906       
30907         item.setWidth(this.columnWidth);
30908         item.autoBoxAdjust  = false;
30909         
30910         var sz = item.getSize();
30911  
30912         // how many columns does this brick span
30913         var remainder = this.containerWidth % this.columnWidth;
30914         
30915         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30916         // round if off by 1 pixel, otherwise use ceil
30917         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30918         colSpan = Math.min( colSpan, this.cols );
30919         
30920         // normally this should be '1' as we dont' currently allow multi width columns..
30921         
30922         var colGroup = this._getColGroup( colSpan );
30923         // get the minimum Y value from the columns
30924         var minimumY = Math.min.apply( Math, colGroup );
30925         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30926         
30927         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30928          
30929         // position the brick
30930         var position = {
30931             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30932             y: this.currentSize.y + minimumY + this.padHeight
30933         };
30934         
30935         Roo.log(position);
30936         // apply setHeight to necessary columns
30937         var setHeight = minimumY + sz.height + this.padHeight;
30938         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30939         
30940         var setSpan = this.cols + 1 - colGroup.length;
30941         for ( var i = 0; i < setSpan; i++ ) {
30942           this.colYs[ shortColIndex + i ] = setHeight ;
30943         }
30944       
30945         return position;
30946     },
30947     
30948     /**
30949      * @param {Number} colSpan - number of columns the element spans
30950      * @returns {Array} colGroup
30951      */
30952     _getColGroup : function( colSpan )
30953     {
30954         if ( colSpan < 2 ) {
30955           // if brick spans only one column, use all the column Ys
30956           return this.colYs;
30957         }
30958       
30959         var colGroup = [];
30960         // how many different places could this brick fit horizontally
30961         var groupCount = this.cols + 1 - colSpan;
30962         // for each group potential horizontal position
30963         for ( var i = 0; i < groupCount; i++ ) {
30964           // make an array of colY values for that one group
30965           var groupColYs = this.colYs.slice( i, i + colSpan );
30966           // and get the max value of the array
30967           colGroup[i] = Math.max.apply( Math, groupColYs );
30968         }
30969         return colGroup;
30970     },
30971     /*
30972     _manageStamp : function( stamp )
30973     {
30974         var stampSize =  stamp.getSize();
30975         var offset = stamp.getBox();
30976         // get the columns that this stamp affects
30977         var firstX = this.isOriginLeft ? offset.x : offset.right;
30978         var lastX = firstX + stampSize.width;
30979         var firstCol = Math.floor( firstX / this.columnWidth );
30980         firstCol = Math.max( 0, firstCol );
30981         
30982         var lastCol = Math.floor( lastX / this.columnWidth );
30983         // lastCol should not go over if multiple of columnWidth #425
30984         lastCol -= lastX % this.columnWidth ? 0 : 1;
30985         lastCol = Math.min( this.cols - 1, lastCol );
30986         
30987         // set colYs to bottom of the stamp
30988         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30989             stampSize.height;
30990             
30991         for ( var i = firstCol; i <= lastCol; i++ ) {
30992           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30993         }
30994     },
30995     */
30996     
30997     _getContainerSize : function()
30998     {
30999         this.maxY = Math.max.apply( Math, this.colYs );
31000         var size = {
31001             height: this.maxY
31002         };
31003       
31004         if ( this.isFitWidth ) {
31005             size.width = this._getContainerFitWidth();
31006         }
31007       
31008         return size;
31009     },
31010     
31011     _getContainerFitWidth : function()
31012     {
31013         var unusedCols = 0;
31014         // count unused columns
31015         var i = this.cols;
31016         while ( --i ) {
31017           if ( this.colYs[i] !== 0 ) {
31018             break;
31019           }
31020           unusedCols++;
31021         }
31022         // fit container to columns that have been used
31023         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31024     },
31025     
31026     needsResizeLayout : function()
31027     {
31028         var previousWidth = this.containerWidth;
31029         this.getContainerWidth();
31030         return previousWidth !== this.containerWidth;
31031     }
31032  
31033 });
31034
31035  
31036
31037  /*
31038  * - LGPL
31039  *
31040  * element
31041  * 
31042  */
31043
31044 /**
31045  * @class Roo.bootstrap.MasonryBrick
31046  * @extends Roo.bootstrap.Component
31047  * Bootstrap MasonryBrick class
31048  * 
31049  * @constructor
31050  * Create a new MasonryBrick
31051  * @param {Object} config The config object
31052  */
31053
31054 Roo.bootstrap.MasonryBrick = function(config){
31055     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31056     
31057     this.addEvents({
31058         // raw events
31059         /**
31060          * @event click
31061          * When a MasonryBrick is clcik
31062          * @param {Roo.bootstrap.MasonryBrick} this
31063          * @param {Roo.EventObject} e
31064          */
31065         "click" : true
31066     });
31067 };
31068
31069 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31070     
31071     /**
31072      * @cfg {String} title
31073      */   
31074     title : '',
31075     /**
31076      * @cfg {String} html
31077      */   
31078     html : '',
31079     /**
31080      * @cfg {String} bgimage
31081      */   
31082     bgimage : '',
31083     /**
31084      * @cfg {String} videourl
31085      */   
31086     videourl : '',
31087     /**
31088      * @cfg {String} cls
31089      */   
31090     cls : '',
31091     /**
31092      * @cfg {String} href
31093      */   
31094     href : '',
31095     /**
31096      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31097      */   
31098     size : 'xs',
31099     
31100     /**
31101      * @cfg {String} (center|bottom) placetitle
31102      */   
31103     placetitle : '',
31104     
31105     /**
31106      * @cfg {Boolean} isFitContainer defalut true
31107      */   
31108     isFitContainer : true, 
31109     
31110     /**
31111      * @cfg {Boolean} preventDefault defalut false
31112      */   
31113     preventDefault : false, 
31114     
31115     getAutoCreate : function()
31116     {
31117         if(!this.isFitContainer){
31118             return this.getSplitAutoCreate();
31119         }
31120         
31121         var cls = 'masonry-brick masonry-brick-full';
31122         
31123         if(this.href.length){
31124             cls += ' masonry-brick-link';
31125         }
31126         
31127         if(this.bgimage.length){
31128             cls += ' masonry-brick-image';
31129         }
31130         
31131         if(!this.html.length){
31132             cls += ' enable-mask';
31133         }
31134         
31135         if(this.size){
31136             cls += ' masonry-' + this.size + '-brick';
31137         }
31138         
31139         if(this.placetitle.length){
31140             
31141             switch (this.placetitle) {
31142                 case 'center' :
31143                     cls += ' masonry-center-title';
31144                     break;
31145                 case 'bottom' :
31146                     cls += ' masonry-bottom-title';
31147                     break;
31148                 default:
31149                     break;
31150             }
31151             
31152         } else {
31153             if(!this.html.length && !this.bgimage.length){
31154                 cls += ' masonry-center-title';
31155             }
31156
31157             if(!this.html.length && this.bgimage.length){
31158                 cls += ' masonry-bottom-title';
31159             }
31160         }
31161         
31162         if(this.cls){
31163             cls += ' ' + this.cls;
31164         }
31165         
31166         var cfg = {
31167             tag: (this.href.length) ? 'a' : 'div',
31168             cls: cls,
31169             cn: [
31170                 {
31171                     tag: 'div',
31172                     cls: 'masonry-brick-paragraph',
31173                     cn: []
31174                 }
31175             ]
31176         };
31177         
31178         if(this.href.length){
31179             cfg.href = this.href;
31180         }
31181         
31182         var cn = cfg.cn[0].cn;
31183         
31184         if(this.title.length){
31185             cn.push({
31186                 tag: 'h4',
31187                 cls: 'masonry-brick-title',
31188                 html: this.title
31189             });
31190         }
31191         
31192         if(this.html.length){
31193             cn.push({
31194                 tag: 'p',
31195                 cls: 'masonry-brick-text',
31196                 html: this.html
31197             });
31198         }  
31199         if (!this.title.length && !this.html.length) {
31200             cfg.cn[0].cls += ' hide';
31201         }
31202         
31203         if(this.bgimage.length){
31204             cfg.cn.push({
31205                 tag: 'img',
31206                 cls: 'masonry-brick-image-view',
31207                 src: this.bgimage
31208             });
31209         }
31210         
31211         if(this.videourl.length){
31212             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31213             // youtube support only?
31214             cfg.cn.push({
31215                 tag: 'iframe',
31216                 cls: 'masonry-brick-image-view',
31217                 src: vurl,
31218                 frameborder : 0,
31219                 allowfullscreen : true
31220             });
31221             
31222             
31223         }
31224         
31225         cfg.cn.push({
31226             tag: 'div',
31227             cls: 'masonry-brick-mask'
31228         });
31229         
31230         return cfg;
31231         
31232     },
31233     
31234     getSplitAutoCreate : function()
31235     {
31236         var cls = 'masonry-brick masonry-brick-split';
31237         
31238         if(this.href.length){
31239             cls += ' masonry-brick-link';
31240         }
31241         
31242         if(this.bgimage.length){
31243             cls += ' masonry-brick-image';
31244         }
31245         
31246         if(this.size){
31247             cls += ' masonry-' + this.size + '-brick';
31248         }
31249         
31250         switch (this.placetitle) {
31251             case 'center' :
31252                 cls += ' masonry-center-title';
31253                 break;
31254             case 'bottom' :
31255                 cls += ' masonry-bottom-title';
31256                 break;
31257             default:
31258                 if(!this.bgimage.length){
31259                     cls += ' masonry-center-title';
31260                 }
31261
31262                 if(this.bgimage.length){
31263                     cls += ' masonry-bottom-title';
31264                 }
31265                 break;
31266         }
31267         
31268         if(this.cls){
31269             cls += ' ' + this.cls;
31270         }
31271         
31272         var cfg = {
31273             tag: (this.href.length) ? 'a' : 'div',
31274             cls: cls,
31275             cn: [
31276                 {
31277                     tag: 'div',
31278                     cls: 'masonry-brick-split-head',
31279                     cn: [
31280                         {
31281                             tag: 'div',
31282                             cls: 'masonry-brick-paragraph',
31283                             cn: []
31284                         }
31285                     ]
31286                 },
31287                 {
31288                     tag: 'div',
31289                     cls: 'masonry-brick-split-body',
31290                     cn: []
31291                 }
31292             ]
31293         };
31294         
31295         if(this.href.length){
31296             cfg.href = this.href;
31297         }
31298         
31299         if(this.title.length){
31300             cfg.cn[0].cn[0].cn.push({
31301                 tag: 'h4',
31302                 cls: 'masonry-brick-title',
31303                 html: this.title
31304             });
31305         }
31306         
31307         if(this.html.length){
31308             cfg.cn[1].cn.push({
31309                 tag: 'p',
31310                 cls: 'masonry-brick-text',
31311                 html: this.html
31312             });
31313         }
31314
31315         if(this.bgimage.length){
31316             cfg.cn[0].cn.push({
31317                 tag: 'img',
31318                 cls: 'masonry-brick-image-view',
31319                 src: this.bgimage
31320             });
31321         }
31322         
31323         if(this.videourl.length){
31324             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31325             // youtube support only?
31326             cfg.cn[0].cn.cn.push({
31327                 tag: 'iframe',
31328                 cls: 'masonry-brick-image-view',
31329                 src: vurl,
31330                 frameborder : 0,
31331                 allowfullscreen : true
31332             });
31333         }
31334         
31335         return cfg;
31336     },
31337     
31338     initEvents: function() 
31339     {
31340         switch (this.size) {
31341             case 'xs' :
31342                 this.x = 1;
31343                 this.y = 1;
31344                 break;
31345             case 'sm' :
31346                 this.x = 2;
31347                 this.y = 2;
31348                 break;
31349             case 'md' :
31350             case 'md-left' :
31351             case 'md-right' :
31352                 this.x = 3;
31353                 this.y = 3;
31354                 break;
31355             case 'tall' :
31356                 this.x = 2;
31357                 this.y = 3;
31358                 break;
31359             case 'wide' :
31360                 this.x = 3;
31361                 this.y = 2;
31362                 break;
31363             case 'wide-thin' :
31364                 this.x = 3;
31365                 this.y = 1;
31366                 break;
31367                         
31368             default :
31369                 break;
31370         }
31371         
31372         if(Roo.isTouch){
31373             this.el.on('touchstart', this.onTouchStart, this);
31374             this.el.on('touchmove', this.onTouchMove, this);
31375             this.el.on('touchend', this.onTouchEnd, this);
31376             this.el.on('contextmenu', this.onContextMenu, this);
31377         } else {
31378             this.el.on('mouseenter'  ,this.enter, this);
31379             this.el.on('mouseleave', this.leave, this);
31380             this.el.on('click', this.onClick, this);
31381         }
31382         
31383         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31384             this.parent().bricks.push(this);   
31385         }
31386         
31387     },
31388     
31389     onClick: function(e, el)
31390     {
31391         var time = this.endTimer - this.startTimer;
31392         
31393         if(Roo.isTouch){
31394             if(time > 1000){
31395                 e.preventDefault();
31396                 return;
31397             }
31398         }
31399         
31400         if(!this.preventDefault){
31401             return;
31402         }
31403         
31404         e.preventDefault();
31405         this.fireEvent('click', this);
31406     },
31407     
31408     enter: function(e, el)
31409     {
31410         e.preventDefault();
31411         
31412         if(!this.isFitContainer){
31413             return;
31414         }
31415         
31416         if(this.bgimage.length && this.html.length){
31417             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31418         }
31419     },
31420     
31421     leave: function(e, el)
31422     {
31423         e.preventDefault();
31424         
31425         if(!this.isFitContainer){
31426             return;
31427         }
31428         
31429         if(this.bgimage.length && this.html.length){
31430             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31431         }
31432     },
31433     
31434     onTouchStart: function(e, el)
31435     {
31436 //        e.preventDefault();
31437         
31438         this.touchmoved = false;
31439         
31440         if(!this.isFitContainer){
31441             return;
31442         }
31443         
31444         if(!this.bgimage.length || !this.html.length){
31445             return;
31446         }
31447         
31448         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31449         
31450         this.timer = new Date().getTime();
31451         
31452     },
31453     
31454     onTouchMove: function(e, el)
31455     {
31456         this.touchmoved = true;
31457     },
31458     
31459     onContextMenu : function(e,el)
31460     {
31461         e.preventDefault();
31462         e.stopPropagation();
31463         return false;
31464     },
31465     
31466     onTouchEnd: function(e, el)
31467     {
31468 //        e.preventDefault();
31469         
31470         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31471         
31472             this.leave(e,el);
31473             
31474             return;
31475         }
31476         
31477         if(!this.bgimage.length || !this.html.length){
31478             
31479             if(this.href.length){
31480                 window.location.href = this.href;
31481             }
31482             
31483             return;
31484         }
31485         
31486         if(!this.isFitContainer){
31487             return;
31488         }
31489         
31490         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31491         
31492         window.location.href = this.href;
31493     }
31494     
31495 });
31496
31497  
31498
31499  /*
31500  * - LGPL
31501  *
31502  * element
31503  * 
31504  */
31505
31506 /**
31507  * @class Roo.bootstrap.Brick
31508  * @extends Roo.bootstrap.Component
31509  * Bootstrap Brick class
31510  * 
31511  * @constructor
31512  * Create a new Brick
31513  * @param {Object} config The config object
31514  */
31515
31516 Roo.bootstrap.Brick = function(config){
31517     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31518     
31519     this.addEvents({
31520         // raw events
31521         /**
31522          * @event click
31523          * When a Brick is click
31524          * @param {Roo.bootstrap.Brick} this
31525          * @param {Roo.EventObject} e
31526          */
31527         "click" : true
31528     });
31529 };
31530
31531 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31532     
31533     /**
31534      * @cfg {String} title
31535      */   
31536     title : '',
31537     /**
31538      * @cfg {String} html
31539      */   
31540     html : '',
31541     /**
31542      * @cfg {String} bgimage
31543      */   
31544     bgimage : '',
31545     /**
31546      * @cfg {String} cls
31547      */   
31548     cls : '',
31549     /**
31550      * @cfg {String} href
31551      */   
31552     href : '',
31553     /**
31554      * @cfg {String} video
31555      */   
31556     video : '',
31557     /**
31558      * @cfg {Boolean} square
31559      */   
31560     square : true,
31561     
31562     getAutoCreate : function()
31563     {
31564         var cls = 'roo-brick';
31565         
31566         if(this.href.length){
31567             cls += ' roo-brick-link';
31568         }
31569         
31570         if(this.bgimage.length){
31571             cls += ' roo-brick-image';
31572         }
31573         
31574         if(!this.html.length && !this.bgimage.length){
31575             cls += ' roo-brick-center-title';
31576         }
31577         
31578         if(!this.html.length && this.bgimage.length){
31579             cls += ' roo-brick-bottom-title';
31580         }
31581         
31582         if(this.cls){
31583             cls += ' ' + this.cls;
31584         }
31585         
31586         var cfg = {
31587             tag: (this.href.length) ? 'a' : 'div',
31588             cls: cls,
31589             cn: [
31590                 {
31591                     tag: 'div',
31592                     cls: 'roo-brick-paragraph',
31593                     cn: []
31594                 }
31595             ]
31596         };
31597         
31598         if(this.href.length){
31599             cfg.href = this.href;
31600         }
31601         
31602         var cn = cfg.cn[0].cn;
31603         
31604         if(this.title.length){
31605             cn.push({
31606                 tag: 'h4',
31607                 cls: 'roo-brick-title',
31608                 html: this.title
31609             });
31610         }
31611         
31612         if(this.html.length){
31613             cn.push({
31614                 tag: 'p',
31615                 cls: 'roo-brick-text',
31616                 html: this.html
31617             });
31618         } else {
31619             cn.cls += ' hide';
31620         }
31621         
31622         if(this.bgimage.length){
31623             cfg.cn.push({
31624                 tag: 'img',
31625                 cls: 'roo-brick-image-view',
31626                 src: this.bgimage
31627             });
31628         }
31629         
31630         return cfg;
31631     },
31632     
31633     initEvents: function() 
31634     {
31635         if(this.title.length || this.html.length){
31636             this.el.on('mouseenter'  ,this.enter, this);
31637             this.el.on('mouseleave', this.leave, this);
31638         }
31639         
31640         
31641         Roo.EventManager.onWindowResize(this.resize, this); 
31642         
31643         this.resize();
31644     },
31645     
31646     resize : function()
31647     {
31648         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31649         
31650         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31651         
31652         if(this.bgimage.length){
31653             var image = this.el.select('.roo-brick-image-view', true).first();
31654             image.setWidth(paragraph.getWidth());
31655             image.setHeight(paragraph.getWidth());
31656             
31657             this.el.setHeight(paragraph.getWidth());
31658             
31659         }
31660         
31661     },
31662     
31663     enter: function(e, el)
31664     {
31665         e.preventDefault();
31666         
31667         if(this.bgimage.length){
31668             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31669             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31670         }
31671     },
31672     
31673     leave: function(e, el)
31674     {
31675         e.preventDefault();
31676         
31677         if(this.bgimage.length){
31678             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31679             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31680         }
31681     }
31682     
31683 });
31684
31685  
31686
31687  /*
31688  * - LGPL
31689  *
31690  * Input
31691  * 
31692  */
31693
31694 /**
31695  * @class Roo.bootstrap.NumberField
31696  * @extends Roo.bootstrap.Input
31697  * Bootstrap NumberField class
31698  * 
31699  * 
31700  * 
31701  * 
31702  * @constructor
31703  * Create a new NumberField
31704  * @param {Object} config The config object
31705  */
31706
31707 Roo.bootstrap.NumberField = function(config){
31708     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31709 };
31710
31711 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31712     
31713     /**
31714      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31715      */
31716     allowDecimals : true,
31717     /**
31718      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31719      */
31720     decimalSeparator : ".",
31721     /**
31722      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31723      */
31724     decimalPrecision : 2,
31725     /**
31726      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31727      */
31728     allowNegative : true,
31729     /**
31730      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31731      */
31732     minValue : Number.NEGATIVE_INFINITY,
31733     /**
31734      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31735      */
31736     maxValue : Number.MAX_VALUE,
31737     /**
31738      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31739      */
31740     minText : "The minimum value for this field is {0}",
31741     /**
31742      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31743      */
31744     maxText : "The maximum value for this field is {0}",
31745     /**
31746      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
31747      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31748      */
31749     nanText : "{0} is not a valid number",
31750     /**
31751      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31752      */
31753     castInt : true,
31754
31755     // private
31756     initEvents : function()
31757     {   
31758         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31759         
31760         var allowed = "0123456789";
31761         
31762         if(this.allowDecimals){
31763             allowed += this.decimalSeparator;
31764         }
31765         
31766         if(this.allowNegative){
31767             allowed += "-";
31768         }
31769         
31770         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31771         
31772         var keyPress = function(e){
31773             
31774             var k = e.getKey();
31775             
31776             var c = e.getCharCode();
31777             
31778             if(
31779                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31780                     allowed.indexOf(String.fromCharCode(c)) === -1
31781             ){
31782                 e.stopEvent();
31783                 return;
31784             }
31785             
31786             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31787                 return;
31788             }
31789             
31790             if(allowed.indexOf(String.fromCharCode(c)) === -1){
31791                 e.stopEvent();
31792             }
31793         };
31794         
31795         this.el.on("keypress", keyPress, this);
31796     },
31797     
31798     validateValue : function(value)
31799     {
31800         
31801         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31802             return false;
31803         }
31804         
31805         var num = this.parseValue(value);
31806         
31807         if(isNaN(num)){
31808             this.markInvalid(String.format(this.nanText, value));
31809             return false;
31810         }
31811         
31812         if(num < this.minValue){
31813             this.markInvalid(String.format(this.minText, this.minValue));
31814             return false;
31815         }
31816         
31817         if(num > this.maxValue){
31818             this.markInvalid(String.format(this.maxText, this.maxValue));
31819             return false;
31820         }
31821         
31822         return true;
31823     },
31824
31825     getValue : function()
31826     {
31827         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31828     },
31829
31830     parseValue : function(value)
31831     {
31832         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31833         return isNaN(value) ? '' : value;
31834     },
31835
31836     fixPrecision : function(value)
31837     {
31838         var nan = isNaN(value);
31839         
31840         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31841             return nan ? '' : value;
31842         }
31843         return parseFloat(value).toFixed(this.decimalPrecision);
31844     },
31845
31846     setValue : function(v)
31847     {
31848         v = this.fixPrecision(v);
31849         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31850     },
31851
31852     decimalPrecisionFcn : function(v)
31853     {
31854         return Math.floor(v);
31855     },
31856
31857     beforeBlur : function()
31858     {
31859         if(!this.castInt){
31860             return;
31861         }
31862         
31863         var v = this.parseValue(this.getRawValue());
31864         if(v){
31865             this.setValue(v);
31866         }
31867     }
31868     
31869 });
31870
31871  
31872
31873 /*
31874 * Licence: LGPL
31875 */
31876
31877 /**
31878  * @class Roo.bootstrap.DocumentSlider
31879  * @extends Roo.bootstrap.Component
31880  * Bootstrap DocumentSlider class
31881  * 
31882  * @constructor
31883  * Create a new DocumentViewer
31884  * @param {Object} config The config object
31885  */
31886
31887 Roo.bootstrap.DocumentSlider = function(config){
31888     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31889     
31890     this.files = [];
31891     
31892     this.addEvents({
31893         /**
31894          * @event initial
31895          * Fire after initEvent
31896          * @param {Roo.bootstrap.DocumentSlider} this
31897          */
31898         "initial" : true,
31899         /**
31900          * @event update
31901          * Fire after update
31902          * @param {Roo.bootstrap.DocumentSlider} this
31903          */
31904         "update" : true,
31905         /**
31906          * @event click
31907          * Fire after click
31908          * @param {Roo.bootstrap.DocumentSlider} this
31909          */
31910         "click" : true
31911     });
31912 };
31913
31914 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
31915     
31916     files : false,
31917     
31918     indicator : 0,
31919     
31920     getAutoCreate : function()
31921     {
31922         var cfg = {
31923             tag : 'div',
31924             cls : 'roo-document-slider',
31925             cn : [
31926                 {
31927                     tag : 'div',
31928                     cls : 'roo-document-slider-header',
31929                     cn : [
31930                         {
31931                             tag : 'div',
31932                             cls : 'roo-document-slider-header-title'
31933                         }
31934                     ]
31935                 },
31936                 {
31937                     tag : 'div',
31938                     cls : 'roo-document-slider-body',
31939                     cn : [
31940                         {
31941                             tag : 'div',
31942                             cls : 'roo-document-slider-prev',
31943                             cn : [
31944                                 {
31945                                     tag : 'i',
31946                                     cls : 'fa fa-chevron-left'
31947                                 }
31948                             ]
31949                         },
31950                         {
31951                             tag : 'div',
31952                             cls : 'roo-document-slider-thumb',
31953                             cn : [
31954                                 {
31955                                     tag : 'img',
31956                                     cls : 'roo-document-slider-image'
31957                                 }
31958                             ]
31959                         },
31960                         {
31961                             tag : 'div',
31962                             cls : 'roo-document-slider-next',
31963                             cn : [
31964                                 {
31965                                     tag : 'i',
31966                                     cls : 'fa fa-chevron-right'
31967                                 }
31968                             ]
31969                         }
31970                     ]
31971                 }
31972             ]
31973         };
31974         
31975         return cfg;
31976     },
31977     
31978     initEvents : function()
31979     {
31980         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31981         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31982         
31983         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31984         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31985         
31986         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31987         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31988         
31989         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31990         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31991         
31992         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31993         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31994         
31995         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31996         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31997         
31998         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31999         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32000         
32001         this.thumbEl.on('click', this.onClick, this);
32002         
32003         this.prevIndicator.on('click', this.prev, this);
32004         
32005         this.nextIndicator.on('click', this.next, this);
32006         
32007     },
32008     
32009     initial : function()
32010     {
32011         if(this.files.length){
32012             this.indicator = 1;
32013             this.update()
32014         }
32015         
32016         this.fireEvent('initial', this);
32017     },
32018     
32019     update : function()
32020     {
32021         this.imageEl.attr('src', this.files[this.indicator - 1]);
32022         
32023         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32024         
32025         this.prevIndicator.show();
32026         
32027         if(this.indicator == 1){
32028             this.prevIndicator.hide();
32029         }
32030         
32031         this.nextIndicator.show();
32032         
32033         if(this.indicator == this.files.length){
32034             this.nextIndicator.hide();
32035         }
32036         
32037         this.thumbEl.scrollTo('top');
32038         
32039         this.fireEvent('update', this);
32040     },
32041     
32042     onClick : function(e)
32043     {
32044         e.preventDefault();
32045         
32046         this.fireEvent('click', this);
32047     },
32048     
32049     prev : function(e)
32050     {
32051         e.preventDefault();
32052         
32053         this.indicator = Math.max(1, this.indicator - 1);
32054         
32055         this.update();
32056     },
32057     
32058     next : function(e)
32059     {
32060         e.preventDefault();
32061         
32062         this.indicator = Math.min(this.files.length, this.indicator + 1);
32063         
32064         this.update();
32065     }
32066 });
32067 /*
32068  * Based on:
32069  * Ext JS Library 1.1.1
32070  * Copyright(c) 2006-2007, Ext JS, LLC.
32071  *
32072  * Originally Released Under LGPL - original licence link has changed is not relivant.
32073  *
32074  * Fork - LGPL
32075  * <script type="text/javascript">
32076  */
32077
32078
32079 /**
32080  * @class Roo.bootstrap.SplitBar
32081  * @extends Roo.util.Observable
32082  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32083  * <br><br>
32084  * Usage:
32085  * <pre><code>
32086 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32087                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32088 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32089 split.minSize = 100;
32090 split.maxSize = 600;
32091 split.animate = true;
32092 split.on('moved', splitterMoved);
32093 </code></pre>
32094  * @constructor
32095  * Create a new SplitBar
32096  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32097  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32098  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32099  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32100                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32101                         position of the SplitBar).
32102  */
32103 Roo.bootstrap.SplitBar = function(cfg){
32104     
32105     /** @private */
32106     
32107     //{
32108     //  dragElement : elm
32109     //  resizingElement: el,
32110         // optional..
32111     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32112     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32113         // existingProxy ???
32114     //}
32115     
32116     this.el = Roo.get(cfg.dragElement, true);
32117     this.el.dom.unselectable = "on";
32118     /** @private */
32119     this.resizingEl = Roo.get(cfg.resizingElement, true);
32120
32121     /**
32122      * @private
32123      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32124      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32125      * @type Number
32126      */
32127     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32128     
32129     /**
32130      * The minimum size of the resizing element. (Defaults to 0)
32131      * @type Number
32132      */
32133     this.minSize = 0;
32134     
32135     /**
32136      * The maximum size of the resizing element. (Defaults to 2000)
32137      * @type Number
32138      */
32139     this.maxSize = 2000;
32140     
32141     /**
32142      * Whether to animate the transition to the new size
32143      * @type Boolean
32144      */
32145     this.animate = false;
32146     
32147     /**
32148      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32149      * @type Boolean
32150      */
32151     this.useShim = false;
32152     
32153     /** @private */
32154     this.shim = null;
32155     
32156     if(!cfg.existingProxy){
32157         /** @private */
32158         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32159     }else{
32160         this.proxy = Roo.get(cfg.existingProxy).dom;
32161     }
32162     /** @private */
32163     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32164     
32165     /** @private */
32166     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32167     
32168     /** @private */
32169     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32170     
32171     /** @private */
32172     this.dragSpecs = {};
32173     
32174     /**
32175      * @private The adapter to use to positon and resize elements
32176      */
32177     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32178     this.adapter.init(this);
32179     
32180     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32181         /** @private */
32182         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32183         this.el.addClass("roo-splitbar-h");
32184     }else{
32185         /** @private */
32186         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32187         this.el.addClass("roo-splitbar-v");
32188     }
32189     
32190     this.addEvents({
32191         /**
32192          * @event resize
32193          * Fires when the splitter is moved (alias for {@link #event-moved})
32194          * @param {Roo.bootstrap.SplitBar} this
32195          * @param {Number} newSize the new width or height
32196          */
32197         "resize" : true,
32198         /**
32199          * @event moved
32200          * Fires when the splitter is moved
32201          * @param {Roo.bootstrap.SplitBar} this
32202          * @param {Number} newSize the new width or height
32203          */
32204         "moved" : true,
32205         /**
32206          * @event beforeresize
32207          * Fires before the splitter is dragged
32208          * @param {Roo.bootstrap.SplitBar} this
32209          */
32210         "beforeresize" : true,
32211
32212         "beforeapply" : true
32213     });
32214
32215     Roo.util.Observable.call(this);
32216 };
32217
32218 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32219     onStartProxyDrag : function(x, y){
32220         this.fireEvent("beforeresize", this);
32221         if(!this.overlay){
32222             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32223             o.unselectable();
32224             o.enableDisplayMode("block");
32225             // all splitbars share the same overlay
32226             Roo.bootstrap.SplitBar.prototype.overlay = o;
32227         }
32228         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32229         this.overlay.show();
32230         Roo.get(this.proxy).setDisplayed("block");
32231         var size = this.adapter.getElementSize(this);
32232         this.activeMinSize = this.getMinimumSize();;
32233         this.activeMaxSize = this.getMaximumSize();;
32234         var c1 = size - this.activeMinSize;
32235         var c2 = Math.max(this.activeMaxSize - size, 0);
32236         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32237             this.dd.resetConstraints();
32238             this.dd.setXConstraint(
32239                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32240                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32241             );
32242             this.dd.setYConstraint(0, 0);
32243         }else{
32244             this.dd.resetConstraints();
32245             this.dd.setXConstraint(0, 0);
32246             this.dd.setYConstraint(
32247                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32248                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32249             );
32250          }
32251         this.dragSpecs.startSize = size;
32252         this.dragSpecs.startPoint = [x, y];
32253         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32254     },
32255     
32256     /** 
32257      * @private Called after the drag operation by the DDProxy
32258      */
32259     onEndProxyDrag : function(e){
32260         Roo.get(this.proxy).setDisplayed(false);
32261         var endPoint = Roo.lib.Event.getXY(e);
32262         if(this.overlay){
32263             this.overlay.hide();
32264         }
32265         var newSize;
32266         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32267             newSize = this.dragSpecs.startSize + 
32268                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32269                     endPoint[0] - this.dragSpecs.startPoint[0] :
32270                     this.dragSpecs.startPoint[0] - endPoint[0]
32271                 );
32272         }else{
32273             newSize = this.dragSpecs.startSize + 
32274                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32275                     endPoint[1] - this.dragSpecs.startPoint[1] :
32276                     this.dragSpecs.startPoint[1] - endPoint[1]
32277                 );
32278         }
32279         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32280         if(newSize != this.dragSpecs.startSize){
32281             if(this.fireEvent('beforeapply', this, newSize) !== false){
32282                 this.adapter.setElementSize(this, newSize);
32283                 this.fireEvent("moved", this, newSize);
32284                 this.fireEvent("resize", this, newSize);
32285             }
32286         }
32287     },
32288     
32289     /**
32290      * Get the adapter this SplitBar uses
32291      * @return The adapter object
32292      */
32293     getAdapter : function(){
32294         return this.adapter;
32295     },
32296     
32297     /**
32298      * Set the adapter this SplitBar uses
32299      * @param {Object} adapter A SplitBar adapter object
32300      */
32301     setAdapter : function(adapter){
32302         this.adapter = adapter;
32303         this.adapter.init(this);
32304     },
32305     
32306     /**
32307      * Gets the minimum size for the resizing element
32308      * @return {Number} The minimum size
32309      */
32310     getMinimumSize : function(){
32311         return this.minSize;
32312     },
32313     
32314     /**
32315      * Sets the minimum size for the resizing element
32316      * @param {Number} minSize The minimum size
32317      */
32318     setMinimumSize : function(minSize){
32319         this.minSize = minSize;
32320     },
32321     
32322     /**
32323      * Gets the maximum size for the resizing element
32324      * @return {Number} The maximum size
32325      */
32326     getMaximumSize : function(){
32327         return this.maxSize;
32328     },
32329     
32330     /**
32331      * Sets the maximum size for the resizing element
32332      * @param {Number} maxSize The maximum size
32333      */
32334     setMaximumSize : function(maxSize){
32335         this.maxSize = maxSize;
32336     },
32337     
32338     /**
32339      * Sets the initialize size for the resizing element
32340      * @param {Number} size The initial size
32341      */
32342     setCurrentSize : function(size){
32343         var oldAnimate = this.animate;
32344         this.animate = false;
32345         this.adapter.setElementSize(this, size);
32346         this.animate = oldAnimate;
32347     },
32348     
32349     /**
32350      * Destroy this splitbar. 
32351      * @param {Boolean} removeEl True to remove the element
32352      */
32353     destroy : function(removeEl){
32354         if(this.shim){
32355             this.shim.remove();
32356         }
32357         this.dd.unreg();
32358         this.proxy.parentNode.removeChild(this.proxy);
32359         if(removeEl){
32360             this.el.remove();
32361         }
32362     }
32363 });
32364
32365 /**
32366  * @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.
32367  */
32368 Roo.bootstrap.SplitBar.createProxy = function(dir){
32369     var proxy = new Roo.Element(document.createElement("div"));
32370     proxy.unselectable();
32371     var cls = 'roo-splitbar-proxy';
32372     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32373     document.body.appendChild(proxy.dom);
32374     return proxy.dom;
32375 };
32376
32377 /** 
32378  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32379  * Default Adapter. It assumes the splitter and resizing element are not positioned
32380  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32381  */
32382 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32383 };
32384
32385 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32386     // do nothing for now
32387     init : function(s){
32388     
32389     },
32390     /**
32391      * Called before drag operations to get the current size of the resizing element. 
32392      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32393      */
32394      getElementSize : function(s){
32395         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32396             return s.resizingEl.getWidth();
32397         }else{
32398             return s.resizingEl.getHeight();
32399         }
32400     },
32401     
32402     /**
32403      * Called after drag operations to set the size of the resizing element.
32404      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32405      * @param {Number} newSize The new size to set
32406      * @param {Function} onComplete A function to be invoked when resizing is complete
32407      */
32408     setElementSize : function(s, newSize, onComplete){
32409         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32410             if(!s.animate){
32411                 s.resizingEl.setWidth(newSize);
32412                 if(onComplete){
32413                     onComplete(s, newSize);
32414                 }
32415             }else{
32416                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32417             }
32418         }else{
32419             
32420             if(!s.animate){
32421                 s.resizingEl.setHeight(newSize);
32422                 if(onComplete){
32423                     onComplete(s, newSize);
32424                 }
32425             }else{
32426                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32427             }
32428         }
32429     }
32430 };
32431
32432 /** 
32433  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32434  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32435  * Adapter that  moves the splitter element to align with the resized sizing element. 
32436  * Used with an absolute positioned SplitBar.
32437  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32438  * document.body, make sure you assign an id to the body element.
32439  */
32440 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32441     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32442     this.container = Roo.get(container);
32443 };
32444
32445 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32446     init : function(s){
32447         this.basic.init(s);
32448     },
32449     
32450     getElementSize : function(s){
32451         return this.basic.getElementSize(s);
32452     },
32453     
32454     setElementSize : function(s, newSize, onComplete){
32455         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32456     },
32457     
32458     moveSplitter : function(s){
32459         var yes = Roo.bootstrap.SplitBar;
32460         switch(s.placement){
32461             case yes.LEFT:
32462                 s.el.setX(s.resizingEl.getRight());
32463                 break;
32464             case yes.RIGHT:
32465                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32466                 break;
32467             case yes.TOP:
32468                 s.el.setY(s.resizingEl.getBottom());
32469                 break;
32470             case yes.BOTTOM:
32471                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32472                 break;
32473         }
32474     }
32475 };
32476
32477 /**
32478  * Orientation constant - Create a vertical SplitBar
32479  * @static
32480  * @type Number
32481  */
32482 Roo.bootstrap.SplitBar.VERTICAL = 1;
32483
32484 /**
32485  * Orientation constant - Create a horizontal SplitBar
32486  * @static
32487  * @type Number
32488  */
32489 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32490
32491 /**
32492  * Placement constant - The resizing element is to the left of the splitter element
32493  * @static
32494  * @type Number
32495  */
32496 Roo.bootstrap.SplitBar.LEFT = 1;
32497
32498 /**
32499  * Placement constant - The resizing element is to the right of the splitter element
32500  * @static
32501  * @type Number
32502  */
32503 Roo.bootstrap.SplitBar.RIGHT = 2;
32504
32505 /**
32506  * Placement constant - The resizing element is positioned above the splitter element
32507  * @static
32508  * @type Number
32509  */
32510 Roo.bootstrap.SplitBar.TOP = 3;
32511
32512 /**
32513  * Placement constant - The resizing element is positioned under splitter element
32514  * @static
32515  * @type Number
32516  */
32517 Roo.bootstrap.SplitBar.BOTTOM = 4;
32518 Roo.namespace("Roo.bootstrap.layout");/*
32519  * Based on:
32520  * Ext JS Library 1.1.1
32521  * Copyright(c) 2006-2007, Ext JS, LLC.
32522  *
32523  * Originally Released Under LGPL - original licence link has changed is not relivant.
32524  *
32525  * Fork - LGPL
32526  * <script type="text/javascript">
32527  */
32528
32529 /**
32530  * @class Roo.bootstrap.layout.Manager
32531  * @extends Roo.bootstrap.Component
32532  * Base class for layout managers.
32533  */
32534 Roo.bootstrap.layout.Manager = function(config)
32535 {
32536     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32537
32538
32539
32540
32541
32542     /** false to disable window resize monitoring @type Boolean */
32543     this.monitorWindowResize = true;
32544     this.regions = {};
32545     this.addEvents({
32546         /**
32547          * @event layout
32548          * Fires when a layout is performed.
32549          * @param {Roo.LayoutManager} this
32550          */
32551         "layout" : true,
32552         /**
32553          * @event regionresized
32554          * Fires when the user resizes a region.
32555          * @param {Roo.LayoutRegion} region The resized region
32556          * @param {Number} newSize The new size (width for east/west, height for north/south)
32557          */
32558         "regionresized" : true,
32559         /**
32560          * @event regioncollapsed
32561          * Fires when a region is collapsed.
32562          * @param {Roo.LayoutRegion} region The collapsed region
32563          */
32564         "regioncollapsed" : true,
32565         /**
32566          * @event regionexpanded
32567          * Fires when a region is expanded.
32568          * @param {Roo.LayoutRegion} region The expanded region
32569          */
32570         "regionexpanded" : true
32571     });
32572     this.updating = false;
32573
32574     if (config.el) {
32575         this.el = Roo.get(config.el);
32576         this.initEvents();
32577     }
32578
32579 };
32580
32581 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32582
32583
32584     regions : null,
32585
32586     monitorWindowResize : true,
32587
32588
32589     updating : false,
32590
32591
32592     onRender : function(ct, position)
32593     {
32594         if(!this.el){
32595             this.el = Roo.get(ct);
32596             this.initEvents();
32597         }
32598         //this.fireEvent('render',this);
32599     },
32600
32601
32602     initEvents: function()
32603     {
32604
32605
32606         // ie scrollbar fix
32607         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32608             document.body.scroll = "no";
32609         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32610             this.el.position('relative');
32611         }
32612         this.id = this.el.id;
32613         this.el.addClass("roo-layout-container");
32614         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32615         if(this.el.dom != document.body ) {
32616             this.el.on('resize', this.layout,this);
32617             this.el.on('show', this.layout,this);
32618         }
32619
32620     },
32621
32622     /**
32623      * Returns true if this layout is currently being updated
32624      * @return {Boolean}
32625      */
32626     isUpdating : function(){
32627         return this.updating;
32628     },
32629
32630     /**
32631      * Suspend the LayoutManager from doing auto-layouts while
32632      * making multiple add or remove calls
32633      */
32634     beginUpdate : function(){
32635         this.updating = true;
32636     },
32637
32638     /**
32639      * Restore auto-layouts and optionally disable the manager from performing a layout
32640      * @param {Boolean} noLayout true to disable a layout update
32641      */
32642     endUpdate : function(noLayout){
32643         this.updating = false;
32644         if(!noLayout){
32645             this.layout();
32646         }
32647     },
32648
32649     layout: function(){
32650         // abstract...
32651     },
32652
32653     onRegionResized : function(region, newSize){
32654         this.fireEvent("regionresized", region, newSize);
32655         this.layout();
32656     },
32657
32658     onRegionCollapsed : function(region){
32659         this.fireEvent("regioncollapsed", region);
32660     },
32661
32662     onRegionExpanded : function(region){
32663         this.fireEvent("regionexpanded", region);
32664     },
32665
32666     /**
32667      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32668      * performs box-model adjustments.
32669      * @return {Object} The size as an object {width: (the width), height: (the height)}
32670      */
32671     getViewSize : function()
32672     {
32673         var size;
32674         if(this.el.dom != document.body){
32675             size = this.el.getSize();
32676         }else{
32677             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32678         }
32679         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32680         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32681         return size;
32682     },
32683
32684     /**
32685      * Returns the Element this layout is bound to.
32686      * @return {Roo.Element}
32687      */
32688     getEl : function(){
32689         return this.el;
32690     },
32691
32692     /**
32693      * Returns the specified region.
32694      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32695      * @return {Roo.LayoutRegion}
32696      */
32697     getRegion : function(target){
32698         return this.regions[target.toLowerCase()];
32699     },
32700
32701     onWindowResize : function(){
32702         if(this.monitorWindowResize){
32703             this.layout();
32704         }
32705     }
32706 });
32707 /*
32708  * Based on:
32709  * Ext JS Library 1.1.1
32710  * Copyright(c) 2006-2007, Ext JS, LLC.
32711  *
32712  * Originally Released Under LGPL - original licence link has changed is not relivant.
32713  *
32714  * Fork - LGPL
32715  * <script type="text/javascript">
32716  */
32717 /**
32718  * @class Roo.bootstrap.layout.Border
32719  * @extends Roo.bootstrap.layout.Manager
32720  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32721  * please see: examples/bootstrap/nested.html<br><br>
32722  
32723 <b>The container the layout is rendered into can be either the body element or any other element.
32724 If it is not the body element, the container needs to either be an absolute positioned element,
32725 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32726 the container size if it is not the body element.</b>
32727
32728 * @constructor
32729 * Create a new Border
32730 * @param {Object} config Configuration options
32731  */
32732 Roo.bootstrap.layout.Border = function(config){
32733     config = config || {};
32734     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32735     
32736     
32737     
32738     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32739         if(config[region]){
32740             config[region].region = region;
32741             this.addRegion(config[region]);
32742         }
32743     },this);
32744     
32745 };
32746
32747 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32748
32749 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32750     /**
32751      * Creates and adds a new region if it doesn't already exist.
32752      * @param {String} target The target region key (north, south, east, west or center).
32753      * @param {Object} config The regions config object
32754      * @return {BorderLayoutRegion} The new region
32755      */
32756     addRegion : function(config)
32757     {
32758         if(!this.regions[config.region]){
32759             var r = this.factory(config);
32760             this.bindRegion(r);
32761         }
32762         return this.regions[config.region];
32763     },
32764
32765     // private (kinda)
32766     bindRegion : function(r){
32767         this.regions[r.config.region] = r;
32768         
32769         r.on("visibilitychange",    this.layout, this);
32770         r.on("paneladded",          this.layout, this);
32771         r.on("panelremoved",        this.layout, this);
32772         r.on("invalidated",         this.layout, this);
32773         r.on("resized",             this.onRegionResized, this);
32774         r.on("collapsed",           this.onRegionCollapsed, this);
32775         r.on("expanded",            this.onRegionExpanded, this);
32776     },
32777
32778     /**
32779      * Performs a layout update.
32780      */
32781     layout : function()
32782     {
32783         if(this.updating) {
32784             return;
32785         }
32786         
32787         // render all the rebions if they have not been done alreayd?
32788         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32789             if(this.regions[region] && !this.regions[region].bodyEl){
32790                 this.regions[region].onRender(this.el)
32791             }
32792         },this);
32793         
32794         var size = this.getViewSize();
32795         var w = size.width;
32796         var h = size.height;
32797         var centerW = w;
32798         var centerH = h;
32799         var centerY = 0;
32800         var centerX = 0;
32801         //var x = 0, y = 0;
32802
32803         var rs = this.regions;
32804         var north = rs["north"];
32805         var south = rs["south"]; 
32806         var west = rs["west"];
32807         var east = rs["east"];
32808         var center = rs["center"];
32809         //if(this.hideOnLayout){ // not supported anymore
32810             //c.el.setStyle("display", "none");
32811         //}
32812         if(north && north.isVisible()){
32813             var b = north.getBox();
32814             var m = north.getMargins();
32815             b.width = w - (m.left+m.right);
32816             b.x = m.left;
32817             b.y = m.top;
32818             centerY = b.height + b.y + m.bottom;
32819             centerH -= centerY;
32820             north.updateBox(this.safeBox(b));
32821         }
32822         if(south && south.isVisible()){
32823             var b = south.getBox();
32824             var m = south.getMargins();
32825             b.width = w - (m.left+m.right);
32826             b.x = m.left;
32827             var totalHeight = (b.height + m.top + m.bottom);
32828             b.y = h - totalHeight + m.top;
32829             centerH -= totalHeight;
32830             south.updateBox(this.safeBox(b));
32831         }
32832         if(west && west.isVisible()){
32833             var b = west.getBox();
32834             var m = west.getMargins();
32835             b.height = centerH - (m.top+m.bottom);
32836             b.x = m.left;
32837             b.y = centerY + m.top;
32838             var totalWidth = (b.width + m.left + m.right);
32839             centerX += totalWidth;
32840             centerW -= totalWidth;
32841             west.updateBox(this.safeBox(b));
32842         }
32843         if(east && east.isVisible()){
32844             var b = east.getBox();
32845             var m = east.getMargins();
32846             b.height = centerH - (m.top+m.bottom);
32847             var totalWidth = (b.width + m.left + m.right);
32848             b.x = w - totalWidth + m.left;
32849             b.y = centerY + m.top;
32850             centerW -= totalWidth;
32851             east.updateBox(this.safeBox(b));
32852         }
32853         if(center){
32854             var m = center.getMargins();
32855             var centerBox = {
32856                 x: centerX + m.left,
32857                 y: centerY + m.top,
32858                 width: centerW - (m.left+m.right),
32859                 height: centerH - (m.top+m.bottom)
32860             };
32861             //if(this.hideOnLayout){
32862                 //center.el.setStyle("display", "block");
32863             //}
32864             center.updateBox(this.safeBox(centerBox));
32865         }
32866         this.el.repaint();
32867         this.fireEvent("layout", this);
32868     },
32869
32870     // private
32871     safeBox : function(box){
32872         box.width = Math.max(0, box.width);
32873         box.height = Math.max(0, box.height);
32874         return box;
32875     },
32876
32877     /**
32878      * Adds a ContentPanel (or subclass) to this layout.
32879      * @param {String} target The target region key (north, south, east, west or center).
32880      * @param {Roo.ContentPanel} panel The panel to add
32881      * @return {Roo.ContentPanel} The added panel
32882      */
32883     add : function(target, panel){
32884          
32885         target = target.toLowerCase();
32886         return this.regions[target].add(panel);
32887     },
32888
32889     /**
32890      * Remove a ContentPanel (or subclass) to this layout.
32891      * @param {String} target The target region key (north, south, east, west or center).
32892      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32893      * @return {Roo.ContentPanel} The removed panel
32894      */
32895     remove : function(target, panel){
32896         target = target.toLowerCase();
32897         return this.regions[target].remove(panel);
32898     },
32899
32900     /**
32901      * Searches all regions for a panel with the specified id
32902      * @param {String} panelId
32903      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32904      */
32905     findPanel : function(panelId){
32906         var rs = this.regions;
32907         for(var target in rs){
32908             if(typeof rs[target] != "function"){
32909                 var p = rs[target].getPanel(panelId);
32910                 if(p){
32911                     return p;
32912                 }
32913             }
32914         }
32915         return null;
32916     },
32917
32918     /**
32919      * Searches all regions for a panel with the specified id and activates (shows) it.
32920      * @param {String/ContentPanel} panelId The panels id or the panel itself
32921      * @return {Roo.ContentPanel} The shown panel or null
32922      */
32923     showPanel : function(panelId) {
32924       var rs = this.regions;
32925       for(var target in rs){
32926          var r = rs[target];
32927          if(typeof r != "function"){
32928             if(r.hasPanel(panelId)){
32929                return r.showPanel(panelId);
32930             }
32931          }
32932       }
32933       return null;
32934    },
32935
32936    /**
32937      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32938      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32939      */
32940    /*
32941     restoreState : function(provider){
32942         if(!provider){
32943             provider = Roo.state.Manager;
32944         }
32945         var sm = new Roo.LayoutStateManager();
32946         sm.init(this, provider);
32947     },
32948 */
32949  
32950  
32951     /**
32952      * Adds a xtype elements to the layout.
32953      * <pre><code>
32954
32955 layout.addxtype({
32956        xtype : 'ContentPanel',
32957        region: 'west',
32958        items: [ .... ]
32959    }
32960 );
32961
32962 layout.addxtype({
32963         xtype : 'NestedLayoutPanel',
32964         region: 'west',
32965         layout: {
32966            center: { },
32967            west: { }   
32968         },
32969         items : [ ... list of content panels or nested layout panels.. ]
32970    }
32971 );
32972 </code></pre>
32973      * @param {Object} cfg Xtype definition of item to add.
32974      */
32975     addxtype : function(cfg)
32976     {
32977         // basically accepts a pannel...
32978         // can accept a layout region..!?!?
32979         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32980         
32981         
32982         // theory?  children can only be panels??
32983         
32984         //if (!cfg.xtype.match(/Panel$/)) {
32985         //    return false;
32986         //}
32987         var ret = false;
32988         
32989         if (typeof(cfg.region) == 'undefined') {
32990             Roo.log("Failed to add Panel, region was not set");
32991             Roo.log(cfg);
32992             return false;
32993         }
32994         var region = cfg.region;
32995         delete cfg.region;
32996         
32997           
32998         var xitems = [];
32999         if (cfg.items) {
33000             xitems = cfg.items;
33001             delete cfg.items;
33002         }
33003         var nb = false;
33004         
33005         switch(cfg.xtype) 
33006         {
33007             case 'Content':  // ContentPanel (el, cfg)
33008             case 'Scroll':  // ContentPanel (el, cfg)
33009             case 'View': 
33010                 cfg.autoCreate = true;
33011                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33012                 //} else {
33013                 //    var el = this.el.createChild();
33014                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33015                 //}
33016                 
33017                 this.add(region, ret);
33018                 break;
33019             
33020             /*
33021             case 'TreePanel': // our new panel!
33022                 cfg.el = this.el.createChild();
33023                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33024                 this.add(region, ret);
33025                 break;
33026             */
33027             
33028             case 'Nest': 
33029                 // create a new Layout (which is  a Border Layout...
33030                 
33031                 var clayout = cfg.layout;
33032                 clayout.el  = this.el.createChild();
33033                 clayout.items   = clayout.items  || [];
33034                 
33035                 delete cfg.layout;
33036                 
33037                 // replace this exitems with the clayout ones..
33038                 xitems = clayout.items;
33039                  
33040                 // force background off if it's in center...
33041                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33042                     cfg.background = false;
33043                 }
33044                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33045                 
33046                 
33047                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33048                 //console.log('adding nested layout panel '  + cfg.toSource());
33049                 this.add(region, ret);
33050                 nb = {}; /// find first...
33051                 break;
33052             
33053             case 'Grid':
33054                 
33055                 // needs grid and region
33056                 
33057                 //var el = this.getRegion(region).el.createChild();
33058                 /*
33059                  *var el = this.el.createChild();
33060                 // create the grid first...
33061                 cfg.grid.container = el;
33062                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33063                 */
33064                 
33065                 if (region == 'center' && this.active ) {
33066                     cfg.background = false;
33067                 }
33068                 
33069                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33070                 
33071                 this.add(region, ret);
33072                 /*
33073                 if (cfg.background) {
33074                     // render grid on panel activation (if panel background)
33075                     ret.on('activate', function(gp) {
33076                         if (!gp.grid.rendered) {
33077                     //        gp.grid.render(el);
33078                         }
33079                     });
33080                 } else {
33081                   //  cfg.grid.render(el);
33082                 }
33083                 */
33084                 break;
33085            
33086            
33087             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33088                 // it was the old xcomponent building that caused this before.
33089                 // espeically if border is the top element in the tree.
33090                 ret = this;
33091                 break; 
33092                 
33093                     
33094                 
33095                 
33096                 
33097             default:
33098                 /*
33099                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33100                     
33101                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33102                     this.add(region, ret);
33103                 } else {
33104                 */
33105                     Roo.log(cfg);
33106                     throw "Can not add '" + cfg.xtype + "' to Border";
33107                     return null;
33108              
33109                                 
33110              
33111         }
33112         this.beginUpdate();
33113         // add children..
33114         var region = '';
33115         var abn = {};
33116         Roo.each(xitems, function(i)  {
33117             region = nb && i.region ? i.region : false;
33118             
33119             var add = ret.addxtype(i);
33120            
33121             if (region) {
33122                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33123                 if (!i.background) {
33124                     abn[region] = nb[region] ;
33125                 }
33126             }
33127             
33128         });
33129         this.endUpdate();
33130
33131         // make the last non-background panel active..
33132         //if (nb) { Roo.log(abn); }
33133         if (nb) {
33134             
33135             for(var r in abn) {
33136                 region = this.getRegion(r);
33137                 if (region) {
33138                     // tried using nb[r], but it does not work..
33139                      
33140                     region.showPanel(abn[r]);
33141                    
33142                 }
33143             }
33144         }
33145         return ret;
33146         
33147     },
33148     
33149     
33150 // private
33151     factory : function(cfg)
33152     {
33153         
33154         var validRegions = Roo.bootstrap.layout.Border.regions;
33155
33156         var target = cfg.region;
33157         cfg.mgr = this;
33158         
33159         var r = Roo.bootstrap.layout;
33160         Roo.log(target);
33161         switch(target){
33162             case "north":
33163                 return new r.North(cfg);
33164             case "south":
33165                 return new r.South(cfg);
33166             case "east":
33167                 return new r.East(cfg);
33168             case "west":
33169                 return new r.West(cfg);
33170             case "center":
33171                 return new r.Center(cfg);
33172         }
33173         throw 'Layout region "'+target+'" not supported.';
33174     }
33175     
33176     
33177 });
33178  /*
33179  * Based on:
33180  * Ext JS Library 1.1.1
33181  * Copyright(c) 2006-2007, Ext JS, LLC.
33182  *
33183  * Originally Released Under LGPL - original licence link has changed is not relivant.
33184  *
33185  * Fork - LGPL
33186  * <script type="text/javascript">
33187  */
33188  
33189 /**
33190  * @class Roo.bootstrap.layout.Basic
33191  * @extends Roo.util.Observable
33192  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33193  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33194  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33195  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33196  * @cfg {string}   region  the region that it inhabits..
33197  * @cfg {bool}   skipConfig skip config?
33198  * 
33199
33200  */
33201 Roo.bootstrap.layout.Basic = function(config){
33202     
33203     this.mgr = config.mgr;
33204     
33205     this.position = config.region;
33206     
33207     var skipConfig = config.skipConfig;
33208     
33209     this.events = {
33210         /**
33211          * @scope Roo.BasicLayoutRegion
33212          */
33213         
33214         /**
33215          * @event beforeremove
33216          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33217          * @param {Roo.LayoutRegion} this
33218          * @param {Roo.ContentPanel} panel The panel
33219          * @param {Object} e The cancel event object
33220          */
33221         "beforeremove" : true,
33222         /**
33223          * @event invalidated
33224          * Fires when the layout for this region is changed.
33225          * @param {Roo.LayoutRegion} this
33226          */
33227         "invalidated" : true,
33228         /**
33229          * @event visibilitychange
33230          * Fires when this region is shown or hidden 
33231          * @param {Roo.LayoutRegion} this
33232          * @param {Boolean} visibility true or false
33233          */
33234         "visibilitychange" : true,
33235         /**
33236          * @event paneladded
33237          * Fires when a panel is added. 
33238          * @param {Roo.LayoutRegion} this
33239          * @param {Roo.ContentPanel} panel The panel
33240          */
33241         "paneladded" : true,
33242         /**
33243          * @event panelremoved
33244          * Fires when a panel is removed. 
33245          * @param {Roo.LayoutRegion} this
33246          * @param {Roo.ContentPanel} panel The panel
33247          */
33248         "panelremoved" : true,
33249         /**
33250          * @event beforecollapse
33251          * Fires when this region before collapse.
33252          * @param {Roo.LayoutRegion} this
33253          */
33254         "beforecollapse" : true,
33255         /**
33256          * @event collapsed
33257          * Fires when this region is collapsed.
33258          * @param {Roo.LayoutRegion} this
33259          */
33260         "collapsed" : true,
33261         /**
33262          * @event expanded
33263          * Fires when this region is expanded.
33264          * @param {Roo.LayoutRegion} this
33265          */
33266         "expanded" : true,
33267         /**
33268          * @event slideshow
33269          * Fires when this region is slid into view.
33270          * @param {Roo.LayoutRegion} this
33271          */
33272         "slideshow" : true,
33273         /**
33274          * @event slidehide
33275          * Fires when this region slides out of view. 
33276          * @param {Roo.LayoutRegion} this
33277          */
33278         "slidehide" : true,
33279         /**
33280          * @event panelactivated
33281          * Fires when a panel is activated. 
33282          * @param {Roo.LayoutRegion} this
33283          * @param {Roo.ContentPanel} panel The activated panel
33284          */
33285         "panelactivated" : true,
33286         /**
33287          * @event resized
33288          * Fires when the user resizes this region. 
33289          * @param {Roo.LayoutRegion} this
33290          * @param {Number} newSize The new size (width for east/west, height for north/south)
33291          */
33292         "resized" : true
33293     };
33294     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33295     this.panels = new Roo.util.MixedCollection();
33296     this.panels.getKey = this.getPanelId.createDelegate(this);
33297     this.box = null;
33298     this.activePanel = null;
33299     // ensure listeners are added...
33300     
33301     if (config.listeners || config.events) {
33302         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33303             listeners : config.listeners || {},
33304             events : config.events || {}
33305         });
33306     }
33307     
33308     if(skipConfig !== true){
33309         this.applyConfig(config);
33310     }
33311 };
33312
33313 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33314 {
33315     getPanelId : function(p){
33316         return p.getId();
33317     },
33318     
33319     applyConfig : function(config){
33320         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33321         this.config = config;
33322         
33323     },
33324     
33325     /**
33326      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33327      * the width, for horizontal (north, south) the height.
33328      * @param {Number} newSize The new width or height
33329      */
33330     resizeTo : function(newSize){
33331         var el = this.el ? this.el :
33332                  (this.activePanel ? this.activePanel.getEl() : null);
33333         if(el){
33334             switch(this.position){
33335                 case "east":
33336                 case "west":
33337                     el.setWidth(newSize);
33338                     this.fireEvent("resized", this, newSize);
33339                 break;
33340                 case "north":
33341                 case "south":
33342                     el.setHeight(newSize);
33343                     this.fireEvent("resized", this, newSize);
33344                 break;                
33345             }
33346         }
33347     },
33348     
33349     getBox : function(){
33350         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33351     },
33352     
33353     getMargins : function(){
33354         return this.margins;
33355     },
33356     
33357     updateBox : function(box){
33358         this.box = box;
33359         var el = this.activePanel.getEl();
33360         el.dom.style.left = box.x + "px";
33361         el.dom.style.top = box.y + "px";
33362         this.activePanel.setSize(box.width, box.height);
33363     },
33364     
33365     /**
33366      * Returns the container element for this region.
33367      * @return {Roo.Element}
33368      */
33369     getEl : function(){
33370         return this.activePanel;
33371     },
33372     
33373     /**
33374      * Returns true if this region is currently visible.
33375      * @return {Boolean}
33376      */
33377     isVisible : function(){
33378         return this.activePanel ? true : false;
33379     },
33380     
33381     setActivePanel : function(panel){
33382         panel = this.getPanel(panel);
33383         if(this.activePanel && this.activePanel != panel){
33384             this.activePanel.setActiveState(false);
33385             this.activePanel.getEl().setLeftTop(-10000,-10000);
33386         }
33387         this.activePanel = panel;
33388         panel.setActiveState(true);
33389         if(this.box){
33390             panel.setSize(this.box.width, this.box.height);
33391         }
33392         this.fireEvent("panelactivated", this, panel);
33393         this.fireEvent("invalidated");
33394     },
33395     
33396     /**
33397      * Show the specified panel.
33398      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33399      * @return {Roo.ContentPanel} The shown panel or null
33400      */
33401     showPanel : function(panel){
33402         panel = this.getPanel(panel);
33403         if(panel){
33404             this.setActivePanel(panel);
33405         }
33406         return panel;
33407     },
33408     
33409     /**
33410      * Get the active panel for this region.
33411      * @return {Roo.ContentPanel} The active panel or null
33412      */
33413     getActivePanel : function(){
33414         return this.activePanel;
33415     },
33416     
33417     /**
33418      * Add the passed ContentPanel(s)
33419      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33420      * @return {Roo.ContentPanel} The panel added (if only one was added)
33421      */
33422     add : function(panel){
33423         if(arguments.length > 1){
33424             for(var i = 0, len = arguments.length; i < len; i++) {
33425                 this.add(arguments[i]);
33426             }
33427             return null;
33428         }
33429         if(this.hasPanel(panel)){
33430             this.showPanel(panel);
33431             return panel;
33432         }
33433         var el = panel.getEl();
33434         if(el.dom.parentNode != this.mgr.el.dom){
33435             this.mgr.el.dom.appendChild(el.dom);
33436         }
33437         if(panel.setRegion){
33438             panel.setRegion(this);
33439         }
33440         this.panels.add(panel);
33441         el.setStyle("position", "absolute");
33442         if(!panel.background){
33443             this.setActivePanel(panel);
33444             if(this.config.initialSize && this.panels.getCount()==1){
33445                 this.resizeTo(this.config.initialSize);
33446             }
33447         }
33448         this.fireEvent("paneladded", this, panel);
33449         return panel;
33450     },
33451     
33452     /**
33453      * Returns true if the panel is in this region.
33454      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33455      * @return {Boolean}
33456      */
33457     hasPanel : function(panel){
33458         if(typeof panel == "object"){ // must be panel obj
33459             panel = panel.getId();
33460         }
33461         return this.getPanel(panel) ? true : false;
33462     },
33463     
33464     /**
33465      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33466      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33467      * @param {Boolean} preservePanel Overrides the config preservePanel option
33468      * @return {Roo.ContentPanel} The panel that was removed
33469      */
33470     remove : function(panel, preservePanel){
33471         panel = this.getPanel(panel);
33472         if(!panel){
33473             return null;
33474         }
33475         var e = {};
33476         this.fireEvent("beforeremove", this, panel, e);
33477         if(e.cancel === true){
33478             return null;
33479         }
33480         var panelId = panel.getId();
33481         this.panels.removeKey(panelId);
33482         return panel;
33483     },
33484     
33485     /**
33486      * Returns the panel specified or null if it's not in this region.
33487      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33488      * @return {Roo.ContentPanel}
33489      */
33490     getPanel : function(id){
33491         if(typeof id == "object"){ // must be panel obj
33492             return id;
33493         }
33494         return this.panels.get(id);
33495     },
33496     
33497     /**
33498      * Returns this regions position (north/south/east/west/center).
33499      * @return {String} 
33500      */
33501     getPosition: function(){
33502         return this.position;    
33503     }
33504 });/*
33505  * Based on:
33506  * Ext JS Library 1.1.1
33507  * Copyright(c) 2006-2007, Ext JS, LLC.
33508  *
33509  * Originally Released Under LGPL - original licence link has changed is not relivant.
33510  *
33511  * Fork - LGPL
33512  * <script type="text/javascript">
33513  */
33514  
33515 /**
33516  * @class Roo.bootstrap.layout.Region
33517  * @extends Roo.bootstrap.layout.Basic
33518  * This class represents a region in a layout manager.
33519  
33520  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33521  * @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})
33522  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33523  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33524  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33525  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33526  * @cfg {String}    title           The title for the region (overrides panel titles)
33527  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33528  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33529  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33530  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33531  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33532  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33533  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33534  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33535  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33536  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33537
33538  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33539  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33540  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33541  * @cfg {Number}    width           For East/West panels
33542  * @cfg {Number}    height          For North/South panels
33543  * @cfg {Boolean}   split           To show the splitter
33544  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33545  * 
33546  * @cfg {string}   cls             Extra CSS classes to add to region
33547  * 
33548  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33549  * @cfg {string}   region  the region that it inhabits..
33550  *
33551
33552  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33553  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33554
33555  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33556  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33557  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33558  */
33559 Roo.bootstrap.layout.Region = function(config)
33560 {
33561     this.applyConfig(config);
33562
33563     var mgr = config.mgr;
33564     var pos = config.region;
33565     config.skipConfig = true;
33566     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33567     
33568     if (mgr.el) {
33569         this.onRender(mgr.el);   
33570     }
33571      
33572     this.visible = true;
33573     this.collapsed = false;
33574     this.unrendered_panels = [];
33575 };
33576
33577 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33578
33579     position: '', // set by wrapper (eg. north/south etc..)
33580     unrendered_panels : null,  // unrendered panels.
33581     createBody : function(){
33582         /** This region's body element 
33583         * @type Roo.Element */
33584         this.bodyEl = this.el.createChild({
33585                 tag: "div",
33586                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33587         });
33588     },
33589
33590     onRender: function(ctr, pos)
33591     {
33592         var dh = Roo.DomHelper;
33593         /** This region's container element 
33594         * @type Roo.Element */
33595         this.el = dh.append(ctr.dom, {
33596                 tag: "div",
33597                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33598             }, true);
33599         /** This region's title element 
33600         * @type Roo.Element */
33601     
33602         this.titleEl = dh.append(this.el.dom,
33603             {
33604                     tag: "div",
33605                     unselectable: "on",
33606                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33607                     children:[
33608                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33609                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33610                     ]}, true);
33611         
33612         this.titleEl.enableDisplayMode();
33613         /** This region's title text element 
33614         * @type HTMLElement */
33615         this.titleTextEl = this.titleEl.dom.firstChild;
33616         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33617         /*
33618         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33619         this.closeBtn.enableDisplayMode();
33620         this.closeBtn.on("click", this.closeClicked, this);
33621         this.closeBtn.hide();
33622     */
33623         this.createBody(this.config);
33624         if(this.config.hideWhenEmpty){
33625             this.hide();
33626             this.on("paneladded", this.validateVisibility, this);
33627             this.on("panelremoved", this.validateVisibility, this);
33628         }
33629         if(this.autoScroll){
33630             this.bodyEl.setStyle("overflow", "auto");
33631         }else{
33632             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33633         }
33634         //if(c.titlebar !== false){
33635             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33636                 this.titleEl.hide();
33637             }else{
33638                 this.titleEl.show();
33639                 if(this.config.title){
33640                     this.titleTextEl.innerHTML = this.config.title;
33641                 }
33642             }
33643         //}
33644         if(this.config.collapsed){
33645             this.collapse(true);
33646         }
33647         if(this.config.hidden){
33648             this.hide();
33649         }
33650         
33651         if (this.unrendered_panels && this.unrendered_panels.length) {
33652             for (var i =0;i< this.unrendered_panels.length; i++) {
33653                 this.add(this.unrendered_panels[i]);
33654             }
33655             this.unrendered_panels = null;
33656             
33657         }
33658         
33659     },
33660     
33661     applyConfig : function(c)
33662     {
33663         /*
33664          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33665             var dh = Roo.DomHelper;
33666             if(c.titlebar !== false){
33667                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33668                 this.collapseBtn.on("click", this.collapse, this);
33669                 this.collapseBtn.enableDisplayMode();
33670                 /*
33671                 if(c.showPin === true || this.showPin){
33672                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33673                     this.stickBtn.enableDisplayMode();
33674                     this.stickBtn.on("click", this.expand, this);
33675                     this.stickBtn.hide();
33676                 }
33677                 
33678             }
33679             */
33680             /** This region's collapsed element
33681             * @type Roo.Element */
33682             /*
33683              *
33684             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33685                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33686             ]}, true);
33687             
33688             if(c.floatable !== false){
33689                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33690                this.collapsedEl.on("click", this.collapseClick, this);
33691             }
33692
33693             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33694                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33695                    id: "message", unselectable: "on", style:{"float":"left"}});
33696                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33697              }
33698             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33699             this.expandBtn.on("click", this.expand, this);
33700             
33701         }
33702         
33703         if(this.collapseBtn){
33704             this.collapseBtn.setVisible(c.collapsible == true);
33705         }
33706         
33707         this.cmargins = c.cmargins || this.cmargins ||
33708                          (this.position == "west" || this.position == "east" ?
33709                              {top: 0, left: 2, right:2, bottom: 0} :
33710                              {top: 2, left: 0, right:0, bottom: 2});
33711         */
33712         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33713         
33714         
33715         this.bottomTabs = c.tabPosition != "top";
33716         
33717         this.autoScroll = c.autoScroll || false;
33718         
33719         
33720        
33721         
33722         this.duration = c.duration || .30;
33723         this.slideDuration = c.slideDuration || .45;
33724         this.config = c;
33725        
33726     },
33727     /**
33728      * Returns true if this region is currently visible.
33729      * @return {Boolean}
33730      */
33731     isVisible : function(){
33732         return this.visible;
33733     },
33734
33735     /**
33736      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33737      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33738      */
33739     //setCollapsedTitle : function(title){
33740     //    title = title || "&#160;";
33741      //   if(this.collapsedTitleTextEl){
33742       //      this.collapsedTitleTextEl.innerHTML = title;
33743        // }
33744     //},
33745
33746     getBox : function(){
33747         var b;
33748       //  if(!this.collapsed){
33749             b = this.el.getBox(false, true);
33750        // }else{
33751           //  b = this.collapsedEl.getBox(false, true);
33752         //}
33753         return b;
33754     },
33755
33756     getMargins : function(){
33757         return this.margins;
33758         //return this.collapsed ? this.cmargins : this.margins;
33759     },
33760 /*
33761     highlight : function(){
33762         this.el.addClass("x-layout-panel-dragover");
33763     },
33764
33765     unhighlight : function(){
33766         this.el.removeClass("x-layout-panel-dragover");
33767     },
33768 */
33769     updateBox : function(box)
33770     {
33771         if (!this.bodyEl) {
33772             return; // not rendered yet..
33773         }
33774         
33775         this.box = box;
33776         if(!this.collapsed){
33777             this.el.dom.style.left = box.x + "px";
33778             this.el.dom.style.top = box.y + "px";
33779             this.updateBody(box.width, box.height);
33780         }else{
33781             this.collapsedEl.dom.style.left = box.x + "px";
33782             this.collapsedEl.dom.style.top = box.y + "px";
33783             this.collapsedEl.setSize(box.width, box.height);
33784         }
33785         if(this.tabs){
33786             this.tabs.autoSizeTabs();
33787         }
33788     },
33789
33790     updateBody : function(w, h)
33791     {
33792         if(w !== null){
33793             this.el.setWidth(w);
33794             w -= this.el.getBorderWidth("rl");
33795             if(this.config.adjustments){
33796                 w += this.config.adjustments[0];
33797             }
33798         }
33799         if(h !== null && h > 0){
33800             this.el.setHeight(h);
33801             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33802             h -= this.el.getBorderWidth("tb");
33803             if(this.config.adjustments){
33804                 h += this.config.adjustments[1];
33805             }
33806             this.bodyEl.setHeight(h);
33807             if(this.tabs){
33808                 h = this.tabs.syncHeight(h);
33809             }
33810         }
33811         if(this.panelSize){
33812             w = w !== null ? w : this.panelSize.width;
33813             h = h !== null ? h : this.panelSize.height;
33814         }
33815         if(this.activePanel){
33816             var el = this.activePanel.getEl();
33817             w = w !== null ? w : el.getWidth();
33818             h = h !== null ? h : el.getHeight();
33819             this.panelSize = {width: w, height: h};
33820             this.activePanel.setSize(w, h);
33821         }
33822         if(Roo.isIE && this.tabs){
33823             this.tabs.el.repaint();
33824         }
33825     },
33826
33827     /**
33828      * Returns the container element for this region.
33829      * @return {Roo.Element}
33830      */
33831     getEl : function(){
33832         return this.el;
33833     },
33834
33835     /**
33836      * Hides this region.
33837      */
33838     hide : function(){
33839         //if(!this.collapsed){
33840             this.el.dom.style.left = "-2000px";
33841             this.el.hide();
33842         //}else{
33843          //   this.collapsedEl.dom.style.left = "-2000px";
33844          //   this.collapsedEl.hide();
33845        // }
33846         this.visible = false;
33847         this.fireEvent("visibilitychange", this, false);
33848     },
33849
33850     /**
33851      * Shows this region if it was previously hidden.
33852      */
33853     show : function(){
33854         //if(!this.collapsed){
33855             this.el.show();
33856         //}else{
33857         //    this.collapsedEl.show();
33858        // }
33859         this.visible = true;
33860         this.fireEvent("visibilitychange", this, true);
33861     },
33862 /*
33863     closeClicked : function(){
33864         if(this.activePanel){
33865             this.remove(this.activePanel);
33866         }
33867     },
33868
33869     collapseClick : function(e){
33870         if(this.isSlid){
33871            e.stopPropagation();
33872            this.slideIn();
33873         }else{
33874            e.stopPropagation();
33875            this.slideOut();
33876         }
33877     },
33878 */
33879     /**
33880      * Collapses this region.
33881      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33882      */
33883     /*
33884     collapse : function(skipAnim, skipCheck = false){
33885         if(this.collapsed) {
33886             return;
33887         }
33888         
33889         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33890             
33891             this.collapsed = true;
33892             if(this.split){
33893                 this.split.el.hide();
33894             }
33895             if(this.config.animate && skipAnim !== true){
33896                 this.fireEvent("invalidated", this);
33897                 this.animateCollapse();
33898             }else{
33899                 this.el.setLocation(-20000,-20000);
33900                 this.el.hide();
33901                 this.collapsedEl.show();
33902                 this.fireEvent("collapsed", this);
33903                 this.fireEvent("invalidated", this);
33904             }
33905         }
33906         
33907     },
33908 */
33909     animateCollapse : function(){
33910         // overridden
33911     },
33912
33913     /**
33914      * Expands this region if it was previously collapsed.
33915      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33916      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33917      */
33918     /*
33919     expand : function(e, skipAnim){
33920         if(e) {
33921             e.stopPropagation();
33922         }
33923         if(!this.collapsed || this.el.hasActiveFx()) {
33924             return;
33925         }
33926         if(this.isSlid){
33927             this.afterSlideIn();
33928             skipAnim = true;
33929         }
33930         this.collapsed = false;
33931         if(this.config.animate && skipAnim !== true){
33932             this.animateExpand();
33933         }else{
33934             this.el.show();
33935             if(this.split){
33936                 this.split.el.show();
33937             }
33938             this.collapsedEl.setLocation(-2000,-2000);
33939             this.collapsedEl.hide();
33940             this.fireEvent("invalidated", this);
33941             this.fireEvent("expanded", this);
33942         }
33943     },
33944 */
33945     animateExpand : function(){
33946         // overridden
33947     },
33948
33949     initTabs : function()
33950     {
33951         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33952         
33953         var ts = new Roo.bootstrap.panel.Tabs({
33954                 el: this.bodyEl.dom,
33955                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33956                 disableTooltips: this.config.disableTabTips,
33957                 toolbar : this.config.toolbar
33958             });
33959         
33960         if(this.config.hideTabs){
33961             ts.stripWrap.setDisplayed(false);
33962         }
33963         this.tabs = ts;
33964         ts.resizeTabs = this.config.resizeTabs === true;
33965         ts.minTabWidth = this.config.minTabWidth || 40;
33966         ts.maxTabWidth = this.config.maxTabWidth || 250;
33967         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33968         ts.monitorResize = false;
33969         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33970         ts.bodyEl.addClass('roo-layout-tabs-body');
33971         this.panels.each(this.initPanelAsTab, this);
33972     },
33973
33974     initPanelAsTab : function(panel){
33975         var ti = this.tabs.addTab(
33976             panel.getEl().id,
33977             panel.getTitle(),
33978             null,
33979             this.config.closeOnTab && panel.isClosable(),
33980             panel.tpl
33981         );
33982         if(panel.tabTip !== undefined){
33983             ti.setTooltip(panel.tabTip);
33984         }
33985         ti.on("activate", function(){
33986               this.setActivePanel(panel);
33987         }, this);
33988         
33989         if(this.config.closeOnTab){
33990             ti.on("beforeclose", function(t, e){
33991                 e.cancel = true;
33992                 this.remove(panel);
33993             }, this);
33994         }
33995         
33996         panel.tabItem = ti;
33997         
33998         return ti;
33999     },
34000
34001     updatePanelTitle : function(panel, title)
34002     {
34003         if(this.activePanel == panel){
34004             this.updateTitle(title);
34005         }
34006         if(this.tabs){
34007             var ti = this.tabs.getTab(panel.getEl().id);
34008             ti.setText(title);
34009             if(panel.tabTip !== undefined){
34010                 ti.setTooltip(panel.tabTip);
34011             }
34012         }
34013     },
34014
34015     updateTitle : function(title){
34016         if(this.titleTextEl && !this.config.title){
34017             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34018         }
34019     },
34020
34021     setActivePanel : function(panel)
34022     {
34023         panel = this.getPanel(panel);
34024         if(this.activePanel && this.activePanel != panel){
34025             this.activePanel.setActiveState(false);
34026         }
34027         this.activePanel = panel;
34028         panel.setActiveState(true);
34029         if(this.panelSize){
34030             panel.setSize(this.panelSize.width, this.panelSize.height);
34031         }
34032         if(this.closeBtn){
34033             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34034         }
34035         this.updateTitle(panel.getTitle());
34036         if(this.tabs){
34037             this.fireEvent("invalidated", this);
34038         }
34039         this.fireEvent("panelactivated", this, panel);
34040     },
34041
34042     /**
34043      * Shows the specified panel.
34044      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34045      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34046      */
34047     showPanel : function(panel)
34048     {
34049         panel = this.getPanel(panel);
34050         if(panel){
34051             if(this.tabs){
34052                 var tab = this.tabs.getTab(panel.getEl().id);
34053                 if(tab.isHidden()){
34054                     this.tabs.unhideTab(tab.id);
34055                 }
34056                 tab.activate();
34057             }else{
34058                 this.setActivePanel(panel);
34059             }
34060         }
34061         return panel;
34062     },
34063
34064     /**
34065      * Get the active panel for this region.
34066      * @return {Roo.ContentPanel} The active panel or null
34067      */
34068     getActivePanel : function(){
34069         return this.activePanel;
34070     },
34071
34072     validateVisibility : function(){
34073         if(this.panels.getCount() < 1){
34074             this.updateTitle("&#160;");
34075             this.closeBtn.hide();
34076             this.hide();
34077         }else{
34078             if(!this.isVisible()){
34079                 this.show();
34080             }
34081         }
34082     },
34083
34084     /**
34085      * Adds the passed ContentPanel(s) to this region.
34086      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34087      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34088      */
34089     add : function(panel)
34090     {
34091         if(arguments.length > 1){
34092             for(var i = 0, len = arguments.length; i < len; i++) {
34093                 this.add(arguments[i]);
34094             }
34095             return null;
34096         }
34097         
34098         // if we have not been rendered yet, then we can not really do much of this..
34099         if (!this.bodyEl) {
34100             this.unrendered_panels.push(panel);
34101             return panel;
34102         }
34103         
34104         
34105         
34106         
34107         if(this.hasPanel(panel)){
34108             this.showPanel(panel);
34109             return panel;
34110         }
34111         panel.setRegion(this);
34112         this.panels.add(panel);
34113        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34114             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34115             // and hide them... ???
34116             this.bodyEl.dom.appendChild(panel.getEl().dom);
34117             if(panel.background !== true){
34118                 this.setActivePanel(panel);
34119             }
34120             this.fireEvent("paneladded", this, panel);
34121             return panel;
34122         }
34123         */
34124         if(!this.tabs){
34125             this.initTabs();
34126         }else{
34127             this.initPanelAsTab(panel);
34128         }
34129         
34130         
34131         if(panel.background !== true){
34132             this.tabs.activate(panel.getEl().id);
34133         }
34134         this.fireEvent("paneladded", this, panel);
34135         return panel;
34136     },
34137
34138     /**
34139      * Hides the tab for the specified panel.
34140      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34141      */
34142     hidePanel : function(panel){
34143         if(this.tabs && (panel = this.getPanel(panel))){
34144             this.tabs.hideTab(panel.getEl().id);
34145         }
34146     },
34147
34148     /**
34149      * Unhides the tab for a previously hidden panel.
34150      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34151      */
34152     unhidePanel : function(panel){
34153         if(this.tabs && (panel = this.getPanel(panel))){
34154             this.tabs.unhideTab(panel.getEl().id);
34155         }
34156     },
34157
34158     clearPanels : function(){
34159         while(this.panels.getCount() > 0){
34160              this.remove(this.panels.first());
34161         }
34162     },
34163
34164     /**
34165      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34166      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34167      * @param {Boolean} preservePanel Overrides the config preservePanel option
34168      * @return {Roo.ContentPanel} The panel that was removed
34169      */
34170     remove : function(panel, preservePanel)
34171     {
34172         panel = this.getPanel(panel);
34173         if(!panel){
34174             return null;
34175         }
34176         var e = {};
34177         this.fireEvent("beforeremove", this, panel, e);
34178         if(e.cancel === true){
34179             return null;
34180         }
34181         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34182         var panelId = panel.getId();
34183         this.panels.removeKey(panelId);
34184         if(preservePanel){
34185             document.body.appendChild(panel.getEl().dom);
34186         }
34187         if(this.tabs){
34188             this.tabs.removeTab(panel.getEl().id);
34189         }else if (!preservePanel){
34190             this.bodyEl.dom.removeChild(panel.getEl().dom);
34191         }
34192         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34193             var p = this.panels.first();
34194             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34195             tempEl.appendChild(p.getEl().dom);
34196             this.bodyEl.update("");
34197             this.bodyEl.dom.appendChild(p.getEl().dom);
34198             tempEl = null;
34199             this.updateTitle(p.getTitle());
34200             this.tabs = null;
34201             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34202             this.setActivePanel(p);
34203         }
34204         panel.setRegion(null);
34205         if(this.activePanel == panel){
34206             this.activePanel = null;
34207         }
34208         if(this.config.autoDestroy !== false && preservePanel !== true){
34209             try{panel.destroy();}catch(e){}
34210         }
34211         this.fireEvent("panelremoved", this, panel);
34212         return panel;
34213     },
34214
34215     /**
34216      * Returns the TabPanel component used by this region
34217      * @return {Roo.TabPanel}
34218      */
34219     getTabs : function(){
34220         return this.tabs;
34221     },
34222
34223     createTool : function(parentEl, className){
34224         var btn = Roo.DomHelper.append(parentEl, {
34225             tag: "div",
34226             cls: "x-layout-tools-button",
34227             children: [ {
34228                 tag: "div",
34229                 cls: "roo-layout-tools-button-inner " + className,
34230                 html: "&#160;"
34231             }]
34232         }, true);
34233         btn.addClassOnOver("roo-layout-tools-button-over");
34234         return btn;
34235     }
34236 });/*
34237  * Based on:
34238  * Ext JS Library 1.1.1
34239  * Copyright(c) 2006-2007, Ext JS, LLC.
34240  *
34241  * Originally Released Under LGPL - original licence link has changed is not relivant.
34242  *
34243  * Fork - LGPL
34244  * <script type="text/javascript">
34245  */
34246  
34247
34248
34249 /**
34250  * @class Roo.SplitLayoutRegion
34251  * @extends Roo.LayoutRegion
34252  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34253  */
34254 Roo.bootstrap.layout.Split = function(config){
34255     this.cursor = config.cursor;
34256     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34257 };
34258
34259 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34260 {
34261     splitTip : "Drag to resize.",
34262     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34263     useSplitTips : false,
34264
34265     applyConfig : function(config){
34266         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34267     },
34268     
34269     onRender : function(ctr,pos) {
34270         
34271         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34272         if(!this.config.split){
34273             return;
34274         }
34275         if(!this.split){
34276             
34277             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34278                             tag: "div",
34279                             id: this.el.id + "-split",
34280                             cls: "roo-layout-split roo-layout-split-"+this.position,
34281                             html: "&#160;"
34282             });
34283             /** The SplitBar for this region 
34284             * @type Roo.SplitBar */
34285             // does not exist yet...
34286             Roo.log([this.position, this.orientation]);
34287             
34288             this.split = new Roo.bootstrap.SplitBar({
34289                 dragElement : splitEl,
34290                 resizingElement: this.el,
34291                 orientation : this.orientation
34292             });
34293             
34294             this.split.on("moved", this.onSplitMove, this);
34295             this.split.useShim = this.config.useShim === true;
34296             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34297             if(this.useSplitTips){
34298                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34299             }
34300             //if(config.collapsible){
34301             //    this.split.el.on("dblclick", this.collapse,  this);
34302             //}
34303         }
34304         if(typeof this.config.minSize != "undefined"){
34305             this.split.minSize = this.config.minSize;
34306         }
34307         if(typeof this.config.maxSize != "undefined"){
34308             this.split.maxSize = this.config.maxSize;
34309         }
34310         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34311             this.hideSplitter();
34312         }
34313         
34314     },
34315
34316     getHMaxSize : function(){
34317          var cmax = this.config.maxSize || 10000;
34318          var center = this.mgr.getRegion("center");
34319          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34320     },
34321
34322     getVMaxSize : function(){
34323          var cmax = this.config.maxSize || 10000;
34324          var center = this.mgr.getRegion("center");
34325          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34326     },
34327
34328     onSplitMove : function(split, newSize){
34329         this.fireEvent("resized", this, newSize);
34330     },
34331     
34332     /** 
34333      * Returns the {@link Roo.SplitBar} for this region.
34334      * @return {Roo.SplitBar}
34335      */
34336     getSplitBar : function(){
34337         return this.split;
34338     },
34339     
34340     hide : function(){
34341         this.hideSplitter();
34342         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34343     },
34344
34345     hideSplitter : function(){
34346         if(this.split){
34347             this.split.el.setLocation(-2000,-2000);
34348             this.split.el.hide();
34349         }
34350     },
34351
34352     show : function(){
34353         if(this.split){
34354             this.split.el.show();
34355         }
34356         Roo.bootstrap.layout.Split.superclass.show.call(this);
34357     },
34358     
34359     beforeSlide: function(){
34360         if(Roo.isGecko){// firefox overflow auto bug workaround
34361             this.bodyEl.clip();
34362             if(this.tabs) {
34363                 this.tabs.bodyEl.clip();
34364             }
34365             if(this.activePanel){
34366                 this.activePanel.getEl().clip();
34367                 
34368                 if(this.activePanel.beforeSlide){
34369                     this.activePanel.beforeSlide();
34370                 }
34371             }
34372         }
34373     },
34374     
34375     afterSlide : function(){
34376         if(Roo.isGecko){// firefox overflow auto bug workaround
34377             this.bodyEl.unclip();
34378             if(this.tabs) {
34379                 this.tabs.bodyEl.unclip();
34380             }
34381             if(this.activePanel){
34382                 this.activePanel.getEl().unclip();
34383                 if(this.activePanel.afterSlide){
34384                     this.activePanel.afterSlide();
34385                 }
34386             }
34387         }
34388     },
34389
34390     initAutoHide : function(){
34391         if(this.autoHide !== false){
34392             if(!this.autoHideHd){
34393                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34394                 this.autoHideHd = {
34395                     "mouseout": function(e){
34396                         if(!e.within(this.el, true)){
34397                             st.delay(500);
34398                         }
34399                     },
34400                     "mouseover" : function(e){
34401                         st.cancel();
34402                     },
34403                     scope : this
34404                 };
34405             }
34406             this.el.on(this.autoHideHd);
34407         }
34408     },
34409
34410     clearAutoHide : function(){
34411         if(this.autoHide !== false){
34412             this.el.un("mouseout", this.autoHideHd.mouseout);
34413             this.el.un("mouseover", this.autoHideHd.mouseover);
34414         }
34415     },
34416
34417     clearMonitor : function(){
34418         Roo.get(document).un("click", this.slideInIf, this);
34419     },
34420
34421     // these names are backwards but not changed for compat
34422     slideOut : function(){
34423         if(this.isSlid || this.el.hasActiveFx()){
34424             return;
34425         }
34426         this.isSlid = true;
34427         if(this.collapseBtn){
34428             this.collapseBtn.hide();
34429         }
34430         this.closeBtnState = this.closeBtn.getStyle('display');
34431         this.closeBtn.hide();
34432         if(this.stickBtn){
34433             this.stickBtn.show();
34434         }
34435         this.el.show();
34436         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34437         this.beforeSlide();
34438         this.el.setStyle("z-index", 10001);
34439         this.el.slideIn(this.getSlideAnchor(), {
34440             callback: function(){
34441                 this.afterSlide();
34442                 this.initAutoHide();
34443                 Roo.get(document).on("click", this.slideInIf, this);
34444                 this.fireEvent("slideshow", this);
34445             },
34446             scope: this,
34447             block: true
34448         });
34449     },
34450
34451     afterSlideIn : function(){
34452         this.clearAutoHide();
34453         this.isSlid = false;
34454         this.clearMonitor();
34455         this.el.setStyle("z-index", "");
34456         if(this.collapseBtn){
34457             this.collapseBtn.show();
34458         }
34459         this.closeBtn.setStyle('display', this.closeBtnState);
34460         if(this.stickBtn){
34461             this.stickBtn.hide();
34462         }
34463         this.fireEvent("slidehide", this);
34464     },
34465
34466     slideIn : function(cb){
34467         if(!this.isSlid || this.el.hasActiveFx()){
34468             Roo.callback(cb);
34469             return;
34470         }
34471         this.isSlid = false;
34472         this.beforeSlide();
34473         this.el.slideOut(this.getSlideAnchor(), {
34474             callback: function(){
34475                 this.el.setLeftTop(-10000, -10000);
34476                 this.afterSlide();
34477                 this.afterSlideIn();
34478                 Roo.callback(cb);
34479             },
34480             scope: this,
34481             block: true
34482         });
34483     },
34484     
34485     slideInIf : function(e){
34486         if(!e.within(this.el)){
34487             this.slideIn();
34488         }
34489     },
34490
34491     animateCollapse : function(){
34492         this.beforeSlide();
34493         this.el.setStyle("z-index", 20000);
34494         var anchor = this.getSlideAnchor();
34495         this.el.slideOut(anchor, {
34496             callback : function(){
34497                 this.el.setStyle("z-index", "");
34498                 this.collapsedEl.slideIn(anchor, {duration:.3});
34499                 this.afterSlide();
34500                 this.el.setLocation(-10000,-10000);
34501                 this.el.hide();
34502                 this.fireEvent("collapsed", this);
34503             },
34504             scope: this,
34505             block: true
34506         });
34507     },
34508
34509     animateExpand : function(){
34510         this.beforeSlide();
34511         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34512         this.el.setStyle("z-index", 20000);
34513         this.collapsedEl.hide({
34514             duration:.1
34515         });
34516         this.el.slideIn(this.getSlideAnchor(), {
34517             callback : function(){
34518                 this.el.setStyle("z-index", "");
34519                 this.afterSlide();
34520                 if(this.split){
34521                     this.split.el.show();
34522                 }
34523                 this.fireEvent("invalidated", this);
34524                 this.fireEvent("expanded", this);
34525             },
34526             scope: this,
34527             block: true
34528         });
34529     },
34530
34531     anchors : {
34532         "west" : "left",
34533         "east" : "right",
34534         "north" : "top",
34535         "south" : "bottom"
34536     },
34537
34538     sanchors : {
34539         "west" : "l",
34540         "east" : "r",
34541         "north" : "t",
34542         "south" : "b"
34543     },
34544
34545     canchors : {
34546         "west" : "tl-tr",
34547         "east" : "tr-tl",
34548         "north" : "tl-bl",
34549         "south" : "bl-tl"
34550     },
34551
34552     getAnchor : function(){
34553         return this.anchors[this.position];
34554     },
34555
34556     getCollapseAnchor : function(){
34557         return this.canchors[this.position];
34558     },
34559
34560     getSlideAnchor : function(){
34561         return this.sanchors[this.position];
34562     },
34563
34564     getAlignAdj : function(){
34565         var cm = this.cmargins;
34566         switch(this.position){
34567             case "west":
34568                 return [0, 0];
34569             break;
34570             case "east":
34571                 return [0, 0];
34572             break;
34573             case "north":
34574                 return [0, 0];
34575             break;
34576             case "south":
34577                 return [0, 0];
34578             break;
34579         }
34580     },
34581
34582     getExpandAdj : function(){
34583         var c = this.collapsedEl, cm = this.cmargins;
34584         switch(this.position){
34585             case "west":
34586                 return [-(cm.right+c.getWidth()+cm.left), 0];
34587             break;
34588             case "east":
34589                 return [cm.right+c.getWidth()+cm.left, 0];
34590             break;
34591             case "north":
34592                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34593             break;
34594             case "south":
34595                 return [0, cm.top+cm.bottom+c.getHeight()];
34596             break;
34597         }
34598     }
34599 });/*
34600  * Based on:
34601  * Ext JS Library 1.1.1
34602  * Copyright(c) 2006-2007, Ext JS, LLC.
34603  *
34604  * Originally Released Under LGPL - original licence link has changed is not relivant.
34605  *
34606  * Fork - LGPL
34607  * <script type="text/javascript">
34608  */
34609 /*
34610  * These classes are private internal classes
34611  */
34612 Roo.bootstrap.layout.Center = function(config){
34613     config.region = "center";
34614     Roo.bootstrap.layout.Region.call(this, config);
34615     this.visible = true;
34616     this.minWidth = config.minWidth || 20;
34617     this.minHeight = config.minHeight || 20;
34618 };
34619
34620 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34621     hide : function(){
34622         // center panel can't be hidden
34623     },
34624     
34625     show : function(){
34626         // center panel can't be hidden
34627     },
34628     
34629     getMinWidth: function(){
34630         return this.minWidth;
34631     },
34632     
34633     getMinHeight: function(){
34634         return this.minHeight;
34635     }
34636 });
34637
34638
34639
34640
34641  
34642
34643
34644
34645
34646
34647 Roo.bootstrap.layout.North = function(config)
34648 {
34649     config.region = 'north';
34650     config.cursor = 'n-resize';
34651     
34652     Roo.bootstrap.layout.Split.call(this, config);
34653     
34654     
34655     if(this.split){
34656         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34657         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34658         this.split.el.addClass("roo-layout-split-v");
34659     }
34660     var size = config.initialSize || config.height;
34661     if(typeof size != "undefined"){
34662         this.el.setHeight(size);
34663     }
34664 };
34665 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34666 {
34667     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34668     
34669     
34670     
34671     getBox : function(){
34672         if(this.collapsed){
34673             return this.collapsedEl.getBox();
34674         }
34675         var box = this.el.getBox();
34676         if(this.split){
34677             box.height += this.split.el.getHeight();
34678         }
34679         return box;
34680     },
34681     
34682     updateBox : function(box){
34683         if(this.split && !this.collapsed){
34684             box.height -= this.split.el.getHeight();
34685             this.split.el.setLeft(box.x);
34686             this.split.el.setTop(box.y+box.height);
34687             this.split.el.setWidth(box.width);
34688         }
34689         if(this.collapsed){
34690             this.updateBody(box.width, null);
34691         }
34692         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34693     }
34694 });
34695
34696
34697
34698
34699
34700 Roo.bootstrap.layout.South = function(config){
34701     config.region = 'south';
34702     config.cursor = 's-resize';
34703     Roo.bootstrap.layout.Split.call(this, config);
34704     if(this.split){
34705         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34706         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34707         this.split.el.addClass("roo-layout-split-v");
34708     }
34709     var size = config.initialSize || config.height;
34710     if(typeof size != "undefined"){
34711         this.el.setHeight(size);
34712     }
34713 };
34714
34715 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34716     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34717     getBox : function(){
34718         if(this.collapsed){
34719             return this.collapsedEl.getBox();
34720         }
34721         var box = this.el.getBox();
34722         if(this.split){
34723             var sh = this.split.el.getHeight();
34724             box.height += sh;
34725             box.y -= sh;
34726         }
34727         return box;
34728     },
34729     
34730     updateBox : function(box){
34731         if(this.split && !this.collapsed){
34732             var sh = this.split.el.getHeight();
34733             box.height -= sh;
34734             box.y += sh;
34735             this.split.el.setLeft(box.x);
34736             this.split.el.setTop(box.y-sh);
34737             this.split.el.setWidth(box.width);
34738         }
34739         if(this.collapsed){
34740             this.updateBody(box.width, null);
34741         }
34742         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34743     }
34744 });
34745
34746 Roo.bootstrap.layout.East = function(config){
34747     config.region = "east";
34748     config.cursor = "e-resize";
34749     Roo.bootstrap.layout.Split.call(this, config);
34750     if(this.split){
34751         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34752         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34753         this.split.el.addClass("roo-layout-split-h");
34754     }
34755     var size = config.initialSize || config.width;
34756     if(typeof size != "undefined"){
34757         this.el.setWidth(size);
34758     }
34759 };
34760 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34761     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34762     getBox : function(){
34763         if(this.collapsed){
34764             return this.collapsedEl.getBox();
34765         }
34766         var box = this.el.getBox();
34767         if(this.split){
34768             var sw = this.split.el.getWidth();
34769             box.width += sw;
34770             box.x -= sw;
34771         }
34772         return box;
34773     },
34774
34775     updateBox : function(box){
34776         if(this.split && !this.collapsed){
34777             var sw = this.split.el.getWidth();
34778             box.width -= sw;
34779             this.split.el.setLeft(box.x);
34780             this.split.el.setTop(box.y);
34781             this.split.el.setHeight(box.height);
34782             box.x += sw;
34783         }
34784         if(this.collapsed){
34785             this.updateBody(null, box.height);
34786         }
34787         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34788     }
34789 });
34790
34791 Roo.bootstrap.layout.West = function(config){
34792     config.region = "west";
34793     config.cursor = "w-resize";
34794     
34795     Roo.bootstrap.layout.Split.call(this, config);
34796     if(this.split){
34797         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34798         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34799         this.split.el.addClass("roo-layout-split-h");
34800     }
34801     
34802 };
34803 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34804     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34805     
34806     onRender: function(ctr, pos)
34807     {
34808         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34809         var size = this.config.initialSize || this.config.width;
34810         if(typeof size != "undefined"){
34811             this.el.setWidth(size);
34812         }
34813     },
34814     
34815     getBox : function(){
34816         if(this.collapsed){
34817             return this.collapsedEl.getBox();
34818         }
34819         var box = this.el.getBox();
34820         if(this.split){
34821             box.width += this.split.el.getWidth();
34822         }
34823         return box;
34824     },
34825     
34826     updateBox : function(box){
34827         if(this.split && !this.collapsed){
34828             var sw = this.split.el.getWidth();
34829             box.width -= sw;
34830             this.split.el.setLeft(box.x+box.width);
34831             this.split.el.setTop(box.y);
34832             this.split.el.setHeight(box.height);
34833         }
34834         if(this.collapsed){
34835             this.updateBody(null, box.height);
34836         }
34837         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34838     }
34839 });
34840 Roo.namespace("Roo.bootstrap.panel");/*
34841  * Based on:
34842  * Ext JS Library 1.1.1
34843  * Copyright(c) 2006-2007, Ext JS, LLC.
34844  *
34845  * Originally Released Under LGPL - original licence link has changed is not relivant.
34846  *
34847  * Fork - LGPL
34848  * <script type="text/javascript">
34849  */
34850 /**
34851  * @class Roo.ContentPanel
34852  * @extends Roo.util.Observable
34853  * A basic ContentPanel element.
34854  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34855  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34856  * @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
34857  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34858  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34859  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34860  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34861  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34862  * @cfg {String} title          The title for this panel
34863  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34864  * @cfg {String} url            Calls {@link #setUrl} with this value
34865  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34866  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34867  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34868  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34869  * @cfg {Boolean} badges render the badges
34870
34871  * @constructor
34872  * Create a new ContentPanel.
34873  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34874  * @param {String/Object} config A string to set only the title or a config object
34875  * @param {String} content (optional) Set the HTML content for this panel
34876  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34877  */
34878 Roo.bootstrap.panel.Content = function( config){
34879     
34880     this.tpl = config.tpl || false;
34881     
34882     var el = config.el;
34883     var content = config.content;
34884
34885     if(config.autoCreate){ // xtype is available if this is called from factory
34886         el = Roo.id();
34887     }
34888     this.el = Roo.get(el);
34889     if(!this.el && config && config.autoCreate){
34890         if(typeof config.autoCreate == "object"){
34891             if(!config.autoCreate.id){
34892                 config.autoCreate.id = config.id||el;
34893             }
34894             this.el = Roo.DomHelper.append(document.body,
34895                         config.autoCreate, true);
34896         }else{
34897             var elcfg =  {   tag: "div",
34898                             cls: "roo-layout-inactive-content",
34899                             id: config.id||el
34900                             };
34901             if (config.html) {
34902                 elcfg.html = config.html;
34903                 
34904             }
34905                         
34906             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34907         }
34908     } 
34909     this.closable = false;
34910     this.loaded = false;
34911     this.active = false;
34912    
34913       
34914     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34915         
34916         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34917         
34918         this.wrapEl = this.el; //this.el.wrap();
34919         var ti = [];
34920         if (config.toolbar.items) {
34921             ti = config.toolbar.items ;
34922             delete config.toolbar.items ;
34923         }
34924         
34925         var nitems = [];
34926         this.toolbar.render(this.wrapEl, 'before');
34927         for(var i =0;i < ti.length;i++) {
34928           //  Roo.log(['add child', items[i]]);
34929             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34930         }
34931         this.toolbar.items = nitems;
34932         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34933         delete config.toolbar;
34934         
34935     }
34936     /*
34937     // xtype created footer. - not sure if will work as we normally have to render first..
34938     if (this.footer && !this.footer.el && this.footer.xtype) {
34939         if (!this.wrapEl) {
34940             this.wrapEl = this.el.wrap();
34941         }
34942     
34943         this.footer.container = this.wrapEl.createChild();
34944          
34945         this.footer = Roo.factory(this.footer, Roo);
34946         
34947     }
34948     */
34949     
34950      if(typeof config == "string"){
34951         this.title = config;
34952     }else{
34953         Roo.apply(this, config);
34954     }
34955     
34956     if(this.resizeEl){
34957         this.resizeEl = Roo.get(this.resizeEl, true);
34958     }else{
34959         this.resizeEl = this.el;
34960     }
34961     // handle view.xtype
34962     
34963  
34964     
34965     
34966     this.addEvents({
34967         /**
34968          * @event activate
34969          * Fires when this panel is activated. 
34970          * @param {Roo.ContentPanel} this
34971          */
34972         "activate" : true,
34973         /**
34974          * @event deactivate
34975          * Fires when this panel is activated. 
34976          * @param {Roo.ContentPanel} this
34977          */
34978         "deactivate" : true,
34979
34980         /**
34981          * @event resize
34982          * Fires when this panel is resized if fitToFrame is true.
34983          * @param {Roo.ContentPanel} this
34984          * @param {Number} width The width after any component adjustments
34985          * @param {Number} height The height after any component adjustments
34986          */
34987         "resize" : true,
34988         
34989          /**
34990          * @event render
34991          * Fires when this tab is created
34992          * @param {Roo.ContentPanel} this
34993          */
34994         "render" : true
34995         
34996         
34997         
34998     });
34999     
35000
35001     
35002     
35003     if(this.autoScroll){
35004         this.resizeEl.setStyle("overflow", "auto");
35005     } else {
35006         // fix randome scrolling
35007         //this.el.on('scroll', function() {
35008         //    Roo.log('fix random scolling');
35009         //    this.scrollTo('top',0); 
35010         //});
35011     }
35012     content = content || this.content;
35013     if(content){
35014         this.setContent(content);
35015     }
35016     if(config && config.url){
35017         this.setUrl(this.url, this.params, this.loadOnce);
35018     }
35019     
35020     
35021     
35022     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35023     
35024     if (this.view && typeof(this.view.xtype) != 'undefined') {
35025         this.view.el = this.el.appendChild(document.createElement("div"));
35026         this.view = Roo.factory(this.view); 
35027         this.view.render  &&  this.view.render(false, '');  
35028     }
35029     
35030     
35031     this.fireEvent('render', this);
35032 };
35033
35034 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35035     
35036     tabTip : '',
35037     
35038     setRegion : function(region){
35039         this.region = region;
35040         this.setActiveClass(region && !this.background);
35041     },
35042     
35043     
35044     setActiveClass: function(state)
35045     {
35046         if(state){
35047            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35048            this.el.setStyle('position','relative');
35049         }else{
35050            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35051            this.el.setStyle('position', 'absolute');
35052         } 
35053     },
35054     
35055     /**
35056      * Returns the toolbar for this Panel if one was configured. 
35057      * @return {Roo.Toolbar} 
35058      */
35059     getToolbar : function(){
35060         return this.toolbar;
35061     },
35062     
35063     setActiveState : function(active)
35064     {
35065         this.active = active;
35066         this.setActiveClass(active);
35067         if(!active){
35068             this.fireEvent("deactivate", this);
35069         }else{
35070             this.fireEvent("activate", this);
35071         }
35072     },
35073     /**
35074      * Updates this panel's element
35075      * @param {String} content The new content
35076      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35077     */
35078     setContent : function(content, loadScripts){
35079         this.el.update(content, loadScripts);
35080     },
35081
35082     ignoreResize : function(w, h){
35083         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35084             return true;
35085         }else{
35086             this.lastSize = {width: w, height: h};
35087             return false;
35088         }
35089     },
35090     /**
35091      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35092      * @return {Roo.UpdateManager} The UpdateManager
35093      */
35094     getUpdateManager : function(){
35095         return this.el.getUpdateManager();
35096     },
35097      /**
35098      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35099      * @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:
35100 <pre><code>
35101 panel.load({
35102     url: "your-url.php",
35103     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35104     callback: yourFunction,
35105     scope: yourObject, //(optional scope)
35106     discardUrl: false,
35107     nocache: false,
35108     text: "Loading...",
35109     timeout: 30,
35110     scripts: false
35111 });
35112 </code></pre>
35113      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35114      * 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.
35115      * @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}
35116      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35117      * @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.
35118      * @return {Roo.ContentPanel} this
35119      */
35120     load : function(){
35121         var um = this.el.getUpdateManager();
35122         um.update.apply(um, arguments);
35123         return this;
35124     },
35125
35126
35127     /**
35128      * 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.
35129      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35130      * @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)
35131      * @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)
35132      * @return {Roo.UpdateManager} The UpdateManager
35133      */
35134     setUrl : function(url, params, loadOnce){
35135         if(this.refreshDelegate){
35136             this.removeListener("activate", this.refreshDelegate);
35137         }
35138         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35139         this.on("activate", this.refreshDelegate);
35140         return this.el.getUpdateManager();
35141     },
35142     
35143     _handleRefresh : function(url, params, loadOnce){
35144         if(!loadOnce || !this.loaded){
35145             var updater = this.el.getUpdateManager();
35146             updater.update(url, params, this._setLoaded.createDelegate(this));
35147         }
35148     },
35149     
35150     _setLoaded : function(){
35151         this.loaded = true;
35152     }, 
35153     
35154     /**
35155      * Returns this panel's id
35156      * @return {String} 
35157      */
35158     getId : function(){
35159         return this.el.id;
35160     },
35161     
35162     /** 
35163      * Returns this panel's element - used by regiosn to add.
35164      * @return {Roo.Element} 
35165      */
35166     getEl : function(){
35167         return this.wrapEl || this.el;
35168     },
35169     
35170    
35171     
35172     adjustForComponents : function(width, height)
35173     {
35174         //Roo.log('adjustForComponents ');
35175         if(this.resizeEl != this.el){
35176             width -= this.el.getFrameWidth('lr');
35177             height -= this.el.getFrameWidth('tb');
35178         }
35179         if(this.toolbar){
35180             var te = this.toolbar.getEl();
35181             height -= te.getHeight();
35182             te.setWidth(width);
35183         }
35184         if(this.footer){
35185             var te = this.footer.getEl();
35186             Roo.log("footer:" + te.getHeight());
35187             
35188             height -= te.getHeight();
35189             te.setWidth(width);
35190         }
35191         
35192         
35193         if(this.adjustments){
35194             width += this.adjustments[0];
35195             height += this.adjustments[1];
35196         }
35197         return {"width": width, "height": height};
35198     },
35199     
35200     setSize : function(width, height){
35201         if(this.fitToFrame && !this.ignoreResize(width, height)){
35202             if(this.fitContainer && this.resizeEl != this.el){
35203                 this.el.setSize(width, height);
35204             }
35205             var size = this.adjustForComponents(width, height);
35206             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35207             this.fireEvent('resize', this, size.width, size.height);
35208         }
35209     },
35210     
35211     /**
35212      * Returns this panel's title
35213      * @return {String} 
35214      */
35215     getTitle : function(){
35216         return this.title;
35217     },
35218     
35219     /**
35220      * Set this panel's title
35221      * @param {String} title
35222      */
35223     setTitle : function(title){
35224         this.title = title;
35225         if(this.region){
35226             this.region.updatePanelTitle(this, title);
35227         }
35228     },
35229     
35230     /**
35231      * Returns true is this panel was configured to be closable
35232      * @return {Boolean} 
35233      */
35234     isClosable : function(){
35235         return this.closable;
35236     },
35237     
35238     beforeSlide : function(){
35239         this.el.clip();
35240         this.resizeEl.clip();
35241     },
35242     
35243     afterSlide : function(){
35244         this.el.unclip();
35245         this.resizeEl.unclip();
35246     },
35247     
35248     /**
35249      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35250      *   Will fail silently if the {@link #setUrl} method has not been called.
35251      *   This does not activate the panel, just updates its content.
35252      */
35253     refresh : function(){
35254         if(this.refreshDelegate){
35255            this.loaded = false;
35256            this.refreshDelegate();
35257         }
35258     },
35259     
35260     /**
35261      * Destroys this panel
35262      */
35263     destroy : function(){
35264         this.el.removeAllListeners();
35265         var tempEl = document.createElement("span");
35266         tempEl.appendChild(this.el.dom);
35267         tempEl.innerHTML = "";
35268         this.el.remove();
35269         this.el = null;
35270     },
35271     
35272     /**
35273      * form - if the content panel contains a form - this is a reference to it.
35274      * @type {Roo.form.Form}
35275      */
35276     form : false,
35277     /**
35278      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35279      *    This contains a reference to it.
35280      * @type {Roo.View}
35281      */
35282     view : false,
35283     
35284       /**
35285      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35286      * <pre><code>
35287
35288 layout.addxtype({
35289        xtype : 'Form',
35290        items: [ .... ]
35291    }
35292 );
35293
35294 </code></pre>
35295      * @param {Object} cfg Xtype definition of item to add.
35296      */
35297     
35298     
35299     getChildContainer: function () {
35300         return this.getEl();
35301     }
35302     
35303     
35304     /*
35305         var  ret = new Roo.factory(cfg);
35306         return ret;
35307         
35308         
35309         // add form..
35310         if (cfg.xtype.match(/^Form$/)) {
35311             
35312             var el;
35313             //if (this.footer) {
35314             //    el = this.footer.container.insertSibling(false, 'before');
35315             //} else {
35316                 el = this.el.createChild();
35317             //}
35318
35319             this.form = new  Roo.form.Form(cfg);
35320             
35321             
35322             if ( this.form.allItems.length) {
35323                 this.form.render(el.dom);
35324             }
35325             return this.form;
35326         }
35327         // should only have one of theses..
35328         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35329             // views.. should not be just added - used named prop 'view''
35330             
35331             cfg.el = this.el.appendChild(document.createElement("div"));
35332             // factory?
35333             
35334             var ret = new Roo.factory(cfg);
35335              
35336              ret.render && ret.render(false, ''); // render blank..
35337             this.view = ret;
35338             return ret;
35339         }
35340         return false;
35341     }
35342     \*/
35343 });
35344  
35345 /**
35346  * @class Roo.bootstrap.panel.Grid
35347  * @extends Roo.bootstrap.panel.Content
35348  * @constructor
35349  * Create a new GridPanel.
35350  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35351  * @param {Object} config A the config object
35352   
35353  */
35354
35355
35356
35357 Roo.bootstrap.panel.Grid = function(config)
35358 {
35359     
35360       
35361     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35362         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35363
35364     config.el = this.wrapper;
35365     //this.el = this.wrapper;
35366     
35367       if (config.container) {
35368         // ctor'ed from a Border/panel.grid
35369         
35370         
35371         this.wrapper.setStyle("overflow", "hidden");
35372         this.wrapper.addClass('roo-grid-container');
35373
35374     }
35375     
35376     
35377     if(config.toolbar){
35378         var tool_el = this.wrapper.createChild();    
35379         this.toolbar = Roo.factory(config.toolbar);
35380         var ti = [];
35381         if (config.toolbar.items) {
35382             ti = config.toolbar.items ;
35383             delete config.toolbar.items ;
35384         }
35385         
35386         var nitems = [];
35387         this.toolbar.render(tool_el);
35388         for(var i =0;i < ti.length;i++) {
35389           //  Roo.log(['add child', items[i]]);
35390             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35391         }
35392         this.toolbar.items = nitems;
35393         
35394         delete config.toolbar;
35395     }
35396     
35397     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35398     config.grid.scrollBody = true;;
35399     config.grid.monitorWindowResize = false; // turn off autosizing
35400     config.grid.autoHeight = false;
35401     config.grid.autoWidth = false;
35402     
35403     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35404     
35405     if (config.background) {
35406         // render grid on panel activation (if panel background)
35407         this.on('activate', function(gp) {
35408             if (!gp.grid.rendered) {
35409                 gp.grid.render(this.wrapper);
35410                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35411             }
35412         });
35413             
35414     } else {
35415         this.grid.render(this.wrapper);
35416         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35417
35418     }
35419     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35420     // ??? needed ??? config.el = this.wrapper;
35421     
35422     
35423     
35424   
35425     // xtype created footer. - not sure if will work as we normally have to render first..
35426     if (this.footer && !this.footer.el && this.footer.xtype) {
35427         
35428         var ctr = this.grid.getView().getFooterPanel(true);
35429         this.footer.dataSource = this.grid.dataSource;
35430         this.footer = Roo.factory(this.footer, Roo);
35431         this.footer.render(ctr);
35432         
35433     }
35434     
35435     
35436     
35437     
35438      
35439 };
35440
35441 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35442     getId : function(){
35443         return this.grid.id;
35444     },
35445     
35446     /**
35447      * Returns the grid for this panel
35448      * @return {Roo.bootstrap.Table} 
35449      */
35450     getGrid : function(){
35451         return this.grid;    
35452     },
35453     
35454     setSize : function(width, height){
35455         if(!this.ignoreResize(width, height)){
35456             var grid = this.grid;
35457             var size = this.adjustForComponents(width, height);
35458             var gridel = grid.getGridEl();
35459             gridel.setSize(size.width, size.height);
35460             /*
35461             var thd = grid.getGridEl().select('thead',true).first();
35462             var tbd = grid.getGridEl().select('tbody', true).first();
35463             if (tbd) {
35464                 tbd.setSize(width, height - thd.getHeight());
35465             }
35466             */
35467             grid.autoSize();
35468         }
35469     },
35470      
35471     
35472     
35473     beforeSlide : function(){
35474         this.grid.getView().scroller.clip();
35475     },
35476     
35477     afterSlide : function(){
35478         this.grid.getView().scroller.unclip();
35479     },
35480     
35481     destroy : function(){
35482         this.grid.destroy();
35483         delete this.grid;
35484         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35485     }
35486 });
35487
35488 /**
35489  * @class Roo.bootstrap.panel.Nest
35490  * @extends Roo.bootstrap.panel.Content
35491  * @constructor
35492  * Create a new Panel, that can contain a layout.Border.
35493  * 
35494  * 
35495  * @param {Roo.BorderLayout} layout The layout for this panel
35496  * @param {String/Object} config A string to set only the title or a config object
35497  */
35498 Roo.bootstrap.panel.Nest = function(config)
35499 {
35500     // construct with only one argument..
35501     /* FIXME - implement nicer consturctors
35502     if (layout.layout) {
35503         config = layout;
35504         layout = config.layout;
35505         delete config.layout;
35506     }
35507     if (layout.xtype && !layout.getEl) {
35508         // then layout needs constructing..
35509         layout = Roo.factory(layout, Roo);
35510     }
35511     */
35512     
35513     config.el =  config.layout.getEl();
35514     
35515     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35516     
35517     config.layout.monitorWindowResize = false; // turn off autosizing
35518     this.layout = config.layout;
35519     this.layout.getEl().addClass("roo-layout-nested-layout");
35520     
35521     
35522     
35523     
35524 };
35525
35526 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35527
35528     setSize : function(width, height){
35529         if(!this.ignoreResize(width, height)){
35530             var size = this.adjustForComponents(width, height);
35531             var el = this.layout.getEl();
35532             if (size.height < 1) {
35533                 el.setWidth(size.width);   
35534             } else {
35535                 el.setSize(size.width, size.height);
35536             }
35537             var touch = el.dom.offsetWidth;
35538             this.layout.layout();
35539             // ie requires a double layout on the first pass
35540             if(Roo.isIE && !this.initialized){
35541                 this.initialized = true;
35542                 this.layout.layout();
35543             }
35544         }
35545     },
35546     
35547     // activate all subpanels if not currently active..
35548     
35549     setActiveState : function(active){
35550         this.active = active;
35551         this.setActiveClass(active);
35552         
35553         if(!active){
35554             this.fireEvent("deactivate", this);
35555             return;
35556         }
35557         
35558         this.fireEvent("activate", this);
35559         // not sure if this should happen before or after..
35560         if (!this.layout) {
35561             return; // should not happen..
35562         }
35563         var reg = false;
35564         for (var r in this.layout.regions) {
35565             reg = this.layout.getRegion(r);
35566             if (reg.getActivePanel()) {
35567                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35568                 reg.setActivePanel(reg.getActivePanel());
35569                 continue;
35570             }
35571             if (!reg.panels.length) {
35572                 continue;
35573             }
35574             reg.showPanel(reg.getPanel(0));
35575         }
35576         
35577         
35578         
35579         
35580     },
35581     
35582     /**
35583      * Returns the nested BorderLayout for this panel
35584      * @return {Roo.BorderLayout} 
35585      */
35586     getLayout : function(){
35587         return this.layout;
35588     },
35589     
35590      /**
35591      * Adds a xtype elements to the layout of the nested panel
35592      * <pre><code>
35593
35594 panel.addxtype({
35595        xtype : 'ContentPanel',
35596        region: 'west',
35597        items: [ .... ]
35598    }
35599 );
35600
35601 panel.addxtype({
35602         xtype : 'NestedLayoutPanel',
35603         region: 'west',
35604         layout: {
35605            center: { },
35606            west: { }   
35607         },
35608         items : [ ... list of content panels or nested layout panels.. ]
35609    }
35610 );
35611 </code></pre>
35612      * @param {Object} cfg Xtype definition of item to add.
35613      */
35614     addxtype : function(cfg) {
35615         return this.layout.addxtype(cfg);
35616     
35617     }
35618 });        /*
35619  * Based on:
35620  * Ext JS Library 1.1.1
35621  * Copyright(c) 2006-2007, Ext JS, LLC.
35622  *
35623  * Originally Released Under LGPL - original licence link has changed is not relivant.
35624  *
35625  * Fork - LGPL
35626  * <script type="text/javascript">
35627  */
35628 /**
35629  * @class Roo.TabPanel
35630  * @extends Roo.util.Observable
35631  * A lightweight tab container.
35632  * <br><br>
35633  * Usage:
35634  * <pre><code>
35635 // basic tabs 1, built from existing content
35636 var tabs = new Roo.TabPanel("tabs1");
35637 tabs.addTab("script", "View Script");
35638 tabs.addTab("markup", "View Markup");
35639 tabs.activate("script");
35640
35641 // more advanced tabs, built from javascript
35642 var jtabs = new Roo.TabPanel("jtabs");
35643 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35644
35645 // set up the UpdateManager
35646 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35647 var updater = tab2.getUpdateManager();
35648 updater.setDefaultUrl("ajax1.htm");
35649 tab2.on('activate', updater.refresh, updater, true);
35650
35651 // Use setUrl for Ajax loading
35652 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35653 tab3.setUrl("ajax2.htm", null, true);
35654
35655 // Disabled tab
35656 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35657 tab4.disable();
35658
35659 jtabs.activate("jtabs-1");
35660  * </code></pre>
35661  * @constructor
35662  * Create a new TabPanel.
35663  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35664  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35665  */
35666 Roo.bootstrap.panel.Tabs = function(config){
35667     /**
35668     * The container element for this TabPanel.
35669     * @type Roo.Element
35670     */
35671     this.el = Roo.get(config.el);
35672     delete config.el;
35673     if(config){
35674         if(typeof config == "boolean"){
35675             this.tabPosition = config ? "bottom" : "top";
35676         }else{
35677             Roo.apply(this, config);
35678         }
35679     }
35680     
35681     if(this.tabPosition == "bottom"){
35682         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35683         this.el.addClass("roo-tabs-bottom");
35684     }
35685     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35686     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35687     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35688     if(Roo.isIE){
35689         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35690     }
35691     if(this.tabPosition != "bottom"){
35692         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35693          * @type Roo.Element
35694          */
35695         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35696         this.el.addClass("roo-tabs-top");
35697     }
35698     this.items = [];
35699
35700     this.bodyEl.setStyle("position", "relative");
35701
35702     this.active = null;
35703     this.activateDelegate = this.activate.createDelegate(this);
35704
35705     this.addEvents({
35706         /**
35707          * @event tabchange
35708          * Fires when the active tab changes
35709          * @param {Roo.TabPanel} this
35710          * @param {Roo.TabPanelItem} activePanel The new active tab
35711          */
35712         "tabchange": true,
35713         /**
35714          * @event beforetabchange
35715          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35716          * @param {Roo.TabPanel} this
35717          * @param {Object} e Set cancel to true on this object to cancel the tab change
35718          * @param {Roo.TabPanelItem} tab The tab being changed to
35719          */
35720         "beforetabchange" : true
35721     });
35722
35723     Roo.EventManager.onWindowResize(this.onResize, this);
35724     this.cpad = this.el.getPadding("lr");
35725     this.hiddenCount = 0;
35726
35727
35728     // toolbar on the tabbar support...
35729     if (this.toolbar) {
35730         alert("no toolbar support yet");
35731         this.toolbar  = false;
35732         /*
35733         var tcfg = this.toolbar;
35734         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35735         this.toolbar = new Roo.Toolbar(tcfg);
35736         if (Roo.isSafari) {
35737             var tbl = tcfg.container.child('table', true);
35738             tbl.setAttribute('width', '100%');
35739         }
35740         */
35741         
35742     }
35743    
35744
35745
35746     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35747 };
35748
35749 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35750     /*
35751      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35752      */
35753     tabPosition : "top",
35754     /*
35755      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35756      */
35757     currentTabWidth : 0,
35758     /*
35759      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35760      */
35761     minTabWidth : 40,
35762     /*
35763      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35764      */
35765     maxTabWidth : 250,
35766     /*
35767      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35768      */
35769     preferredTabWidth : 175,
35770     /*
35771      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35772      */
35773     resizeTabs : false,
35774     /*
35775      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35776      */
35777     monitorResize : true,
35778     /*
35779      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35780      */
35781     toolbar : false,
35782
35783     /**
35784      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35785      * @param {String} id The id of the div to use <b>or create</b>
35786      * @param {String} text The text for the tab
35787      * @param {String} content (optional) Content to put in the TabPanelItem body
35788      * @param {Boolean} closable (optional) True to create a close icon on the tab
35789      * @return {Roo.TabPanelItem} The created TabPanelItem
35790      */
35791     addTab : function(id, text, content, closable, tpl)
35792     {
35793         var item = new Roo.bootstrap.panel.TabItem({
35794             panel: this,
35795             id : id,
35796             text : text,
35797             closable : closable,
35798             tpl : tpl
35799         });
35800         this.addTabItem(item);
35801         if(content){
35802             item.setContent(content);
35803         }
35804         return item;
35805     },
35806
35807     /**
35808      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35809      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35810      * @return {Roo.TabPanelItem}
35811      */
35812     getTab : function(id){
35813         return this.items[id];
35814     },
35815
35816     /**
35817      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35818      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35819      */
35820     hideTab : function(id){
35821         var t = this.items[id];
35822         if(!t.isHidden()){
35823            t.setHidden(true);
35824            this.hiddenCount++;
35825            this.autoSizeTabs();
35826         }
35827     },
35828
35829     /**
35830      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35831      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35832      */
35833     unhideTab : function(id){
35834         var t = this.items[id];
35835         if(t.isHidden()){
35836            t.setHidden(false);
35837            this.hiddenCount--;
35838            this.autoSizeTabs();
35839         }
35840     },
35841
35842     /**
35843      * Adds an existing {@link Roo.TabPanelItem}.
35844      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35845      */
35846     addTabItem : function(item){
35847         this.items[item.id] = item;
35848         this.items.push(item);
35849       //  if(this.resizeTabs){
35850     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35851   //         this.autoSizeTabs();
35852 //        }else{
35853 //            item.autoSize();
35854        // }
35855     },
35856
35857     /**
35858      * Removes a {@link Roo.TabPanelItem}.
35859      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35860      */
35861     removeTab : function(id){
35862         var items = this.items;
35863         var tab = items[id];
35864         if(!tab) { return; }
35865         var index = items.indexOf(tab);
35866         if(this.active == tab && items.length > 1){
35867             var newTab = this.getNextAvailable(index);
35868             if(newTab) {
35869                 newTab.activate();
35870             }
35871         }
35872         this.stripEl.dom.removeChild(tab.pnode.dom);
35873         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35874             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35875         }
35876         items.splice(index, 1);
35877         delete this.items[tab.id];
35878         tab.fireEvent("close", tab);
35879         tab.purgeListeners();
35880         this.autoSizeTabs();
35881     },
35882
35883     getNextAvailable : function(start){
35884         var items = this.items;
35885         var index = start;
35886         // look for a next tab that will slide over to
35887         // replace the one being removed
35888         while(index < items.length){
35889             var item = items[++index];
35890             if(item && !item.isHidden()){
35891                 return item;
35892             }
35893         }
35894         // if one isn't found select the previous tab (on the left)
35895         index = start;
35896         while(index >= 0){
35897             var item = items[--index];
35898             if(item && !item.isHidden()){
35899                 return item;
35900             }
35901         }
35902         return null;
35903     },
35904
35905     /**
35906      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35907      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35908      */
35909     disableTab : function(id){
35910         var tab = this.items[id];
35911         if(tab && this.active != tab){
35912             tab.disable();
35913         }
35914     },
35915
35916     /**
35917      * Enables a {@link Roo.TabPanelItem} that is disabled.
35918      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35919      */
35920     enableTab : function(id){
35921         var tab = this.items[id];
35922         tab.enable();
35923     },
35924
35925     /**
35926      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35927      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35928      * @return {Roo.TabPanelItem} The TabPanelItem.
35929      */
35930     activate : function(id){
35931         var tab = this.items[id];
35932         if(!tab){
35933             return null;
35934         }
35935         if(tab == this.active || tab.disabled){
35936             return tab;
35937         }
35938         var e = {};
35939         this.fireEvent("beforetabchange", this, e, tab);
35940         if(e.cancel !== true && !tab.disabled){
35941             if(this.active){
35942                 this.active.hide();
35943             }
35944             this.active = this.items[id];
35945             this.active.show();
35946             this.fireEvent("tabchange", this, this.active);
35947         }
35948         return tab;
35949     },
35950
35951     /**
35952      * Gets the active {@link Roo.TabPanelItem}.
35953      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35954      */
35955     getActiveTab : function(){
35956         return this.active;
35957     },
35958
35959     /**
35960      * Updates the tab body element to fit the height of the container element
35961      * for overflow scrolling
35962      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35963      */
35964     syncHeight : function(targetHeight){
35965         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35966         var bm = this.bodyEl.getMargins();
35967         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35968         this.bodyEl.setHeight(newHeight);
35969         return newHeight;
35970     },
35971
35972     onResize : function(){
35973         if(this.monitorResize){
35974             this.autoSizeTabs();
35975         }
35976     },
35977
35978     /**
35979      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35980      */
35981     beginUpdate : function(){
35982         this.updating = true;
35983     },
35984
35985     /**
35986      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35987      */
35988     endUpdate : function(){
35989         this.updating = false;
35990         this.autoSizeTabs();
35991     },
35992
35993     /**
35994      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35995      */
35996     autoSizeTabs : function(){
35997         var count = this.items.length;
35998         var vcount = count - this.hiddenCount;
35999         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36000             return;
36001         }
36002         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36003         var availWidth = Math.floor(w / vcount);
36004         var b = this.stripBody;
36005         if(b.getWidth() > w){
36006             var tabs = this.items;
36007             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36008             if(availWidth < this.minTabWidth){
36009                 /*if(!this.sleft){    // incomplete scrolling code
36010                     this.createScrollButtons();
36011                 }
36012                 this.showScroll();
36013                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36014             }
36015         }else{
36016             if(this.currentTabWidth < this.preferredTabWidth){
36017                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36018             }
36019         }
36020     },
36021
36022     /**
36023      * Returns the number of tabs in this TabPanel.
36024      * @return {Number}
36025      */
36026      getCount : function(){
36027          return this.items.length;
36028      },
36029
36030     /**
36031      * Resizes all the tabs to the passed width
36032      * @param {Number} The new width
36033      */
36034     setTabWidth : function(width){
36035         this.currentTabWidth = width;
36036         for(var i = 0, len = this.items.length; i < len; i++) {
36037                 if(!this.items[i].isHidden()) {
36038                 this.items[i].setWidth(width);
36039             }
36040         }
36041     },
36042
36043     /**
36044      * Destroys this TabPanel
36045      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36046      */
36047     destroy : function(removeEl){
36048         Roo.EventManager.removeResizeListener(this.onResize, this);
36049         for(var i = 0, len = this.items.length; i < len; i++){
36050             this.items[i].purgeListeners();
36051         }
36052         if(removeEl === true){
36053             this.el.update("");
36054             this.el.remove();
36055         }
36056     },
36057     
36058     createStrip : function(container)
36059     {
36060         var strip = document.createElement("nav");
36061         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36062         container.appendChild(strip);
36063         return strip;
36064     },
36065     
36066     createStripList : function(strip)
36067     {
36068         // div wrapper for retard IE
36069         // returns the "tr" element.
36070         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36071         //'<div class="x-tabs-strip-wrap">'+
36072           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36073           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36074         return strip.firstChild; //.firstChild.firstChild.firstChild;
36075     },
36076     createBody : function(container)
36077     {
36078         var body = document.createElement("div");
36079         Roo.id(body, "tab-body");
36080         //Roo.fly(body).addClass("x-tabs-body");
36081         Roo.fly(body).addClass("tab-content");
36082         container.appendChild(body);
36083         return body;
36084     },
36085     createItemBody :function(bodyEl, id){
36086         var body = Roo.getDom(id);
36087         if(!body){
36088             body = document.createElement("div");
36089             body.id = id;
36090         }
36091         //Roo.fly(body).addClass("x-tabs-item-body");
36092         Roo.fly(body).addClass("tab-pane");
36093          bodyEl.insertBefore(body, bodyEl.firstChild);
36094         return body;
36095     },
36096     /** @private */
36097     createStripElements :  function(stripEl, text, closable, tpl)
36098     {
36099         var td = document.createElement("li"); // was td..
36100         
36101         
36102         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36103         
36104         
36105         stripEl.appendChild(td);
36106         /*if(closable){
36107             td.className = "x-tabs-closable";
36108             if(!this.closeTpl){
36109                 this.closeTpl = new Roo.Template(
36110                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36111                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36112                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36113                 );
36114             }
36115             var el = this.closeTpl.overwrite(td, {"text": text});
36116             var close = el.getElementsByTagName("div")[0];
36117             var inner = el.getElementsByTagName("em")[0];
36118             return {"el": el, "close": close, "inner": inner};
36119         } else {
36120         */
36121         // not sure what this is..
36122 //            if(!this.tabTpl){
36123                 //this.tabTpl = new Roo.Template(
36124                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36125                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36126                 //);
36127 //                this.tabTpl = new Roo.Template(
36128 //                   '<a href="#">' +
36129 //                   '<span unselectable="on"' +
36130 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36131 //                            ' >{text}</span></a>'
36132 //                );
36133 //                
36134 //            }
36135
36136
36137             var template = tpl || this.tabTpl || false;
36138             
36139             if(!template){
36140                 
36141                 template = new Roo.Template(
36142                    '<a href="#">' +
36143                    '<span unselectable="on"' +
36144                             (this.disableTooltips ? '' : ' title="{text}"') +
36145                             ' >{text}</span></a>'
36146                 );
36147             }
36148             
36149             switch (typeof(template)) {
36150                 case 'object' :
36151                     break;
36152                 case 'string' :
36153                     template = new Roo.Template(template);
36154                     break;
36155                 default :
36156                     break;
36157             }
36158             
36159             var el = template.overwrite(td, {"text": text});
36160             
36161             var inner = el.getElementsByTagName("span")[0];
36162             
36163             return {"el": el, "inner": inner};
36164             
36165     }
36166         
36167     
36168 });
36169
36170 /**
36171  * @class Roo.TabPanelItem
36172  * @extends Roo.util.Observable
36173  * Represents an individual item (tab plus body) in a TabPanel.
36174  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36175  * @param {String} id The id of this TabPanelItem
36176  * @param {String} text The text for the tab of this TabPanelItem
36177  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36178  */
36179 Roo.bootstrap.panel.TabItem = function(config){
36180     /**
36181      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36182      * @type Roo.TabPanel
36183      */
36184     this.tabPanel = config.panel;
36185     /**
36186      * The id for this TabPanelItem
36187      * @type String
36188      */
36189     this.id = config.id;
36190     /** @private */
36191     this.disabled = false;
36192     /** @private */
36193     this.text = config.text;
36194     /** @private */
36195     this.loaded = false;
36196     this.closable = config.closable;
36197
36198     /**
36199      * The body element for this TabPanelItem.
36200      * @type Roo.Element
36201      */
36202     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36203     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36204     this.bodyEl.setStyle("display", "block");
36205     this.bodyEl.setStyle("zoom", "1");
36206     //this.hideAction();
36207
36208     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36209     /** @private */
36210     this.el = Roo.get(els.el);
36211     this.inner = Roo.get(els.inner, true);
36212     this.textEl = Roo.get(this.el.dom.firstChild, true);
36213     this.pnode = Roo.get(els.el.parentNode, true);
36214     this.el.on("mousedown", this.onTabMouseDown, this);
36215     this.el.on("click", this.onTabClick, this);
36216     /** @private */
36217     if(config.closable){
36218         var c = Roo.get(els.close, true);
36219         c.dom.title = this.closeText;
36220         c.addClassOnOver("close-over");
36221         c.on("click", this.closeClick, this);
36222      }
36223
36224     this.addEvents({
36225          /**
36226          * @event activate
36227          * Fires when this tab becomes the active tab.
36228          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36229          * @param {Roo.TabPanelItem} this
36230          */
36231         "activate": true,
36232         /**
36233          * @event beforeclose
36234          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36235          * @param {Roo.TabPanelItem} this
36236          * @param {Object} e Set cancel to true on this object to cancel the close.
36237          */
36238         "beforeclose": true,
36239         /**
36240          * @event close
36241          * Fires when this tab is closed.
36242          * @param {Roo.TabPanelItem} this
36243          */
36244          "close": true,
36245         /**
36246          * @event deactivate
36247          * Fires when this tab is no longer the active tab.
36248          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36249          * @param {Roo.TabPanelItem} this
36250          */
36251          "deactivate" : true
36252     });
36253     this.hidden = false;
36254
36255     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36256 };
36257
36258 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36259            {
36260     purgeListeners : function(){
36261        Roo.util.Observable.prototype.purgeListeners.call(this);
36262        this.el.removeAllListeners();
36263     },
36264     /**
36265      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36266      */
36267     show : function(){
36268         this.pnode.addClass("active");
36269         this.showAction();
36270         if(Roo.isOpera){
36271             this.tabPanel.stripWrap.repaint();
36272         }
36273         this.fireEvent("activate", this.tabPanel, this);
36274     },
36275
36276     /**
36277      * Returns true if this tab is the active tab.
36278      * @return {Boolean}
36279      */
36280     isActive : function(){
36281         return this.tabPanel.getActiveTab() == this;
36282     },
36283
36284     /**
36285      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36286      */
36287     hide : function(){
36288         this.pnode.removeClass("active");
36289         this.hideAction();
36290         this.fireEvent("deactivate", this.tabPanel, this);
36291     },
36292
36293     hideAction : function(){
36294         this.bodyEl.hide();
36295         this.bodyEl.setStyle("position", "absolute");
36296         this.bodyEl.setLeft("-20000px");
36297         this.bodyEl.setTop("-20000px");
36298     },
36299
36300     showAction : function(){
36301         this.bodyEl.setStyle("position", "relative");
36302         this.bodyEl.setTop("");
36303         this.bodyEl.setLeft("");
36304         this.bodyEl.show();
36305     },
36306
36307     /**
36308      * Set the tooltip for the tab.
36309      * @param {String} tooltip The tab's tooltip
36310      */
36311     setTooltip : function(text){
36312         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36313             this.textEl.dom.qtip = text;
36314             this.textEl.dom.removeAttribute('title');
36315         }else{
36316             this.textEl.dom.title = text;
36317         }
36318     },
36319
36320     onTabClick : function(e){
36321         e.preventDefault();
36322         this.tabPanel.activate(this.id);
36323     },
36324
36325     onTabMouseDown : function(e){
36326         e.preventDefault();
36327         this.tabPanel.activate(this.id);
36328     },
36329 /*
36330     getWidth : function(){
36331         return this.inner.getWidth();
36332     },
36333
36334     setWidth : function(width){
36335         var iwidth = width - this.pnode.getPadding("lr");
36336         this.inner.setWidth(iwidth);
36337         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36338         this.pnode.setWidth(width);
36339     },
36340 */
36341     /**
36342      * Show or hide the tab
36343      * @param {Boolean} hidden True to hide or false to show.
36344      */
36345     setHidden : function(hidden){
36346         this.hidden = hidden;
36347         this.pnode.setStyle("display", hidden ? "none" : "");
36348     },
36349
36350     /**
36351      * Returns true if this tab is "hidden"
36352      * @return {Boolean}
36353      */
36354     isHidden : function(){
36355         return this.hidden;
36356     },
36357
36358     /**
36359      * Returns the text for this tab
36360      * @return {String}
36361      */
36362     getText : function(){
36363         return this.text;
36364     },
36365     /*
36366     autoSize : function(){
36367         //this.el.beginMeasure();
36368         this.textEl.setWidth(1);
36369         /*
36370          *  #2804 [new] Tabs in Roojs
36371          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36372          */
36373         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36374         //this.el.endMeasure();
36375     //},
36376
36377     /**
36378      * Sets the text for the tab (Note: this also sets the tooltip text)
36379      * @param {String} text The tab's text and tooltip
36380      */
36381     setText : function(text){
36382         this.text = text;
36383         this.textEl.update(text);
36384         this.setTooltip(text);
36385         //if(!this.tabPanel.resizeTabs){
36386         //    this.autoSize();
36387         //}
36388     },
36389     /**
36390      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36391      */
36392     activate : function(){
36393         this.tabPanel.activate(this.id);
36394     },
36395
36396     /**
36397      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36398      */
36399     disable : function(){
36400         if(this.tabPanel.active != this){
36401             this.disabled = true;
36402             this.pnode.addClass("disabled");
36403         }
36404     },
36405
36406     /**
36407      * Enables this TabPanelItem if it was previously disabled.
36408      */
36409     enable : function(){
36410         this.disabled = false;
36411         this.pnode.removeClass("disabled");
36412     },
36413
36414     /**
36415      * Sets the content for this TabPanelItem.
36416      * @param {String} content The content
36417      * @param {Boolean} loadScripts true to look for and load scripts
36418      */
36419     setContent : function(content, loadScripts){
36420         this.bodyEl.update(content, loadScripts);
36421     },
36422
36423     /**
36424      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36425      * @return {Roo.UpdateManager} The UpdateManager
36426      */
36427     getUpdateManager : function(){
36428         return this.bodyEl.getUpdateManager();
36429     },
36430
36431     /**
36432      * Set a URL to be used to load the content for this TabPanelItem.
36433      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36434      * @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)
36435      * @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)
36436      * @return {Roo.UpdateManager} The UpdateManager
36437      */
36438     setUrl : function(url, params, loadOnce){
36439         if(this.refreshDelegate){
36440             this.un('activate', this.refreshDelegate);
36441         }
36442         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36443         this.on("activate", this.refreshDelegate);
36444         return this.bodyEl.getUpdateManager();
36445     },
36446
36447     /** @private */
36448     _handleRefresh : function(url, params, loadOnce){
36449         if(!loadOnce || !this.loaded){
36450             var updater = this.bodyEl.getUpdateManager();
36451             updater.update(url, params, this._setLoaded.createDelegate(this));
36452         }
36453     },
36454
36455     /**
36456      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36457      *   Will fail silently if the setUrl method has not been called.
36458      *   This does not activate the panel, just updates its content.
36459      */
36460     refresh : function(){
36461         if(this.refreshDelegate){
36462            this.loaded = false;
36463            this.refreshDelegate();
36464         }
36465     },
36466
36467     /** @private */
36468     _setLoaded : function(){
36469         this.loaded = true;
36470     },
36471
36472     /** @private */
36473     closeClick : function(e){
36474         var o = {};
36475         e.stopEvent();
36476         this.fireEvent("beforeclose", this, o);
36477         if(o.cancel !== true){
36478             this.tabPanel.removeTab(this.id);
36479         }
36480     },
36481     /**
36482      * The text displayed in the tooltip for the close icon.
36483      * @type String
36484      */
36485     closeText : "Close this tab"
36486 });