buildSDK/dependancy_bootstrap.txt
[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         return;
7600         
7601         if(!this.errPopover){
7602             return;
7603         }
7604         
7605         target.inputEl().focus();
7606         
7607         var oIndex = target.el.getStyle('z-index');
7608         
7609         target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7610         
7611         target.el.addClass('roo-invalid-outline');
7612         
7613         var fadeout = function(){
7614             
7615             target.inputEl().un('blur', fadeout);
7616             target.inputEl().un('keyup', fadeout);
7617             
7618             target.el.setStyle('z-index', oIndex);
7619         
7620             target.el.removeClass('roo-invalid-outline');
7621             
7622         }
7623         
7624         target.inputEl().on('blur', fadeout);
7625         target.inputEl().on('keyup', fadeout);
7626         
7627         Roo.log(target.el);
7628         
7629         Roo.log(target);
7630           
7631     },
7632     
7633     /**
7634      * Returns true if any fields in this form have changed since their original load.
7635      * @return Boolean
7636      */
7637     isDirty : function(){
7638         var dirty = false;
7639         var items = this.getItems();
7640         items.each(function(f){
7641            if(f.isDirty()){
7642                dirty = true;
7643                return false;
7644            }
7645            return true;
7646         });
7647         return dirty;
7648     },
7649      /**
7650      * Performs a predefined action (submit or load) or custom actions you define on this form.
7651      * @param {String} actionName The name of the action type
7652      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7653      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7654      * accept other config options):
7655      * <pre>
7656 Property          Type             Description
7657 ----------------  ---------------  ----------------------------------------------------------------------------------
7658 url               String           The url for the action (defaults to the form's url)
7659 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7660 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7661 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7662                                    validate the form on the client (defaults to false)
7663      * </pre>
7664      * @return {BasicForm} this
7665      */
7666     doAction : function(action, options){
7667         if(typeof action == 'string'){
7668             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7669         }
7670         if(this.fireEvent('beforeaction', this, action) !== false){
7671             this.beforeAction(action);
7672             action.run.defer(100, action);
7673         }
7674         return this;
7675     },
7676
7677     // private
7678     beforeAction : function(action){
7679         var o = action.options;
7680
7681         if(this.loadMask){
7682             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7683         }
7684         // not really supported yet.. ??
7685
7686         //if(this.waitMsgTarget === true){
7687         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7688         //}else if(this.waitMsgTarget){
7689         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7690         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7691         //}else {
7692         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7693        // }
7694
7695     },
7696
7697     // private
7698     afterAction : function(action, success){
7699         this.activeAction = null;
7700         var o = action.options;
7701
7702         //if(this.waitMsgTarget === true){
7703             this.el.unmask();
7704         //}else if(this.waitMsgTarget){
7705         //    this.waitMsgTarget.unmask();
7706         //}else{
7707         //    Roo.MessageBox.updateProgress(1);
7708         //    Roo.MessageBox.hide();
7709        // }
7710         //
7711         if(success){
7712             if(o.reset){
7713                 this.reset();
7714             }
7715             Roo.callback(o.success, o.scope, [this, action]);
7716             this.fireEvent('actioncomplete', this, action);
7717
7718         }else{
7719
7720             // failure condition..
7721             // we have a scenario where updates need confirming.
7722             // eg. if a locking scenario exists..
7723             // we look for { errors : { needs_confirm : true }} in the response.
7724             if (
7725                 (typeof(action.result) != 'undefined')  &&
7726                 (typeof(action.result.errors) != 'undefined')  &&
7727                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7728            ){
7729                 var _t = this;
7730                 Roo.log("not supported yet");
7731                  /*
7732
7733                 Roo.MessageBox.confirm(
7734                     "Change requires confirmation",
7735                     action.result.errorMsg,
7736                     function(r) {
7737                         if (r != 'yes') {
7738                             return;
7739                         }
7740                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7741                     }
7742
7743                 );
7744                 */
7745
7746
7747                 return;
7748             }
7749
7750             Roo.callback(o.failure, o.scope, [this, action]);
7751             // show an error message if no failed handler is set..
7752             if (!this.hasListener('actionfailed')) {
7753                 Roo.log("need to add dialog support");
7754                 /*
7755                 Roo.MessageBox.alert("Error",
7756                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7757                         action.result.errorMsg :
7758                         "Saving Failed, please check your entries or try again"
7759                 );
7760                 */
7761             }
7762
7763             this.fireEvent('actionfailed', this, action);
7764         }
7765
7766     },
7767     /**
7768      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7769      * @param {String} id The value to search for
7770      * @return Field
7771      */
7772     findField : function(id){
7773         var items = this.getItems();
7774         var field = items.get(id);
7775         if(!field){
7776              items.each(function(f){
7777                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7778                     field = f;
7779                     return false;
7780                 }
7781                 return true;
7782             });
7783         }
7784         return field || null;
7785     },
7786      /**
7787      * Mark fields in this form invalid in bulk.
7788      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7789      * @return {BasicForm} this
7790      */
7791     markInvalid : function(errors){
7792         if(errors instanceof Array){
7793             for(var i = 0, len = errors.length; i < len; i++){
7794                 var fieldError = errors[i];
7795                 var f = this.findField(fieldError.id);
7796                 if(f){
7797                     f.markInvalid(fieldError.msg);
7798                 }
7799             }
7800         }else{
7801             var field, id;
7802             for(id in errors){
7803                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7804                     field.markInvalid(errors[id]);
7805                 }
7806             }
7807         }
7808         //Roo.each(this.childForms || [], function (f) {
7809         //    f.markInvalid(errors);
7810         //});
7811
7812         return this;
7813     },
7814
7815     /**
7816      * Set values for fields in this form in bulk.
7817      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7818      * @return {BasicForm} this
7819      */
7820     setValues : function(values){
7821         if(values instanceof Array){ // array of objects
7822             for(var i = 0, len = values.length; i < len; i++){
7823                 var v = values[i];
7824                 var f = this.findField(v.id);
7825                 if(f){
7826                     f.setValue(v.value);
7827                     if(this.trackResetOnLoad){
7828                         f.originalValue = f.getValue();
7829                     }
7830                 }
7831             }
7832         }else{ // object hash
7833             var field, id;
7834             for(id in values){
7835                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7836
7837                     if (field.setFromData &&
7838                         field.valueField &&
7839                         field.displayField &&
7840                         // combos' with local stores can
7841                         // be queried via setValue()
7842                         // to set their value..
7843                         (field.store && !field.store.isLocal)
7844                         ) {
7845                         // it's a combo
7846                         var sd = { };
7847                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7848                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7849                         field.setFromData(sd);
7850
7851                     } else {
7852                         field.setValue(values[id]);
7853                     }
7854
7855
7856                     if(this.trackResetOnLoad){
7857                         field.originalValue = field.getValue();
7858                     }
7859                 }
7860             }
7861         }
7862
7863         //Roo.each(this.childForms || [], function (f) {
7864         //    f.setValues(values);
7865         //});
7866
7867         return this;
7868     },
7869
7870     /**
7871      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7872      * they are returned as an array.
7873      * @param {Boolean} asString
7874      * @return {Object}
7875      */
7876     getValues : function(asString){
7877         //if (this.childForms) {
7878             // copy values from the child forms
7879         //    Roo.each(this.childForms, function (f) {
7880         //        this.setValues(f.getValues());
7881         //    }, this);
7882         //}
7883
7884
7885
7886         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7887         if(asString === true){
7888             return fs;
7889         }
7890         return Roo.urlDecode(fs);
7891     },
7892
7893     /**
7894      * Returns the fields in this form as an object with key/value pairs.
7895      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7896      * @return {Object}
7897      */
7898     getFieldValues : function(with_hidden)
7899     {
7900         var items = this.getItems();
7901         var ret = {};
7902         items.each(function(f){
7903             if (!f.getName()) {
7904                 return;
7905             }
7906             var v = f.getValue();
7907             if (f.inputType =='radio') {
7908                 if (typeof(ret[f.getName()]) == 'undefined') {
7909                     ret[f.getName()] = ''; // empty..
7910                 }
7911
7912                 if (!f.el.dom.checked) {
7913                     return;
7914
7915                 }
7916                 v = f.el.dom.value;
7917
7918             }
7919
7920             // not sure if this supported any more..
7921             if ((typeof(v) == 'object') && f.getRawValue) {
7922                 v = f.getRawValue() ; // dates..
7923             }
7924             // combo boxes where name != hiddenName...
7925             if (f.name != f.getName()) {
7926                 ret[f.name] = f.getRawValue();
7927             }
7928             ret[f.getName()] = v;
7929         });
7930
7931         return ret;
7932     },
7933
7934     /**
7935      * Clears all invalid messages in this form.
7936      * @return {BasicForm} this
7937      */
7938     clearInvalid : function(){
7939         var items = this.getItems();
7940
7941         items.each(function(f){
7942            f.clearInvalid();
7943         });
7944
7945
7946
7947         return this;
7948     },
7949
7950     /**
7951      * Resets this form.
7952      * @return {BasicForm} this
7953      */
7954     reset : function(){
7955         var items = this.getItems();
7956         items.each(function(f){
7957             f.reset();
7958         });
7959
7960         Roo.each(this.childForms || [], function (f) {
7961             f.reset();
7962         });
7963
7964
7965         return this;
7966     },
7967     getItems : function()
7968     {
7969         var r=new Roo.util.MixedCollection(false, function(o){
7970             return o.id || (o.id = Roo.id());
7971         });
7972         var iter = function(el) {
7973             if (el.inputEl) {
7974                 r.add(el);
7975             }
7976             if (!el.items) {
7977                 return;
7978             }
7979             Roo.each(el.items,function(e) {
7980                 iter(e);
7981             });
7982
7983
7984         };
7985
7986         iter(this);
7987         return r;
7988
7989
7990
7991
7992     }
7993
7994 });
7995 /*
7996  * Based on:
7997  * Ext JS Library 1.1.1
7998  * Copyright(c) 2006-2007, Ext JS, LLC.
7999  *
8000  * Originally Released Under LGPL - original licence link has changed is not relivant.
8001  *
8002  * Fork - LGPL
8003  * <script type="text/javascript">
8004  */
8005 /**
8006  * @class Roo.form.VTypes
8007  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8008  * @singleton
8009  */
8010 Roo.form.VTypes = function(){
8011     // closure these in so they are only created once.
8012     var alpha = /^[a-zA-Z_]+$/;
8013     var alphanum = /^[a-zA-Z0-9_]+$/;
8014     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8015     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8016
8017     // All these messages and functions are configurable
8018     return {
8019         /**
8020          * The function used to validate email addresses
8021          * @param {String} value The email address
8022          */
8023         'email' : function(v){
8024             return email.test(v);
8025         },
8026         /**
8027          * The error text to display when the email validation function returns false
8028          * @type String
8029          */
8030         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8031         /**
8032          * The keystroke filter mask to be applied on email input
8033          * @type RegExp
8034          */
8035         'emailMask' : /[a-z0-9_\.\-@]/i,
8036
8037         /**
8038          * The function used to validate URLs
8039          * @param {String} value The URL
8040          */
8041         'url' : function(v){
8042             return url.test(v);
8043         },
8044         /**
8045          * The error text to display when the url validation function returns false
8046          * @type String
8047          */
8048         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8049         
8050         /**
8051          * The function used to validate alpha values
8052          * @param {String} value The value
8053          */
8054         'alpha' : function(v){
8055             return alpha.test(v);
8056         },
8057         /**
8058          * The error text to display when the alpha validation function returns false
8059          * @type String
8060          */
8061         'alphaText' : 'This field should only contain letters and _',
8062         /**
8063          * The keystroke filter mask to be applied on alpha input
8064          * @type RegExp
8065          */
8066         'alphaMask' : /[a-z_]/i,
8067
8068         /**
8069          * The function used to validate alphanumeric values
8070          * @param {String} value The value
8071          */
8072         'alphanum' : function(v){
8073             return alphanum.test(v);
8074         },
8075         /**
8076          * The error text to display when the alphanumeric validation function returns false
8077          * @type String
8078          */
8079         'alphanumText' : 'This field should only contain letters, numbers and _',
8080         /**
8081          * The keystroke filter mask to be applied on alphanumeric input
8082          * @type RegExp
8083          */
8084         'alphanumMask' : /[a-z0-9_]/i
8085     };
8086 }();/*
8087  * - LGPL
8088  *
8089  * Input
8090  * 
8091  */
8092
8093 /**
8094  * @class Roo.bootstrap.Input
8095  * @extends Roo.bootstrap.Component
8096  * Bootstrap Input class
8097  * @cfg {Boolean} disabled is it disabled
8098  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8099  * @cfg {String} name name of the input
8100  * @cfg {string} fieldLabel - the label associated
8101  * @cfg {string} placeholder - placeholder to put in text.
8102  * @cfg {string}  before - input group add on before
8103  * @cfg {string} after - input group add on after
8104  * @cfg {string} size - (lg|sm) or leave empty..
8105  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8106  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8107  * @cfg {Number} md colspan out of 12 for computer-sized screens
8108  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8109  * @cfg {string} value default value of the input
8110  * @cfg {Number} labelWidth set the width of label (0-12)
8111  * @cfg {String} labelAlign (top|left)
8112  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8113  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8114  * @cfg {String} indicatorpos (left|right) default left
8115
8116  * @cfg {String} align (left|center|right) Default left
8117  * @cfg {Boolean} forceFeedback (true|false) Default false
8118  * 
8119  * 
8120  * 
8121  * 
8122  * @constructor
8123  * Create a new Input
8124  * @param {Object} config The config object
8125  */
8126
8127 Roo.bootstrap.Input = function(config){
8128     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8129    
8130         this.addEvents({
8131             /**
8132              * @event focus
8133              * Fires when this field receives input focus.
8134              * @param {Roo.form.Field} this
8135              */
8136             focus : true,
8137             /**
8138              * @event blur
8139              * Fires when this field loses input focus.
8140              * @param {Roo.form.Field} this
8141              */
8142             blur : true,
8143             /**
8144              * @event specialkey
8145              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8146              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8147              * @param {Roo.form.Field} this
8148              * @param {Roo.EventObject} e The event object
8149              */
8150             specialkey : true,
8151             /**
8152              * @event change
8153              * Fires just before the field blurs if the field value has changed.
8154              * @param {Roo.form.Field} this
8155              * @param {Mixed} newValue The new value
8156              * @param {Mixed} oldValue The original value
8157              */
8158             change : true,
8159             /**
8160              * @event invalid
8161              * Fires after the field has been marked as invalid.
8162              * @param {Roo.form.Field} this
8163              * @param {String} msg The validation message
8164              */
8165             invalid : true,
8166             /**
8167              * @event valid
8168              * Fires after the field has been validated with no errors.
8169              * @param {Roo.form.Field} this
8170              */
8171             valid : true,
8172              /**
8173              * @event keyup
8174              * Fires after the key up
8175              * @param {Roo.form.Field} this
8176              * @param {Roo.EventObject}  e The event Object
8177              */
8178             keyup : true
8179         });
8180 };
8181
8182 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8183      /**
8184      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8185       automatic validation (defaults to "keyup").
8186      */
8187     validationEvent : "keyup",
8188      /**
8189      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8190      */
8191     validateOnBlur : true,
8192     /**
8193      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8194      */
8195     validationDelay : 250,
8196      /**
8197      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8198      */
8199     focusClass : "x-form-focus",  // not needed???
8200     
8201        
8202     /**
8203      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8204      */
8205     invalidClass : "has-warning",
8206     
8207     /**
8208      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8209      */
8210     validClass : "has-success",
8211     
8212     /**
8213      * @cfg {Boolean} hasFeedback (true|false) default true
8214      */
8215     hasFeedback : true,
8216     
8217     /**
8218      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8219      */
8220     invalidFeedbackClass : "glyphicon-warning-sign",
8221     
8222     /**
8223      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8224      */
8225     validFeedbackClass : "glyphicon-ok",
8226     
8227     /**
8228      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8229      */
8230     selectOnFocus : false,
8231     
8232      /**
8233      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8234      */
8235     maskRe : null,
8236        /**
8237      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8238      */
8239     vtype : null,
8240     
8241       /**
8242      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8243      */
8244     disableKeyFilter : false,
8245     
8246        /**
8247      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8248      */
8249     disabled : false,
8250      /**
8251      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8252      */
8253     allowBlank : true,
8254     /**
8255      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8256      */
8257     blankText : "This field is required",
8258     
8259      /**
8260      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8261      */
8262     minLength : 0,
8263     /**
8264      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8265      */
8266     maxLength : Number.MAX_VALUE,
8267     /**
8268      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8269      */
8270     minLengthText : "The minimum length for this field is {0}",
8271     /**
8272      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8273      */
8274     maxLengthText : "The maximum length for this field is {0}",
8275   
8276     
8277     /**
8278      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8279      * If available, this function will be called only after the basic validators all return true, and will be passed the
8280      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8281      */
8282     validator : null,
8283     /**
8284      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8285      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8286      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8287      */
8288     regex : null,
8289     /**
8290      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8291      */
8292     regexText : "",
8293     
8294     autocomplete: false,
8295     
8296     
8297     fieldLabel : '',
8298     inputType : 'text',
8299     
8300     name : false,
8301     placeholder: false,
8302     before : false,
8303     after : false,
8304     size : false,
8305     hasFocus : false,
8306     preventMark: false,
8307     isFormField : true,
8308     value : '',
8309     labelWidth : 2,
8310     labelAlign : false,
8311     readOnly : false,
8312     align : false,
8313     formatedValue : false,
8314     forceFeedback : false,
8315     
8316     indicatorpos : 'left',
8317     
8318     parentLabelAlign : function()
8319     {
8320         var parent = this;
8321         while (parent.parent()) {
8322             parent = parent.parent();
8323             if (typeof(parent.labelAlign) !='undefined') {
8324                 return parent.labelAlign;
8325             }
8326         }
8327         return 'left';
8328         
8329     },
8330     
8331     getAutoCreate : function()
8332     {
8333         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8334         
8335         var id = Roo.id();
8336         
8337         var cfg = {};
8338         
8339         if(this.inputType != 'hidden'){
8340             cfg.cls = 'form-group' //input-group
8341         }
8342         
8343         var input =  {
8344             tag: 'input',
8345             id : id,
8346             type : this.inputType,
8347             value : this.value,
8348             cls : 'form-control',
8349             placeholder : this.placeholder || '',
8350             autocomplete : this.autocomplete || 'new-password'
8351         };
8352         
8353         if(this.align){
8354             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8355         }
8356         
8357         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8358             input.maxLength = this.maxLength;
8359         }
8360         
8361         if (this.disabled) {
8362             input.disabled=true;
8363         }
8364         
8365         if (this.readOnly) {
8366             input.readonly=true;
8367         }
8368         
8369         if (this.name) {
8370             input.name = this.name;
8371         }
8372         
8373         if (this.size) {
8374             input.cls += ' input-' + this.size;
8375         }
8376         
8377         var settings=this;
8378         ['xs','sm','md','lg'].map(function(size){
8379             if (settings[size]) {
8380                 cfg.cls += ' col-' + size + '-' + settings[size];
8381             }
8382         });
8383         
8384         var inputblock = input;
8385         
8386         var feedback = {
8387             tag: 'span',
8388             cls: 'glyphicon form-control-feedback'
8389         };
8390             
8391         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8392             
8393             inputblock = {
8394                 cls : 'has-feedback',
8395                 cn :  [
8396                     input,
8397                     feedback
8398                 ] 
8399             };  
8400         }
8401         
8402         if (this.before || this.after) {
8403             
8404             inputblock = {
8405                 cls : 'input-group',
8406                 cn :  [] 
8407             };
8408             
8409             if (this.before && typeof(this.before) == 'string') {
8410                 
8411                 inputblock.cn.push({
8412                     tag :'span',
8413                     cls : 'roo-input-before input-group-addon',
8414                     html : this.before
8415                 });
8416             }
8417             if (this.before && typeof(this.before) == 'object') {
8418                 this.before = Roo.factory(this.before);
8419                 
8420                 inputblock.cn.push({
8421                     tag :'span',
8422                     cls : 'roo-input-before input-group-' +
8423                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8424                 });
8425             }
8426             
8427             inputblock.cn.push(input);
8428             
8429             if (this.after && typeof(this.after) == 'string') {
8430                 inputblock.cn.push({
8431                     tag :'span',
8432                     cls : 'roo-input-after input-group-addon',
8433                     html : this.after
8434                 });
8435             }
8436             if (this.after && typeof(this.after) == 'object') {
8437                 this.after = Roo.factory(this.after);
8438                 
8439                 inputblock.cn.push({
8440                     tag :'span',
8441                     cls : 'roo-input-after input-group-' +
8442                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8443                 });
8444             }
8445             
8446             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8447                 inputblock.cls += ' has-feedback';
8448                 inputblock.cn.push(feedback);
8449             }
8450         };
8451         
8452         if (align ==='left' && this.fieldLabel.length) {
8453             
8454             cfg.cn = [
8455                 {
8456                     tag : 'i',
8457                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8458                     tooltip : 'This field is required'
8459                 },
8460                 {
8461                     tag: 'label',
8462                     'for' :  id,
8463                     cls : 'control-label col-sm-' + this.labelWidth,
8464                     html : this.fieldLabel
8465
8466                 },
8467                 {
8468                     cls : "col-sm-" + (12 - this.labelWidth), 
8469                     cn: [
8470                         inputblock
8471                     ]
8472                 }
8473
8474             ];
8475             
8476             if(this.indicatorpos == 'right'){
8477                 cfg.cn = [
8478                     {
8479                         tag: 'label',
8480                         'for' :  id,
8481                         cls : 'control-label col-sm-' + this.labelWidth,
8482                         html : this.fieldLabel
8483
8484                     },
8485                     {
8486                         tag : 'i',
8487                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8488                         tooltip : 'This field is required'
8489                     },
8490                     {
8491                         cls : "col-sm-" + (12 - this.labelWidth), 
8492                         cn: [
8493                             inputblock
8494                         ]
8495                     }
8496
8497                 ];
8498             }
8499             
8500         } else if ( this.fieldLabel.length) {
8501                 
8502             cfg.cn = [
8503                 {
8504                     tag : 'i',
8505                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8506                     tooltip : 'This field is required'
8507                 },
8508                 {
8509                     tag: 'label',
8510                    //cls : 'input-group-addon',
8511                     html : this.fieldLabel
8512
8513                 },
8514
8515                inputblock
8516
8517            ];
8518            
8519            if(this.indicatorpos == 'right'){
8520                 
8521                 cfg.cn = [
8522                     {
8523                         tag: 'label',
8524                        //cls : 'input-group-addon',
8525                         html : this.fieldLabel
8526
8527                     },
8528                     {
8529                         tag : 'i',
8530                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8531                         tooltip : 'This field is required'
8532                     },
8533
8534                    inputblock
8535
8536                ];
8537
8538             }
8539
8540         } else {
8541             
8542             cfg.cn = [
8543
8544                     inputblock
8545
8546             ];
8547                 
8548                 
8549         };
8550         
8551         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8552            cfg.cls += ' navbar-form';
8553         }
8554         
8555         if (this.parentType === 'NavGroup') {
8556            cfg.cls += ' navbar-form';
8557            cfg.tag = 'li';
8558         }
8559         
8560         return cfg;
8561         
8562     },
8563     /**
8564      * return the real input element.
8565      */
8566     inputEl: function ()
8567     {
8568         return this.el.select('input.form-control',true).first();
8569     },
8570     
8571     tooltipEl : function()
8572     {
8573         return this.inputEl();
8574     },
8575     
8576     indicatorEl : function()
8577     {
8578         var indicator = this.el.select('i.roo-required-indicator',true).first();
8579         
8580         if(!indicator){
8581             return false;
8582         }
8583         
8584         return indicator;
8585         
8586     },
8587     
8588     setDisabled : function(v)
8589     {
8590         var i  = this.inputEl().dom;
8591         if (!v) {
8592             i.removeAttribute('disabled');
8593             return;
8594             
8595         }
8596         i.setAttribute('disabled','true');
8597     },
8598     initEvents : function()
8599     {
8600           
8601         this.inputEl().on("keydown" , this.fireKey,  this);
8602         this.inputEl().on("focus", this.onFocus,  this);
8603         this.inputEl().on("blur", this.onBlur,  this);
8604         
8605         this.inputEl().relayEvent('keyup', this);
8606         
8607         this.indicator = this.indicatorEl();
8608         
8609         if(this.indicator){
8610             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8611             this.indicator.hide();
8612         }
8613  
8614         // reference to original value for reset
8615         this.originalValue = this.getValue();
8616         //Roo.form.TextField.superclass.initEvents.call(this);
8617         if(this.validationEvent == 'keyup'){
8618             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8619             this.inputEl().on('keyup', this.filterValidation, this);
8620         }
8621         else if(this.validationEvent !== false){
8622             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8623         }
8624         
8625         if(this.selectOnFocus){
8626             this.on("focus", this.preFocus, this);
8627             
8628         }
8629         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8630             this.inputEl().on("keypress", this.filterKeys, this);
8631         } else {
8632             this.inputEl().relayEvent('keypress', this);
8633         }
8634        /* if(this.grow){
8635             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8636             this.el.on("click", this.autoSize,  this);
8637         }
8638         */
8639         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8640             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8641         }
8642         
8643         if (typeof(this.before) == 'object') {
8644             this.before.render(this.el.select('.roo-input-before',true).first());
8645         }
8646         if (typeof(this.after) == 'object') {
8647             this.after.render(this.el.select('.roo-input-after',true).first());
8648         }
8649         
8650         
8651     },
8652     filterValidation : function(e){
8653         if(!e.isNavKeyPress()){
8654             this.validationTask.delay(this.validationDelay);
8655         }
8656     },
8657      /**
8658      * Validates the field value
8659      * @return {Boolean} True if the value is valid, else false
8660      */
8661     validate : function(){
8662         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8663         if(this.disabled || this.validateValue(this.getRawValue())){
8664             this.markValid();
8665             return true;
8666         }
8667         
8668         this.markInvalid();
8669         return false;
8670     },
8671     
8672     
8673     /**
8674      * Validates a value according to the field's validation rules and marks the field as invalid
8675      * if the validation fails
8676      * @param {Mixed} value The value to validate
8677      * @return {Boolean} True if the value is valid, else false
8678      */
8679     validateValue : function(value){
8680         if(value.length < 1)  { // if it's blank
8681             if(this.allowBlank){
8682                 return true;
8683             }
8684             return false;
8685         }
8686         
8687         if(value.length < this.minLength){
8688             return false;
8689         }
8690         if(value.length > this.maxLength){
8691             return false;
8692         }
8693         if(this.vtype){
8694             var vt = Roo.form.VTypes;
8695             if(!vt[this.vtype](value, this)){
8696                 return false;
8697             }
8698         }
8699         if(typeof this.validator == "function"){
8700             var msg = this.validator(value);
8701             if(msg !== true){
8702                 return false;
8703             }
8704         }
8705         
8706         if(this.regex && !this.regex.test(value)){
8707             return false;
8708         }
8709         
8710         return true;
8711     },
8712
8713     
8714     
8715      // private
8716     fireKey : function(e){
8717         //Roo.log('field ' + e.getKey());
8718         if(e.isNavKeyPress()){
8719             this.fireEvent("specialkey", this, e);
8720         }
8721     },
8722     focus : function (selectText){
8723         if(this.rendered){
8724             this.inputEl().focus();
8725             if(selectText === true){
8726                 this.inputEl().dom.select();
8727             }
8728         }
8729         return this;
8730     } ,
8731     
8732     onFocus : function(){
8733         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8734            // this.el.addClass(this.focusClass);
8735         }
8736         if(!this.hasFocus){
8737             this.hasFocus = true;
8738             this.startValue = this.getValue();
8739             this.fireEvent("focus", this);
8740         }
8741     },
8742     
8743     beforeBlur : Roo.emptyFn,
8744
8745     
8746     // private
8747     onBlur : function(){
8748         this.beforeBlur();
8749         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8750             //this.el.removeClass(this.focusClass);
8751         }
8752         this.hasFocus = false;
8753         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8754             this.validate();
8755         }
8756         var v = this.getValue();
8757         if(String(v) !== String(this.startValue)){
8758             this.fireEvent('change', this, v, this.startValue);
8759         }
8760         this.fireEvent("blur", this);
8761     },
8762     
8763     /**
8764      * Resets the current field value to the originally loaded value and clears any validation messages
8765      */
8766     reset : function(){
8767         this.setValue(this.originalValue);
8768         this.validate();
8769     },
8770      /**
8771      * Returns the name of the field
8772      * @return {Mixed} name The name field
8773      */
8774     getName: function(){
8775         return this.name;
8776     },
8777      /**
8778      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8779      * @return {Mixed} value The field value
8780      */
8781     getValue : function(){
8782         
8783         var v = this.inputEl().getValue();
8784         
8785         return v;
8786     },
8787     /**
8788      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8789      * @return {Mixed} value The field value
8790      */
8791     getRawValue : function(){
8792         var v = this.inputEl().getValue();
8793         
8794         return v;
8795     },
8796     
8797     /**
8798      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8799      * @param {Mixed} value The value to set
8800      */
8801     setRawValue : function(v){
8802         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8803     },
8804     
8805     selectText : function(start, end){
8806         var v = this.getRawValue();
8807         if(v.length > 0){
8808             start = start === undefined ? 0 : start;
8809             end = end === undefined ? v.length : end;
8810             var d = this.inputEl().dom;
8811             if(d.setSelectionRange){
8812                 d.setSelectionRange(start, end);
8813             }else if(d.createTextRange){
8814                 var range = d.createTextRange();
8815                 range.moveStart("character", start);
8816                 range.moveEnd("character", v.length-end);
8817                 range.select();
8818             }
8819         }
8820     },
8821     
8822     /**
8823      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8824      * @param {Mixed} value The value to set
8825      */
8826     setValue : function(v){
8827         this.value = v;
8828         if(this.rendered){
8829             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8830             this.validate();
8831         }
8832     },
8833     
8834     /*
8835     processValue : function(value){
8836         if(this.stripCharsRe){
8837             var newValue = value.replace(this.stripCharsRe, '');
8838             if(newValue !== value){
8839                 this.setRawValue(newValue);
8840                 return newValue;
8841             }
8842         }
8843         return value;
8844     },
8845   */
8846     preFocus : function(){
8847         
8848         if(this.selectOnFocus){
8849             this.inputEl().dom.select();
8850         }
8851     },
8852     filterKeys : function(e){
8853         var k = e.getKey();
8854         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8855             return;
8856         }
8857         var c = e.getCharCode(), cc = String.fromCharCode(c);
8858         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8859             return;
8860         }
8861         if(!this.maskRe.test(cc)){
8862             e.stopEvent();
8863         }
8864     },
8865      /**
8866      * Clear any invalid styles/messages for this field
8867      */
8868     clearInvalid : function(){
8869         
8870         if(!this.el || this.preventMark){ // not rendered
8871             return;
8872         }
8873         
8874         if(this.indicator){
8875             this.indicator.hide();
8876         }
8877         
8878         this.el.removeClass(this.invalidClass);
8879         
8880         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8881             
8882             var feedback = this.el.select('.form-control-feedback', true).first();
8883             
8884             if(feedback){
8885                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8886             }
8887             
8888         }
8889         
8890         this.fireEvent('valid', this);
8891     },
8892     
8893      /**
8894      * Mark this field as valid
8895      */
8896     markValid : function()
8897     {
8898         if(!this.el  || this.preventMark){ // not rendered
8899             return;
8900         }
8901         
8902         this.el.removeClass([this.invalidClass, this.validClass]);
8903         
8904         var feedback = this.el.select('.form-control-feedback', true).first();
8905             
8906         if(feedback){
8907             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8908         }
8909
8910         if(this.disabled){
8911             return;
8912         }
8913         
8914         if(this.allowBlank && !this.getRawValue().length){
8915             return;
8916         }
8917         
8918         if(this.indicator){
8919             this.indicator.hide();
8920         }
8921         
8922         this.el.addClass(this.validClass);
8923         
8924         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8925             
8926             var feedback = this.el.select('.form-control-feedback', true).first();
8927             
8928             if(feedback){
8929                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8930                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8931             }
8932             
8933         }
8934         
8935         this.fireEvent('valid', this);
8936     },
8937     
8938      /**
8939      * Mark this field as invalid
8940      * @param {String} msg The validation message
8941      */
8942     markInvalid : function(msg)
8943     {
8944         if(!this.el  || this.preventMark){ // not rendered
8945             return;
8946         }
8947         
8948         this.el.removeClass([this.invalidClass, this.validClass]);
8949         
8950         var feedback = this.el.select('.form-control-feedback', true).first();
8951             
8952         if(feedback){
8953             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8954         }
8955
8956         if(this.disabled){
8957             return;
8958         }
8959         
8960         if(this.allowBlank && !this.getRawValue().length){
8961             return;
8962         }
8963         
8964         if(this.indicator){
8965             this.indicator.show();
8966         }
8967         
8968         this.el.addClass(this.invalidClass);
8969         
8970         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8971             
8972             var feedback = this.el.select('.form-control-feedback', true).first();
8973             
8974             if(feedback){
8975                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8976                 
8977                 if(this.getValue().length || this.forceFeedback){
8978                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8979                 }
8980                 
8981             }
8982             
8983         }
8984         
8985         this.fireEvent('invalid', this, msg);
8986     },
8987     // private
8988     SafariOnKeyDown : function(event)
8989     {
8990         // this is a workaround for a password hang bug on chrome/ webkit.
8991         if (this.inputEl().dom.type != 'password') {
8992             return;
8993         }
8994         
8995         var isSelectAll = false;
8996         
8997         if(this.inputEl().dom.selectionEnd > 0){
8998             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8999         }
9000         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9001             event.preventDefault();
9002             this.setValue('');
9003             return;
9004         }
9005         
9006         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9007             
9008             event.preventDefault();
9009             // this is very hacky as keydown always get's upper case.
9010             //
9011             var cc = String.fromCharCode(event.getCharCode());
9012             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9013             
9014         }
9015     },
9016     adjustWidth : function(tag, w){
9017         tag = tag.toLowerCase();
9018         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9019             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9020                 if(tag == 'input'){
9021                     return w + 2;
9022                 }
9023                 if(tag == 'textarea'){
9024                     return w-2;
9025                 }
9026             }else if(Roo.isOpera){
9027                 if(tag == 'input'){
9028                     return w + 2;
9029                 }
9030                 if(tag == 'textarea'){
9031                     return w-2;
9032                 }
9033             }
9034         }
9035         return w;
9036     }
9037     
9038 });
9039
9040  
9041 /*
9042  * - LGPL
9043  *
9044  * Input
9045  * 
9046  */
9047
9048 /**
9049  * @class Roo.bootstrap.TextArea
9050  * @extends Roo.bootstrap.Input
9051  * Bootstrap TextArea class
9052  * @cfg {Number} cols Specifies the visible width of a text area
9053  * @cfg {Number} rows Specifies the visible number of lines in a text area
9054  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9055  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9056  * @cfg {string} html text
9057  * 
9058  * @constructor
9059  * Create a new TextArea
9060  * @param {Object} config The config object
9061  */
9062
9063 Roo.bootstrap.TextArea = function(config){
9064     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9065    
9066 };
9067
9068 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9069      
9070     cols : false,
9071     rows : 5,
9072     readOnly : false,
9073     warp : 'soft',
9074     resize : false,
9075     value: false,
9076     html: false,
9077     
9078     getAutoCreate : function(){
9079         
9080         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9081         
9082         var id = Roo.id();
9083         
9084         var cfg = {};
9085         
9086         var input =  {
9087             tag: 'textarea',
9088             id : id,
9089             warp : this.warp,
9090             rows : this.rows,
9091             value : this.value || '',
9092             html: this.html || '',
9093             cls : 'form-control',
9094             placeholder : this.placeholder || '' 
9095             
9096         };
9097         
9098         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9099             input.maxLength = this.maxLength;
9100         }
9101         
9102         if(this.resize){
9103             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9104         }
9105         
9106         if(this.cols){
9107             input.cols = this.cols;
9108         }
9109         
9110         if (this.readOnly) {
9111             input.readonly = true;
9112         }
9113         
9114         if (this.name) {
9115             input.name = this.name;
9116         }
9117         
9118         if (this.size) {
9119             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9120         }
9121         
9122         var settings=this;
9123         ['xs','sm','md','lg'].map(function(size){
9124             if (settings[size]) {
9125                 cfg.cls += ' col-' + size + '-' + settings[size];
9126             }
9127         });
9128         
9129         var inputblock = input;
9130         
9131         if(this.hasFeedback && !this.allowBlank){
9132             
9133             var feedback = {
9134                 tag: 'span',
9135                 cls: 'glyphicon form-control-feedback'
9136             };
9137
9138             inputblock = {
9139                 cls : 'has-feedback',
9140                 cn :  [
9141                     input,
9142                     feedback
9143                 ] 
9144             };  
9145         }
9146         
9147         
9148         if (this.before || this.after) {
9149             
9150             inputblock = {
9151                 cls : 'input-group',
9152                 cn :  [] 
9153             };
9154             if (this.before) {
9155                 inputblock.cn.push({
9156                     tag :'span',
9157                     cls : 'input-group-addon',
9158                     html : this.before
9159                 });
9160             }
9161             
9162             inputblock.cn.push(input);
9163             
9164             if(this.hasFeedback && !this.allowBlank){
9165                 inputblock.cls += ' has-feedback';
9166                 inputblock.cn.push(feedback);
9167             }
9168             
9169             if (this.after) {
9170                 inputblock.cn.push({
9171                     tag :'span',
9172                     cls : 'input-group-addon',
9173                     html : this.after
9174                 });
9175             }
9176             
9177         }
9178         
9179         if (align ==='left' && this.fieldLabel.length) {
9180 //                Roo.log("left and has label");
9181                 cfg.cn = [
9182                     
9183                     {
9184                         tag: 'label',
9185                         'for' :  id,
9186                         cls : 'control-label col-sm-' + this.labelWidth,
9187                         html : this.fieldLabel
9188                         
9189                     },
9190                     {
9191                         cls : "col-sm-" + (12 - this.labelWidth), 
9192                         cn: [
9193                             inputblock
9194                         ]
9195                     }
9196                     
9197                 ];
9198         } else if ( this.fieldLabel.length) {
9199 //                Roo.log(" label");
9200                  cfg.cn = [
9201                    
9202                     {
9203                         tag: 'label',
9204                         //cls : 'input-group-addon',
9205                         html : this.fieldLabel
9206                         
9207                     },
9208                     
9209                     inputblock
9210                     
9211                 ];
9212
9213         } else {
9214             
9215 //                   Roo.log(" no label && no align");
9216                 cfg.cn = [
9217                     
9218                         inputblock
9219                     
9220                 ];
9221                 
9222                 
9223         }
9224         
9225         if (this.disabled) {
9226             input.disabled=true;
9227         }
9228         
9229         return cfg;
9230         
9231     },
9232     /**
9233      * return the real textarea element.
9234      */
9235     inputEl: function ()
9236     {
9237         return this.el.select('textarea.form-control',true).first();
9238     },
9239     
9240     /**
9241      * Clear any invalid styles/messages for this field
9242      */
9243     clearInvalid : function()
9244     {
9245         
9246         if(!this.el || this.preventMark){ // not rendered
9247             return;
9248         }
9249         
9250         var label = this.el.select('label', true).first();
9251         var icon = this.el.select('i.fa-star', true).first();
9252         
9253         if(label && icon){
9254             icon.remove();
9255         }
9256         
9257         this.el.removeClass(this.invalidClass);
9258         
9259         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9260             
9261             var feedback = this.el.select('.form-control-feedback', true).first();
9262             
9263             if(feedback){
9264                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9265             }
9266             
9267         }
9268         
9269         this.fireEvent('valid', this);
9270     },
9271     
9272      /**
9273      * Mark this field as valid
9274      */
9275     markValid : function()
9276     {
9277         if(!this.el  || this.preventMark){ // not rendered
9278             return;
9279         }
9280         
9281         this.el.removeClass([this.invalidClass, this.validClass]);
9282         
9283         var feedback = this.el.select('.form-control-feedback', true).first();
9284             
9285         if(feedback){
9286             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9287         }
9288
9289         if(this.disabled || this.allowBlank){
9290             return;
9291         }
9292         
9293         var label = this.el.select('label', true).first();
9294         var icon = this.el.select('i.fa-star', true).first();
9295         
9296         if(label && icon){
9297             icon.remove();
9298         }
9299         
9300         this.el.addClass(this.validClass);
9301         
9302         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9303             
9304             var feedback = this.el.select('.form-control-feedback', true).first();
9305             
9306             if(feedback){
9307                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9308                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9309             }
9310             
9311         }
9312         
9313         this.fireEvent('valid', this);
9314     },
9315     
9316      /**
9317      * Mark this field as invalid
9318      * @param {String} msg The validation message
9319      */
9320     markInvalid : function(msg)
9321     {
9322         if(!this.el  || this.preventMark){ // not rendered
9323             return;
9324         }
9325         
9326         this.el.removeClass([this.invalidClass, this.validClass]);
9327         
9328         var feedback = this.el.select('.form-control-feedback', true).first();
9329             
9330         if(feedback){
9331             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9332         }
9333
9334         if(this.disabled || this.allowBlank){
9335             return;
9336         }
9337         
9338         var label = this.el.select('label', true).first();
9339         var icon = this.el.select('i.fa-star', true).first();
9340         
9341         if(!this.getValue().length && label && !icon){
9342             this.el.createChild({
9343                 tag : 'i',
9344                 cls : 'text-danger fa fa-lg fa-star',
9345                 tooltip : 'This field is required',
9346                 style : 'margin-right:5px;'
9347             }, label, true);
9348         }
9349
9350         this.el.addClass(this.invalidClass);
9351         
9352         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9353             
9354             var feedback = this.el.select('.form-control-feedback', true).first();
9355             
9356             if(feedback){
9357                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9358                 
9359                 if(this.getValue().length || this.forceFeedback){
9360                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9361                 }
9362                 
9363             }
9364             
9365         }
9366         
9367         this.fireEvent('invalid', this, msg);
9368     }
9369 });
9370
9371  
9372 /*
9373  * - LGPL
9374  *
9375  * trigger field - base class for combo..
9376  * 
9377  */
9378  
9379 /**
9380  * @class Roo.bootstrap.TriggerField
9381  * @extends Roo.bootstrap.Input
9382  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9383  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9384  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9385  * for which you can provide a custom implementation.  For example:
9386  * <pre><code>
9387 var trigger = new Roo.bootstrap.TriggerField();
9388 trigger.onTriggerClick = myTriggerFn;
9389 trigger.applyTo('my-field');
9390 </code></pre>
9391  *
9392  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9393  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9394  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9395  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9396  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9397
9398  * @constructor
9399  * Create a new TriggerField.
9400  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9401  * to the base TextField)
9402  */
9403 Roo.bootstrap.TriggerField = function(config){
9404     this.mimicing = false;
9405     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9406 };
9407
9408 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9409     /**
9410      * @cfg {String} triggerClass A CSS class to apply to the trigger
9411      */
9412      /**
9413      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9414      */
9415     hideTrigger:false,
9416
9417     /**
9418      * @cfg {Boolean} removable (true|false) special filter default false
9419      */
9420     removable : false,
9421     
9422     /** @cfg {Boolean} grow @hide */
9423     /** @cfg {Number} growMin @hide */
9424     /** @cfg {Number} growMax @hide */
9425
9426     /**
9427      * @hide 
9428      * @method
9429      */
9430     autoSize: Roo.emptyFn,
9431     // private
9432     monitorTab : true,
9433     // private
9434     deferHeight : true,
9435
9436     
9437     actionMode : 'wrap',
9438     
9439     caret : false,
9440     
9441     
9442     getAutoCreate : function(){
9443        
9444         var align = this.labelAlign || this.parentLabelAlign();
9445         
9446         var id = Roo.id();
9447         
9448         var cfg = {
9449             cls: 'form-group' //input-group
9450         };
9451         
9452         
9453         var input =  {
9454             tag: 'input',
9455             id : id,
9456             type : this.inputType,
9457             cls : 'form-control',
9458             autocomplete: 'new-password',
9459             placeholder : this.placeholder || '' 
9460             
9461         };
9462         if (this.name) {
9463             input.name = this.name;
9464         }
9465         if (this.size) {
9466             input.cls += ' input-' + this.size;
9467         }
9468         
9469         if (this.disabled) {
9470             input.disabled=true;
9471         }
9472         
9473         var inputblock = input;
9474         
9475         if(this.hasFeedback && !this.allowBlank){
9476             
9477             var feedback = {
9478                 tag: 'span',
9479                 cls: 'glyphicon form-control-feedback'
9480             };
9481             
9482             if(this.removable && !this.editable && !this.tickable){
9483                 inputblock = {
9484                     cls : 'has-feedback',
9485                     cn :  [
9486                         inputblock,
9487                         {
9488                             tag: 'button',
9489                             html : 'x',
9490                             cls : 'roo-combo-removable-btn close'
9491                         },
9492                         feedback
9493                     ] 
9494                 };
9495             } else {
9496                 inputblock = {
9497                     cls : 'has-feedback',
9498                     cn :  [
9499                         inputblock,
9500                         feedback
9501                     ] 
9502                 };
9503             }
9504
9505         } else {
9506             if(this.removable && !this.editable && !this.tickable){
9507                 inputblock = {
9508                     cls : 'roo-removable',
9509                     cn :  [
9510                         inputblock,
9511                         {
9512                             tag: 'button',
9513                             html : 'x',
9514                             cls : 'roo-combo-removable-btn close'
9515                         }
9516                     ] 
9517                 };
9518             }
9519         }
9520         
9521         if (this.before || this.after) {
9522             
9523             inputblock = {
9524                 cls : 'input-group',
9525                 cn :  [] 
9526             };
9527             if (this.before) {
9528                 inputblock.cn.push({
9529                     tag :'span',
9530                     cls : 'input-group-addon',
9531                     html : this.before
9532                 });
9533             }
9534             
9535             inputblock.cn.push(input);
9536             
9537             if(this.hasFeedback && !this.allowBlank){
9538                 inputblock.cls += ' has-feedback';
9539                 inputblock.cn.push(feedback);
9540             }
9541             
9542             if (this.after) {
9543                 inputblock.cn.push({
9544                     tag :'span',
9545                     cls : 'input-group-addon',
9546                     html : this.after
9547                 });
9548             }
9549             
9550         };
9551         
9552         var box = {
9553             tag: 'div',
9554             cn: [
9555                 {
9556                     tag: 'input',
9557                     type : 'hidden',
9558                     cls: 'form-hidden-field'
9559                 },
9560                 inputblock
9561             ]
9562             
9563         };
9564         
9565         if(this.multiple){
9566             box = {
9567                 tag: 'div',
9568                 cn: [
9569                     {
9570                         tag: 'input',
9571                         type : 'hidden',
9572                         cls: 'form-hidden-field'
9573                     },
9574                     {
9575                         tag: 'ul',
9576                         cls: 'roo-select2-choices',
9577                         cn:[
9578                             {
9579                                 tag: 'li',
9580                                 cls: 'roo-select2-search-field',
9581                                 cn: [
9582
9583                                     inputblock
9584                                 ]
9585                             }
9586                         ]
9587                     }
9588                 ]
9589             }
9590         };
9591         
9592         var combobox = {
9593             cls: 'roo-select2-container input-group',
9594             cn: [
9595                 box
9596 //                {
9597 //                    tag: 'ul',
9598 //                    cls: 'typeahead typeahead-long dropdown-menu',
9599 //                    style: 'display:none'
9600 //                }
9601             ]
9602         };
9603         
9604         if(!this.multiple && this.showToggleBtn){
9605             
9606             var caret = {
9607                         tag: 'span',
9608                         cls: 'caret'
9609              };
9610             if (this.caret != false) {
9611                 caret = {
9612                      tag: 'i',
9613                      cls: 'fa fa-' + this.caret
9614                 };
9615                 
9616             }
9617             
9618             combobox.cn.push({
9619                 tag :'span',
9620                 cls : 'input-group-addon btn dropdown-toggle',
9621                 cn : [
9622                     caret,
9623                     {
9624                         tag: 'span',
9625                         cls: 'combobox-clear',
9626                         cn  : [
9627                             {
9628                                 tag : 'i',
9629                                 cls: 'icon-remove'
9630                             }
9631                         ]
9632                     }
9633                 ]
9634
9635             })
9636         }
9637         
9638         if(this.multiple){
9639             combobox.cls += ' roo-select2-container-multi';
9640         }
9641         
9642         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9643             
9644 //                Roo.log("left and has label");
9645             cfg.cn = [
9646                 {
9647                     tag : 'i',
9648                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9649                     tooltip : 'This field is required'
9650                 },
9651                 {
9652                     tag: 'label',
9653                     'for' :  id,
9654                     cls : 'control-label col-sm-' + this.labelWidth,
9655                     html : this.fieldLabel
9656
9657                 },
9658                 {
9659                     cls : "col-sm-" + (12 - this.labelWidth), 
9660                     cn: [
9661                         combobox
9662                     ]
9663                 }
9664
9665             ];
9666             
9667             if(this.indicatorpos == 'right'){
9668                 cfg.cn = [
9669                     {
9670                         tag: 'label',
9671                         'for' :  id,
9672                         cls : 'control-label col-sm-' + this.labelWidth,
9673                         html : this.fieldLabel
9674
9675                     },
9676                     {
9677                         tag : 'i',
9678                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9679                         tooltip : 'This field is required'
9680                     },
9681                     {
9682                         cls : "col-sm-" + (12 - this.labelWidth), 
9683                         cn: [
9684                             combobox
9685                         ]
9686                     }
9687
9688                 ];
9689             }
9690             
9691         } else if ( this.fieldLabel.length) {
9692 //                Roo.log(" label");
9693             cfg.cn = [
9694                 {
9695                    tag : 'i',
9696                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9697                    tooltip : 'This field is required'
9698                },
9699                {
9700                    tag: 'label',
9701                    //cls : 'input-group-addon',
9702                    html : this.fieldLabel
9703
9704                },
9705
9706                combobox
9707
9708             ];
9709             
9710             if(this.indicatorpos == 'right'){
9711                 
9712                 cfg.cn = [
9713                     {
9714                        tag: 'label',
9715                        //cls : 'input-group-addon',
9716                        html : this.fieldLabel
9717
9718                     },
9719                     {
9720                        tag : 'i',
9721                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9722                        tooltip : 'This field is required'
9723                     },
9724                     
9725                     combobox
9726
9727                 ];
9728
9729             }
9730
9731         } else {
9732             
9733 //                Roo.log(" no label && no align");
9734                 cfg = combobox
9735                      
9736                 
9737         }
9738          
9739         var settings=this;
9740         ['xs','sm','md','lg'].map(function(size){
9741             if (settings[size]) {
9742                 cfg.cls += ' col-' + size + '-' + settings[size];
9743             }
9744         });
9745         
9746         return cfg;
9747         
9748     },
9749     
9750     
9751     
9752     // private
9753     onResize : function(w, h){
9754 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9755 //        if(typeof w == 'number'){
9756 //            var x = w - this.trigger.getWidth();
9757 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9758 //            this.trigger.setStyle('left', x+'px');
9759 //        }
9760     },
9761
9762     // private
9763     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9764
9765     // private
9766     getResizeEl : function(){
9767         return this.inputEl();
9768     },
9769
9770     // private
9771     getPositionEl : function(){
9772         return this.inputEl();
9773     },
9774
9775     // private
9776     alignErrorIcon : function(){
9777         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9778     },
9779
9780     // private
9781     initEvents : function(){
9782         
9783         this.createList();
9784         
9785         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9786         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9787         if(!this.multiple && this.showToggleBtn){
9788             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9789             if(this.hideTrigger){
9790                 this.trigger.setDisplayed(false);
9791             }
9792             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9793         }
9794         
9795         if(this.multiple){
9796             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9797         }
9798         
9799         if(this.removable && !this.editable && !this.tickable){
9800             var close = this.closeTriggerEl();
9801             
9802             if(close){
9803                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9804                 close.on('click', this.removeBtnClick, this, close);
9805             }
9806         }
9807         
9808         //this.trigger.addClassOnOver('x-form-trigger-over');
9809         //this.trigger.addClassOnClick('x-form-trigger-click');
9810         
9811         //if(!this.width){
9812         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9813         //}
9814     },
9815     
9816     closeTriggerEl : function()
9817     {
9818         var close = this.el.select('.roo-combo-removable-btn', true).first();
9819         return close ? close : false;
9820     },
9821     
9822     removeBtnClick : function(e, h, el)
9823     {
9824         e.preventDefault();
9825         
9826         if(this.fireEvent("remove", this) !== false){
9827             this.reset();
9828             this.fireEvent("afterremove", this)
9829         }
9830     },
9831     
9832     createList : function()
9833     {
9834         this.list = Roo.get(document.body).createChild({
9835             tag: 'ul',
9836             cls: 'typeahead typeahead-long dropdown-menu',
9837             style: 'display:none'
9838         });
9839         
9840         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9841         
9842     },
9843
9844     // private
9845     initTrigger : function(){
9846        
9847     },
9848
9849     // private
9850     onDestroy : function(){
9851         if(this.trigger){
9852             this.trigger.removeAllListeners();
9853           //  this.trigger.remove();
9854         }
9855         //if(this.wrap){
9856         //    this.wrap.remove();
9857         //}
9858         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9859     },
9860
9861     // private
9862     onFocus : function(){
9863         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9864         /*
9865         if(!this.mimicing){
9866             this.wrap.addClass('x-trigger-wrap-focus');
9867             this.mimicing = true;
9868             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9869             if(this.monitorTab){
9870                 this.el.on("keydown", this.checkTab, this);
9871             }
9872         }
9873         */
9874     },
9875
9876     // private
9877     checkTab : function(e){
9878         if(e.getKey() == e.TAB){
9879             this.triggerBlur();
9880         }
9881     },
9882
9883     // private
9884     onBlur : function(){
9885         // do nothing
9886     },
9887
9888     // private
9889     mimicBlur : function(e, t){
9890         /*
9891         if(!this.wrap.contains(t) && this.validateBlur()){
9892             this.triggerBlur();
9893         }
9894         */
9895     },
9896
9897     // private
9898     triggerBlur : function(){
9899         this.mimicing = false;
9900         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9901         if(this.monitorTab){
9902             this.el.un("keydown", this.checkTab, this);
9903         }
9904         //this.wrap.removeClass('x-trigger-wrap-focus');
9905         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9906     },
9907
9908     // private
9909     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9910     validateBlur : function(e, t){
9911         return true;
9912     },
9913
9914     // private
9915     onDisable : function(){
9916         this.inputEl().dom.disabled = true;
9917         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9918         //if(this.wrap){
9919         //    this.wrap.addClass('x-item-disabled');
9920         //}
9921     },
9922
9923     // private
9924     onEnable : function(){
9925         this.inputEl().dom.disabled = false;
9926         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9927         //if(this.wrap){
9928         //    this.el.removeClass('x-item-disabled');
9929         //}
9930     },
9931
9932     // private
9933     onShow : function(){
9934         var ae = this.getActionEl();
9935         
9936         if(ae){
9937             ae.dom.style.display = '';
9938             ae.dom.style.visibility = 'visible';
9939         }
9940     },
9941
9942     // private
9943     
9944     onHide : function(){
9945         var ae = this.getActionEl();
9946         ae.dom.style.display = 'none';
9947     },
9948
9949     /**
9950      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9951      * by an implementing function.
9952      * @method
9953      * @param {EventObject} e
9954      */
9955     onTriggerClick : Roo.emptyFn
9956 });
9957  /*
9958  * Based on:
9959  * Ext JS Library 1.1.1
9960  * Copyright(c) 2006-2007, Ext JS, LLC.
9961  *
9962  * Originally Released Under LGPL - original licence link has changed is not relivant.
9963  *
9964  * Fork - LGPL
9965  * <script type="text/javascript">
9966  */
9967
9968
9969 /**
9970  * @class Roo.data.SortTypes
9971  * @singleton
9972  * Defines the default sorting (casting?) comparison functions used when sorting data.
9973  */
9974 Roo.data.SortTypes = {
9975     /**
9976      * Default sort that does nothing
9977      * @param {Mixed} s The value being converted
9978      * @return {Mixed} The comparison value
9979      */
9980     none : function(s){
9981         return s;
9982     },
9983     
9984     /**
9985      * The regular expression used to strip tags
9986      * @type {RegExp}
9987      * @property
9988      */
9989     stripTagsRE : /<\/?[^>]+>/gi,
9990     
9991     /**
9992      * Strips all HTML tags to sort on text only
9993      * @param {Mixed} s The value being converted
9994      * @return {String} The comparison value
9995      */
9996     asText : function(s){
9997         return String(s).replace(this.stripTagsRE, "");
9998     },
9999     
10000     /**
10001      * Strips all HTML tags to sort on text only - Case insensitive
10002      * @param {Mixed} s The value being converted
10003      * @return {String} The comparison value
10004      */
10005     asUCText : function(s){
10006         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10007     },
10008     
10009     /**
10010      * Case insensitive string
10011      * @param {Mixed} s The value being converted
10012      * @return {String} The comparison value
10013      */
10014     asUCString : function(s) {
10015         return String(s).toUpperCase();
10016     },
10017     
10018     /**
10019      * Date sorting
10020      * @param {Mixed} s The value being converted
10021      * @return {Number} The comparison value
10022      */
10023     asDate : function(s) {
10024         if(!s){
10025             return 0;
10026         }
10027         if(s instanceof Date){
10028             return s.getTime();
10029         }
10030         return Date.parse(String(s));
10031     },
10032     
10033     /**
10034      * Float sorting
10035      * @param {Mixed} s The value being converted
10036      * @return {Float} The comparison value
10037      */
10038     asFloat : function(s) {
10039         var val = parseFloat(String(s).replace(/,/g, ""));
10040         if(isNaN(val)) {
10041             val = 0;
10042         }
10043         return val;
10044     },
10045     
10046     /**
10047      * Integer sorting
10048      * @param {Mixed} s The value being converted
10049      * @return {Number} The comparison value
10050      */
10051     asInt : function(s) {
10052         var val = parseInt(String(s).replace(/,/g, ""));
10053         if(isNaN(val)) {
10054             val = 0;
10055         }
10056         return val;
10057     }
10058 };/*
10059  * Based on:
10060  * Ext JS Library 1.1.1
10061  * Copyright(c) 2006-2007, Ext JS, LLC.
10062  *
10063  * Originally Released Under LGPL - original licence link has changed is not relivant.
10064  *
10065  * Fork - LGPL
10066  * <script type="text/javascript">
10067  */
10068
10069 /**
10070 * @class Roo.data.Record
10071  * Instances of this class encapsulate both record <em>definition</em> information, and record
10072  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10073  * to access Records cached in an {@link Roo.data.Store} object.<br>
10074  * <p>
10075  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10076  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10077  * objects.<br>
10078  * <p>
10079  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10080  * @constructor
10081  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10082  * {@link #create}. The parameters are the same.
10083  * @param {Array} data An associative Array of data values keyed by the field name.
10084  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10085  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10086  * not specified an integer id is generated.
10087  */
10088 Roo.data.Record = function(data, id){
10089     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10090     this.data = data;
10091 };
10092
10093 /**
10094  * Generate a constructor for a specific record layout.
10095  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10096  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10097  * Each field definition object may contain the following properties: <ul>
10098  * <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,
10099  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10100  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10101  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10102  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10103  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10104  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10105  * this may be omitted.</p></li>
10106  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10107  * <ul><li>auto (Default, implies no conversion)</li>
10108  * <li>string</li>
10109  * <li>int</li>
10110  * <li>float</li>
10111  * <li>boolean</li>
10112  * <li>date</li></ul></p></li>
10113  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10114  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10115  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10116  * by the Reader into an object that will be stored in the Record. It is passed the
10117  * following parameters:<ul>
10118  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10119  * </ul></p></li>
10120  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10121  * </ul>
10122  * <br>usage:<br><pre><code>
10123 var TopicRecord = Roo.data.Record.create(
10124     {name: 'title', mapping: 'topic_title'},
10125     {name: 'author', mapping: 'username'},
10126     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10127     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10128     {name: 'lastPoster', mapping: 'user2'},
10129     {name: 'excerpt', mapping: 'post_text'}
10130 );
10131
10132 var myNewRecord = new TopicRecord({
10133     title: 'Do my job please',
10134     author: 'noobie',
10135     totalPosts: 1,
10136     lastPost: new Date(),
10137     lastPoster: 'Animal',
10138     excerpt: 'No way dude!'
10139 });
10140 myStore.add(myNewRecord);
10141 </code></pre>
10142  * @method create
10143  * @static
10144  */
10145 Roo.data.Record.create = function(o){
10146     var f = function(){
10147         f.superclass.constructor.apply(this, arguments);
10148     };
10149     Roo.extend(f, Roo.data.Record);
10150     var p = f.prototype;
10151     p.fields = new Roo.util.MixedCollection(false, function(field){
10152         return field.name;
10153     });
10154     for(var i = 0, len = o.length; i < len; i++){
10155         p.fields.add(new Roo.data.Field(o[i]));
10156     }
10157     f.getField = function(name){
10158         return p.fields.get(name);  
10159     };
10160     return f;
10161 };
10162
10163 Roo.data.Record.AUTO_ID = 1000;
10164 Roo.data.Record.EDIT = 'edit';
10165 Roo.data.Record.REJECT = 'reject';
10166 Roo.data.Record.COMMIT = 'commit';
10167
10168 Roo.data.Record.prototype = {
10169     /**
10170      * Readonly flag - true if this record has been modified.
10171      * @type Boolean
10172      */
10173     dirty : false,
10174     editing : false,
10175     error: null,
10176     modified: null,
10177
10178     // private
10179     join : function(store){
10180         this.store = store;
10181     },
10182
10183     /**
10184      * Set the named field to the specified value.
10185      * @param {String} name The name of the field to set.
10186      * @param {Object} value The value to set the field to.
10187      */
10188     set : function(name, value){
10189         if(this.data[name] == value){
10190             return;
10191         }
10192         this.dirty = true;
10193         if(!this.modified){
10194             this.modified = {};
10195         }
10196         if(typeof this.modified[name] == 'undefined'){
10197             this.modified[name] = this.data[name];
10198         }
10199         this.data[name] = value;
10200         if(!this.editing && this.store){
10201             this.store.afterEdit(this);
10202         }       
10203     },
10204
10205     /**
10206      * Get the value of the named field.
10207      * @param {String} name The name of the field to get the value of.
10208      * @return {Object} The value of the field.
10209      */
10210     get : function(name){
10211         return this.data[name]; 
10212     },
10213
10214     // private
10215     beginEdit : function(){
10216         this.editing = true;
10217         this.modified = {}; 
10218     },
10219
10220     // private
10221     cancelEdit : function(){
10222         this.editing = false;
10223         delete this.modified;
10224     },
10225
10226     // private
10227     endEdit : function(){
10228         this.editing = false;
10229         if(this.dirty && this.store){
10230             this.store.afterEdit(this);
10231         }
10232     },
10233
10234     /**
10235      * Usually called by the {@link Roo.data.Store} which owns the Record.
10236      * Rejects all changes made to the Record since either creation, or the last commit operation.
10237      * Modified fields are reverted to their original values.
10238      * <p>
10239      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10240      * of reject operations.
10241      */
10242     reject : function(){
10243         var m = this.modified;
10244         for(var n in m){
10245             if(typeof m[n] != "function"){
10246                 this.data[n] = m[n];
10247             }
10248         }
10249         this.dirty = false;
10250         delete this.modified;
10251         this.editing = false;
10252         if(this.store){
10253             this.store.afterReject(this);
10254         }
10255     },
10256
10257     /**
10258      * Usually called by the {@link Roo.data.Store} which owns the Record.
10259      * Commits all changes made to the Record since either creation, or the last commit operation.
10260      * <p>
10261      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10262      * of commit operations.
10263      */
10264     commit : function(){
10265         this.dirty = false;
10266         delete this.modified;
10267         this.editing = false;
10268         if(this.store){
10269             this.store.afterCommit(this);
10270         }
10271     },
10272
10273     // private
10274     hasError : function(){
10275         return this.error != null;
10276     },
10277
10278     // private
10279     clearError : function(){
10280         this.error = null;
10281     },
10282
10283     /**
10284      * Creates a copy of this record.
10285      * @param {String} id (optional) A new record id if you don't want to use this record's id
10286      * @return {Record}
10287      */
10288     copy : function(newId) {
10289         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10290     }
10291 };/*
10292  * Based on:
10293  * Ext JS Library 1.1.1
10294  * Copyright(c) 2006-2007, Ext JS, LLC.
10295  *
10296  * Originally Released Under LGPL - original licence link has changed is not relivant.
10297  *
10298  * Fork - LGPL
10299  * <script type="text/javascript">
10300  */
10301
10302
10303
10304 /**
10305  * @class Roo.data.Store
10306  * @extends Roo.util.Observable
10307  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10308  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10309  * <p>
10310  * 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
10311  * has no knowledge of the format of the data returned by the Proxy.<br>
10312  * <p>
10313  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10314  * instances from the data object. These records are cached and made available through accessor functions.
10315  * @constructor
10316  * Creates a new Store.
10317  * @param {Object} config A config object containing the objects needed for the Store to access data,
10318  * and read the data into Records.
10319  */
10320 Roo.data.Store = function(config){
10321     this.data = new Roo.util.MixedCollection(false);
10322     this.data.getKey = function(o){
10323         return o.id;
10324     };
10325     this.baseParams = {};
10326     // private
10327     this.paramNames = {
10328         "start" : "start",
10329         "limit" : "limit",
10330         "sort" : "sort",
10331         "dir" : "dir",
10332         "multisort" : "_multisort"
10333     };
10334
10335     if(config && config.data){
10336         this.inlineData = config.data;
10337         delete config.data;
10338     }
10339
10340     Roo.apply(this, config);
10341     
10342     if(this.reader){ // reader passed
10343         this.reader = Roo.factory(this.reader, Roo.data);
10344         this.reader.xmodule = this.xmodule || false;
10345         if(!this.recordType){
10346             this.recordType = this.reader.recordType;
10347         }
10348         if(this.reader.onMetaChange){
10349             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10350         }
10351     }
10352
10353     if(this.recordType){
10354         this.fields = this.recordType.prototype.fields;
10355     }
10356     this.modified = [];
10357
10358     this.addEvents({
10359         /**
10360          * @event datachanged
10361          * Fires when the data cache has changed, and a widget which is using this Store
10362          * as a Record cache should refresh its view.
10363          * @param {Store} this
10364          */
10365         datachanged : true,
10366         /**
10367          * @event metachange
10368          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10369          * @param {Store} this
10370          * @param {Object} meta The JSON metadata
10371          */
10372         metachange : true,
10373         /**
10374          * @event add
10375          * Fires when Records have been added to the Store
10376          * @param {Store} this
10377          * @param {Roo.data.Record[]} records The array of Records added
10378          * @param {Number} index The index at which the record(s) were added
10379          */
10380         add : true,
10381         /**
10382          * @event remove
10383          * Fires when a Record has been removed from the Store
10384          * @param {Store} this
10385          * @param {Roo.data.Record} record The Record that was removed
10386          * @param {Number} index The index at which the record was removed
10387          */
10388         remove : true,
10389         /**
10390          * @event update
10391          * Fires when a Record has been updated
10392          * @param {Store} this
10393          * @param {Roo.data.Record} record The Record that was updated
10394          * @param {String} operation The update operation being performed.  Value may be one of:
10395          * <pre><code>
10396  Roo.data.Record.EDIT
10397  Roo.data.Record.REJECT
10398  Roo.data.Record.COMMIT
10399          * </code></pre>
10400          */
10401         update : true,
10402         /**
10403          * @event clear
10404          * Fires when the data cache has been cleared.
10405          * @param {Store} this
10406          */
10407         clear : true,
10408         /**
10409          * @event beforeload
10410          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10411          * the load action will be canceled.
10412          * @param {Store} this
10413          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10414          */
10415         beforeload : true,
10416         /**
10417          * @event beforeloadadd
10418          * Fires after a new set of Records has been loaded.
10419          * @param {Store} this
10420          * @param {Roo.data.Record[]} records The Records that were loaded
10421          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10422          */
10423         beforeloadadd : true,
10424         /**
10425          * @event load
10426          * Fires after a new set of Records has been loaded, before they are added to the store.
10427          * @param {Store} this
10428          * @param {Roo.data.Record[]} records The Records that were loaded
10429          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10430          * @params {Object} return from reader
10431          */
10432         load : true,
10433         /**
10434          * @event loadexception
10435          * Fires if an exception occurs in the Proxy during loading.
10436          * Called with the signature of the Proxy's "loadexception" event.
10437          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10438          * 
10439          * @param {Proxy} 
10440          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10441          * @param {Object} load options 
10442          * @param {Object} jsonData from your request (normally this contains the Exception)
10443          */
10444         loadexception : true
10445     });
10446     
10447     if(this.proxy){
10448         this.proxy = Roo.factory(this.proxy, Roo.data);
10449         this.proxy.xmodule = this.xmodule || false;
10450         this.relayEvents(this.proxy,  ["loadexception"]);
10451     }
10452     this.sortToggle = {};
10453     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10454
10455     Roo.data.Store.superclass.constructor.call(this);
10456
10457     if(this.inlineData){
10458         this.loadData(this.inlineData);
10459         delete this.inlineData;
10460     }
10461 };
10462
10463 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10464      /**
10465     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10466     * without a remote query - used by combo/forms at present.
10467     */
10468     
10469     /**
10470     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10471     */
10472     /**
10473     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10474     */
10475     /**
10476     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10477     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10478     */
10479     /**
10480     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10481     * on any HTTP request
10482     */
10483     /**
10484     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10485     */
10486     /**
10487     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10488     */
10489     multiSort: false,
10490     /**
10491     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10492     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10493     */
10494     remoteSort : false,
10495
10496     /**
10497     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10498      * loaded or when a record is removed. (defaults to false).
10499     */
10500     pruneModifiedRecords : false,
10501
10502     // private
10503     lastOptions : null,
10504
10505     /**
10506      * Add Records to the Store and fires the add event.
10507      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10508      */
10509     add : function(records){
10510         records = [].concat(records);
10511         for(var i = 0, len = records.length; i < len; i++){
10512             records[i].join(this);
10513         }
10514         var index = this.data.length;
10515         this.data.addAll(records);
10516         this.fireEvent("add", this, records, index);
10517     },
10518
10519     /**
10520      * Remove a Record from the Store and fires the remove event.
10521      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10522      */
10523     remove : function(record){
10524         var index = this.data.indexOf(record);
10525         this.data.removeAt(index);
10526         if(this.pruneModifiedRecords){
10527             this.modified.remove(record);
10528         }
10529         this.fireEvent("remove", this, record, index);
10530     },
10531
10532     /**
10533      * Remove all Records from the Store and fires the clear event.
10534      */
10535     removeAll : function(){
10536         this.data.clear();
10537         if(this.pruneModifiedRecords){
10538             this.modified = [];
10539         }
10540         this.fireEvent("clear", this);
10541     },
10542
10543     /**
10544      * Inserts Records to the Store at the given index and fires the add event.
10545      * @param {Number} index The start index at which to insert the passed Records.
10546      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10547      */
10548     insert : function(index, records){
10549         records = [].concat(records);
10550         for(var i = 0, len = records.length; i < len; i++){
10551             this.data.insert(index, records[i]);
10552             records[i].join(this);
10553         }
10554         this.fireEvent("add", this, records, index);
10555     },
10556
10557     /**
10558      * Get the index within the cache of the passed Record.
10559      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10560      * @return {Number} The index of the passed Record. Returns -1 if not found.
10561      */
10562     indexOf : function(record){
10563         return this.data.indexOf(record);
10564     },
10565
10566     /**
10567      * Get the index within the cache of the Record with the passed id.
10568      * @param {String} id The id of the Record to find.
10569      * @return {Number} The index of the Record. Returns -1 if not found.
10570      */
10571     indexOfId : function(id){
10572         return this.data.indexOfKey(id);
10573     },
10574
10575     /**
10576      * Get the Record with the specified id.
10577      * @param {String} id The id of the Record to find.
10578      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10579      */
10580     getById : function(id){
10581         return this.data.key(id);
10582     },
10583
10584     /**
10585      * Get the Record at the specified index.
10586      * @param {Number} index The index of the Record to find.
10587      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10588      */
10589     getAt : function(index){
10590         return this.data.itemAt(index);
10591     },
10592
10593     /**
10594      * Returns a range of Records between specified indices.
10595      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10596      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10597      * @return {Roo.data.Record[]} An array of Records
10598      */
10599     getRange : function(start, end){
10600         return this.data.getRange(start, end);
10601     },
10602
10603     // private
10604     storeOptions : function(o){
10605         o = Roo.apply({}, o);
10606         delete o.callback;
10607         delete o.scope;
10608         this.lastOptions = o;
10609     },
10610
10611     /**
10612      * Loads the Record cache from the configured Proxy using the configured Reader.
10613      * <p>
10614      * If using remote paging, then the first load call must specify the <em>start</em>
10615      * and <em>limit</em> properties in the options.params property to establish the initial
10616      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10617      * <p>
10618      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10619      * and this call will return before the new data has been loaded. Perform any post-processing
10620      * in a callback function, or in a "load" event handler.</strong>
10621      * <p>
10622      * @param {Object} options An object containing properties which control loading options:<ul>
10623      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10624      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10625      * passed the following arguments:<ul>
10626      * <li>r : Roo.data.Record[]</li>
10627      * <li>options: Options object from the load call</li>
10628      * <li>success: Boolean success indicator</li></ul></li>
10629      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10630      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10631      * </ul>
10632      */
10633     load : function(options){
10634         options = options || {};
10635         if(this.fireEvent("beforeload", this, options) !== false){
10636             this.storeOptions(options);
10637             var p = Roo.apply(options.params || {}, this.baseParams);
10638             // if meta was not loaded from remote source.. try requesting it.
10639             if (!this.reader.metaFromRemote) {
10640                 p._requestMeta = 1;
10641             }
10642             if(this.sortInfo && this.remoteSort){
10643                 var pn = this.paramNames;
10644                 p[pn["sort"]] = this.sortInfo.field;
10645                 p[pn["dir"]] = this.sortInfo.direction;
10646             }
10647             if (this.multiSort) {
10648                 var pn = this.paramNames;
10649                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10650             }
10651             
10652             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10653         }
10654     },
10655
10656     /**
10657      * Reloads the Record cache from the configured Proxy using the configured Reader and
10658      * the options from the last load operation performed.
10659      * @param {Object} options (optional) An object containing properties which may override the options
10660      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10661      * the most recently used options are reused).
10662      */
10663     reload : function(options){
10664         this.load(Roo.applyIf(options||{}, this.lastOptions));
10665     },
10666
10667     // private
10668     // Called as a callback by the Reader during a load operation.
10669     loadRecords : function(o, options, success){
10670         if(!o || success === false){
10671             if(success !== false){
10672                 this.fireEvent("load", this, [], options, o);
10673             }
10674             if(options.callback){
10675                 options.callback.call(options.scope || this, [], options, false);
10676             }
10677             return;
10678         }
10679         // if data returned failure - throw an exception.
10680         if (o.success === false) {
10681             // show a message if no listener is registered.
10682             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10683                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10684             }
10685             // loadmask wil be hooked into this..
10686             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10687             return;
10688         }
10689         var r = o.records, t = o.totalRecords || r.length;
10690         
10691         this.fireEvent("beforeloadadd", this, r, options, o);
10692         
10693         if(!options || options.add !== true){
10694             if(this.pruneModifiedRecords){
10695                 this.modified = [];
10696             }
10697             for(var i = 0, len = r.length; i < len; i++){
10698                 r[i].join(this);
10699             }
10700             if(this.snapshot){
10701                 this.data = this.snapshot;
10702                 delete this.snapshot;
10703             }
10704             this.data.clear();
10705             this.data.addAll(r);
10706             this.totalLength = t;
10707             this.applySort();
10708             this.fireEvent("datachanged", this);
10709         }else{
10710             this.totalLength = Math.max(t, this.data.length+r.length);
10711             this.add(r);
10712         }
10713         this.fireEvent("load", this, r, options, o);
10714         if(options.callback){
10715             options.callback.call(options.scope || this, r, options, true);
10716         }
10717     },
10718
10719
10720     /**
10721      * Loads data from a passed data block. A Reader which understands the format of the data
10722      * must have been configured in the constructor.
10723      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10724      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10725      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10726      */
10727     loadData : function(o, append){
10728         var r = this.reader.readRecords(o);
10729         this.loadRecords(r, {add: append}, true);
10730     },
10731
10732     /**
10733      * Gets the number of cached records.
10734      * <p>
10735      * <em>If using paging, this may not be the total size of the dataset. If the data object
10736      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10737      * the data set size</em>
10738      */
10739     getCount : function(){
10740         return this.data.length || 0;
10741     },
10742
10743     /**
10744      * Gets the total number of records in the dataset as returned by the server.
10745      * <p>
10746      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10747      * the dataset size</em>
10748      */
10749     getTotalCount : function(){
10750         return this.totalLength || 0;
10751     },
10752
10753     /**
10754      * Returns the sort state of the Store as an object with two properties:
10755      * <pre><code>
10756  field {String} The name of the field by which the Records are sorted
10757  direction {String} The sort order, "ASC" or "DESC"
10758      * </code></pre>
10759      */
10760     getSortState : function(){
10761         return this.sortInfo;
10762     },
10763
10764     // private
10765     applySort : function(){
10766         if(this.sortInfo && !this.remoteSort){
10767             var s = this.sortInfo, f = s.field;
10768             var st = this.fields.get(f).sortType;
10769             var fn = function(r1, r2){
10770                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10771                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10772             };
10773             this.data.sort(s.direction, fn);
10774             if(this.snapshot && this.snapshot != this.data){
10775                 this.snapshot.sort(s.direction, fn);
10776             }
10777         }
10778     },
10779
10780     /**
10781      * Sets the default sort column and order to be used by the next load operation.
10782      * @param {String} fieldName The name of the field to sort by.
10783      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10784      */
10785     setDefaultSort : function(field, dir){
10786         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10787     },
10788
10789     /**
10790      * Sort the Records.
10791      * If remote sorting is used, the sort is performed on the server, and the cache is
10792      * reloaded. If local sorting is used, the cache is sorted internally.
10793      * @param {String} fieldName The name of the field to sort by.
10794      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10795      */
10796     sort : function(fieldName, dir){
10797         var f = this.fields.get(fieldName);
10798         if(!dir){
10799             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10800             
10801             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10802                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10803             }else{
10804                 dir = f.sortDir;
10805             }
10806         }
10807         this.sortToggle[f.name] = dir;
10808         this.sortInfo = {field: f.name, direction: dir};
10809         if(!this.remoteSort){
10810             this.applySort();
10811             this.fireEvent("datachanged", this);
10812         }else{
10813             this.load(this.lastOptions);
10814         }
10815     },
10816
10817     /**
10818      * Calls the specified function for each of the Records in the cache.
10819      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10820      * Returning <em>false</em> aborts and exits the iteration.
10821      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10822      */
10823     each : function(fn, scope){
10824         this.data.each(fn, scope);
10825     },
10826
10827     /**
10828      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10829      * (e.g., during paging).
10830      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10831      */
10832     getModifiedRecords : function(){
10833         return this.modified;
10834     },
10835
10836     // private
10837     createFilterFn : function(property, value, anyMatch){
10838         if(!value.exec){ // not a regex
10839             value = String(value);
10840             if(value.length == 0){
10841                 return false;
10842             }
10843             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10844         }
10845         return function(r){
10846             return value.test(r.data[property]);
10847         };
10848     },
10849
10850     /**
10851      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10852      * @param {String} property A field on your records
10853      * @param {Number} start The record index to start at (defaults to 0)
10854      * @param {Number} end The last record index to include (defaults to length - 1)
10855      * @return {Number} The sum
10856      */
10857     sum : function(property, start, end){
10858         var rs = this.data.items, v = 0;
10859         start = start || 0;
10860         end = (end || end === 0) ? end : rs.length-1;
10861
10862         for(var i = start; i <= end; i++){
10863             v += (rs[i].data[property] || 0);
10864         }
10865         return v;
10866     },
10867
10868     /**
10869      * Filter the records by a specified property.
10870      * @param {String} field A field on your records
10871      * @param {String/RegExp} value Either a string that the field
10872      * should start with or a RegExp to test against the field
10873      * @param {Boolean} anyMatch True to match any part not just the beginning
10874      */
10875     filter : function(property, value, anyMatch){
10876         var fn = this.createFilterFn(property, value, anyMatch);
10877         return fn ? this.filterBy(fn) : this.clearFilter();
10878     },
10879
10880     /**
10881      * Filter by a function. The specified function will be called with each
10882      * record in this data source. If the function returns true the record is included,
10883      * otherwise it is filtered.
10884      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10885      * @param {Object} scope (optional) The scope of the function (defaults to this)
10886      */
10887     filterBy : function(fn, scope){
10888         this.snapshot = this.snapshot || this.data;
10889         this.data = this.queryBy(fn, scope||this);
10890         this.fireEvent("datachanged", this);
10891     },
10892
10893     /**
10894      * Query the records by a specified property.
10895      * @param {String} field A field on your records
10896      * @param {String/RegExp} value Either a string that the field
10897      * should start with or a RegExp to test against the field
10898      * @param {Boolean} anyMatch True to match any part not just the beginning
10899      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10900      */
10901     query : function(property, value, anyMatch){
10902         var fn = this.createFilterFn(property, value, anyMatch);
10903         return fn ? this.queryBy(fn) : this.data.clone();
10904     },
10905
10906     /**
10907      * Query by a function. The specified function will be called with each
10908      * record in this data source. If the function returns true the record is included
10909      * in the results.
10910      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10911      * @param {Object} scope (optional) The scope of the function (defaults to this)
10912       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10913      **/
10914     queryBy : function(fn, scope){
10915         var data = this.snapshot || this.data;
10916         return data.filterBy(fn, scope||this);
10917     },
10918
10919     /**
10920      * Collects unique values for a particular dataIndex from this store.
10921      * @param {String} dataIndex The property to collect
10922      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10923      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10924      * @return {Array} An array of the unique values
10925      **/
10926     collect : function(dataIndex, allowNull, bypassFilter){
10927         var d = (bypassFilter === true && this.snapshot) ?
10928                 this.snapshot.items : this.data.items;
10929         var v, sv, r = [], l = {};
10930         for(var i = 0, len = d.length; i < len; i++){
10931             v = d[i].data[dataIndex];
10932             sv = String(v);
10933             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10934                 l[sv] = true;
10935                 r[r.length] = v;
10936             }
10937         }
10938         return r;
10939     },
10940
10941     /**
10942      * Revert to a view of the Record cache with no filtering applied.
10943      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10944      */
10945     clearFilter : function(suppressEvent){
10946         if(this.snapshot && this.snapshot != this.data){
10947             this.data = this.snapshot;
10948             delete this.snapshot;
10949             if(suppressEvent !== true){
10950                 this.fireEvent("datachanged", this);
10951             }
10952         }
10953     },
10954
10955     // private
10956     afterEdit : function(record){
10957         if(this.modified.indexOf(record) == -1){
10958             this.modified.push(record);
10959         }
10960         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10961     },
10962     
10963     // private
10964     afterReject : function(record){
10965         this.modified.remove(record);
10966         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10967     },
10968
10969     // private
10970     afterCommit : function(record){
10971         this.modified.remove(record);
10972         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10973     },
10974
10975     /**
10976      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10977      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10978      */
10979     commitChanges : function(){
10980         var m = this.modified.slice(0);
10981         this.modified = [];
10982         for(var i = 0, len = m.length; i < len; i++){
10983             m[i].commit();
10984         }
10985     },
10986
10987     /**
10988      * Cancel outstanding changes on all changed records.
10989      */
10990     rejectChanges : function(){
10991         var m = this.modified.slice(0);
10992         this.modified = [];
10993         for(var i = 0, len = m.length; i < len; i++){
10994             m[i].reject();
10995         }
10996     },
10997
10998     onMetaChange : function(meta, rtype, o){
10999         this.recordType = rtype;
11000         this.fields = rtype.prototype.fields;
11001         delete this.snapshot;
11002         this.sortInfo = meta.sortInfo || this.sortInfo;
11003         this.modified = [];
11004         this.fireEvent('metachange', this, this.reader.meta);
11005     },
11006     
11007     moveIndex : function(data, type)
11008     {
11009         var index = this.indexOf(data);
11010         
11011         var newIndex = index + type;
11012         
11013         this.remove(data);
11014         
11015         this.insert(newIndex, data);
11016         
11017     }
11018 });/*
11019  * Based on:
11020  * Ext JS Library 1.1.1
11021  * Copyright(c) 2006-2007, Ext JS, LLC.
11022  *
11023  * Originally Released Under LGPL - original licence link has changed is not relivant.
11024  *
11025  * Fork - LGPL
11026  * <script type="text/javascript">
11027  */
11028
11029 /**
11030  * @class Roo.data.SimpleStore
11031  * @extends Roo.data.Store
11032  * Small helper class to make creating Stores from Array data easier.
11033  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11034  * @cfg {Array} fields An array of field definition objects, or field name strings.
11035  * @cfg {Array} data The multi-dimensional array of data
11036  * @constructor
11037  * @param {Object} config
11038  */
11039 Roo.data.SimpleStore = function(config){
11040     Roo.data.SimpleStore.superclass.constructor.call(this, {
11041         isLocal : true,
11042         reader: new Roo.data.ArrayReader({
11043                 id: config.id
11044             },
11045             Roo.data.Record.create(config.fields)
11046         ),
11047         proxy : new Roo.data.MemoryProxy(config.data)
11048     });
11049     this.load();
11050 };
11051 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11052  * Based on:
11053  * Ext JS Library 1.1.1
11054  * Copyright(c) 2006-2007, Ext JS, LLC.
11055  *
11056  * Originally Released Under LGPL - original licence link has changed is not relivant.
11057  *
11058  * Fork - LGPL
11059  * <script type="text/javascript">
11060  */
11061
11062 /**
11063 /**
11064  * @extends Roo.data.Store
11065  * @class Roo.data.JsonStore
11066  * Small helper class to make creating Stores for JSON data easier. <br/>
11067 <pre><code>
11068 var store = new Roo.data.JsonStore({
11069     url: 'get-images.php',
11070     root: 'images',
11071     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11072 });
11073 </code></pre>
11074  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11075  * JsonReader and HttpProxy (unless inline data is provided).</b>
11076  * @cfg {Array} fields An array of field definition objects, or field name strings.
11077  * @constructor
11078  * @param {Object} config
11079  */
11080 Roo.data.JsonStore = function(c){
11081     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11082         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11083         reader: new Roo.data.JsonReader(c, c.fields)
11084     }));
11085 };
11086 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11087  * Based on:
11088  * Ext JS Library 1.1.1
11089  * Copyright(c) 2006-2007, Ext JS, LLC.
11090  *
11091  * Originally Released Under LGPL - original licence link has changed is not relivant.
11092  *
11093  * Fork - LGPL
11094  * <script type="text/javascript">
11095  */
11096
11097  
11098 Roo.data.Field = function(config){
11099     if(typeof config == "string"){
11100         config = {name: config};
11101     }
11102     Roo.apply(this, config);
11103     
11104     if(!this.type){
11105         this.type = "auto";
11106     }
11107     
11108     var st = Roo.data.SortTypes;
11109     // named sortTypes are supported, here we look them up
11110     if(typeof this.sortType == "string"){
11111         this.sortType = st[this.sortType];
11112     }
11113     
11114     // set default sortType for strings and dates
11115     if(!this.sortType){
11116         switch(this.type){
11117             case "string":
11118                 this.sortType = st.asUCString;
11119                 break;
11120             case "date":
11121                 this.sortType = st.asDate;
11122                 break;
11123             default:
11124                 this.sortType = st.none;
11125         }
11126     }
11127
11128     // define once
11129     var stripRe = /[\$,%]/g;
11130
11131     // prebuilt conversion function for this field, instead of
11132     // switching every time we're reading a value
11133     if(!this.convert){
11134         var cv, dateFormat = this.dateFormat;
11135         switch(this.type){
11136             case "":
11137             case "auto":
11138             case undefined:
11139                 cv = function(v){ return v; };
11140                 break;
11141             case "string":
11142                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11143                 break;
11144             case "int":
11145                 cv = function(v){
11146                     return v !== undefined && v !== null && v !== '' ?
11147                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11148                     };
11149                 break;
11150             case "float":
11151                 cv = function(v){
11152                     return v !== undefined && v !== null && v !== '' ?
11153                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11154                     };
11155                 break;
11156             case "bool":
11157             case "boolean":
11158                 cv = function(v){ return v === true || v === "true" || v == 1; };
11159                 break;
11160             case "date":
11161                 cv = function(v){
11162                     if(!v){
11163                         return '';
11164                     }
11165                     if(v instanceof Date){
11166                         return v;
11167                     }
11168                     if(dateFormat){
11169                         if(dateFormat == "timestamp"){
11170                             return new Date(v*1000);
11171                         }
11172                         return Date.parseDate(v, dateFormat);
11173                     }
11174                     var parsed = Date.parse(v);
11175                     return parsed ? new Date(parsed) : null;
11176                 };
11177              break;
11178             
11179         }
11180         this.convert = cv;
11181     }
11182 };
11183
11184 Roo.data.Field.prototype = {
11185     dateFormat: null,
11186     defaultValue: "",
11187     mapping: null,
11188     sortType : null,
11189     sortDir : "ASC"
11190 };/*
11191  * Based on:
11192  * Ext JS Library 1.1.1
11193  * Copyright(c) 2006-2007, Ext JS, LLC.
11194  *
11195  * Originally Released Under LGPL - original licence link has changed is not relivant.
11196  *
11197  * Fork - LGPL
11198  * <script type="text/javascript">
11199  */
11200  
11201 // Base class for reading structured data from a data source.  This class is intended to be
11202 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11203
11204 /**
11205  * @class Roo.data.DataReader
11206  * Base class for reading structured data from a data source.  This class is intended to be
11207  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11208  */
11209
11210 Roo.data.DataReader = function(meta, recordType){
11211     
11212     this.meta = meta;
11213     
11214     this.recordType = recordType instanceof Array ? 
11215         Roo.data.Record.create(recordType) : recordType;
11216 };
11217
11218 Roo.data.DataReader.prototype = {
11219      /**
11220      * Create an empty record
11221      * @param {Object} data (optional) - overlay some values
11222      * @return {Roo.data.Record} record created.
11223      */
11224     newRow :  function(d) {
11225         var da =  {};
11226         this.recordType.prototype.fields.each(function(c) {
11227             switch( c.type) {
11228                 case 'int' : da[c.name] = 0; break;
11229                 case 'date' : da[c.name] = new Date(); break;
11230                 case 'float' : da[c.name] = 0.0; break;
11231                 case 'boolean' : da[c.name] = false; break;
11232                 default : da[c.name] = ""; break;
11233             }
11234             
11235         });
11236         return new this.recordType(Roo.apply(da, d));
11237     }
11238     
11239 };/*
11240  * Based on:
11241  * Ext JS Library 1.1.1
11242  * Copyright(c) 2006-2007, Ext JS, LLC.
11243  *
11244  * Originally Released Under LGPL - original licence link has changed is not relivant.
11245  *
11246  * Fork - LGPL
11247  * <script type="text/javascript">
11248  */
11249
11250 /**
11251  * @class Roo.data.DataProxy
11252  * @extends Roo.data.Observable
11253  * This class is an abstract base class for implementations which provide retrieval of
11254  * unformatted data objects.<br>
11255  * <p>
11256  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11257  * (of the appropriate type which knows how to parse the data object) to provide a block of
11258  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11259  * <p>
11260  * Custom implementations must implement the load method as described in
11261  * {@link Roo.data.HttpProxy#load}.
11262  */
11263 Roo.data.DataProxy = function(){
11264     this.addEvents({
11265         /**
11266          * @event beforeload
11267          * Fires before a network request is made to retrieve a data object.
11268          * @param {Object} This DataProxy object.
11269          * @param {Object} params The params parameter to the load function.
11270          */
11271         beforeload : true,
11272         /**
11273          * @event load
11274          * Fires before the load method's callback is called.
11275          * @param {Object} This DataProxy object.
11276          * @param {Object} o The data object.
11277          * @param {Object} arg The callback argument object passed to the load function.
11278          */
11279         load : true,
11280         /**
11281          * @event loadexception
11282          * Fires if an Exception occurs during data retrieval.
11283          * @param {Object} This DataProxy object.
11284          * @param {Object} o The data object.
11285          * @param {Object} arg The callback argument object passed to the load function.
11286          * @param {Object} e The Exception.
11287          */
11288         loadexception : true
11289     });
11290     Roo.data.DataProxy.superclass.constructor.call(this);
11291 };
11292
11293 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11294
11295     /**
11296      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11297      */
11298 /*
11299  * Based on:
11300  * Ext JS Library 1.1.1
11301  * Copyright(c) 2006-2007, Ext JS, LLC.
11302  *
11303  * Originally Released Under LGPL - original licence link has changed is not relivant.
11304  *
11305  * Fork - LGPL
11306  * <script type="text/javascript">
11307  */
11308 /**
11309  * @class Roo.data.MemoryProxy
11310  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11311  * to the Reader when its load method is called.
11312  * @constructor
11313  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11314  */
11315 Roo.data.MemoryProxy = function(data){
11316     if (data.data) {
11317         data = data.data;
11318     }
11319     Roo.data.MemoryProxy.superclass.constructor.call(this);
11320     this.data = data;
11321 };
11322
11323 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11324     
11325     /**
11326      * Load data from the requested source (in this case an in-memory
11327      * data object passed to the constructor), read the data object into
11328      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11329      * process that block using the passed callback.
11330      * @param {Object} params This parameter is not used by the MemoryProxy class.
11331      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11332      * object into a block of Roo.data.Records.
11333      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11334      * The function must be passed <ul>
11335      * <li>The Record block object</li>
11336      * <li>The "arg" argument from the load function</li>
11337      * <li>A boolean success indicator</li>
11338      * </ul>
11339      * @param {Object} scope The scope in which to call the callback
11340      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11341      */
11342     load : function(params, reader, callback, scope, arg){
11343         params = params || {};
11344         var result;
11345         try {
11346             result = reader.readRecords(this.data);
11347         }catch(e){
11348             this.fireEvent("loadexception", this, arg, null, e);
11349             callback.call(scope, null, arg, false);
11350             return;
11351         }
11352         callback.call(scope, result, arg, true);
11353     },
11354     
11355     // private
11356     update : function(params, records){
11357         
11358     }
11359 });/*
11360  * Based on:
11361  * Ext JS Library 1.1.1
11362  * Copyright(c) 2006-2007, Ext JS, LLC.
11363  *
11364  * Originally Released Under LGPL - original licence link has changed is not relivant.
11365  *
11366  * Fork - LGPL
11367  * <script type="text/javascript">
11368  */
11369 /**
11370  * @class Roo.data.HttpProxy
11371  * @extends Roo.data.DataProxy
11372  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11373  * configured to reference a certain URL.<br><br>
11374  * <p>
11375  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11376  * from which the running page was served.<br><br>
11377  * <p>
11378  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11379  * <p>
11380  * Be aware that to enable the browser to parse an XML document, the server must set
11381  * the Content-Type header in the HTTP response to "text/xml".
11382  * @constructor
11383  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11384  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11385  * will be used to make the request.
11386  */
11387 Roo.data.HttpProxy = function(conn){
11388     Roo.data.HttpProxy.superclass.constructor.call(this);
11389     // is conn a conn config or a real conn?
11390     this.conn = conn;
11391     this.useAjax = !conn || !conn.events;
11392   
11393 };
11394
11395 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11396     // thse are take from connection...
11397     
11398     /**
11399      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11400      */
11401     /**
11402      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11403      * extra parameters to each request made by this object. (defaults to undefined)
11404      */
11405     /**
11406      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11407      *  to each request made by this object. (defaults to undefined)
11408      */
11409     /**
11410      * @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)
11411      */
11412     /**
11413      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11414      */
11415      /**
11416      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11417      * @type Boolean
11418      */
11419   
11420
11421     /**
11422      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11423      * @type Boolean
11424      */
11425     /**
11426      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11427      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11428      * a finer-grained basis than the DataProxy events.
11429      */
11430     getConnection : function(){
11431         return this.useAjax ? Roo.Ajax : this.conn;
11432     },
11433
11434     /**
11435      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11436      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11437      * process that block using the passed callback.
11438      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11439      * for the request to the remote server.
11440      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11441      * object into a block of Roo.data.Records.
11442      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11443      * The function must be passed <ul>
11444      * <li>The Record block object</li>
11445      * <li>The "arg" argument from the load function</li>
11446      * <li>A boolean success indicator</li>
11447      * </ul>
11448      * @param {Object} scope The scope in which to call the callback
11449      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11450      */
11451     load : function(params, reader, callback, scope, arg){
11452         if(this.fireEvent("beforeload", this, params) !== false){
11453             var  o = {
11454                 params : params || {},
11455                 request: {
11456                     callback : callback,
11457                     scope : scope,
11458                     arg : arg
11459                 },
11460                 reader: reader,
11461                 callback : this.loadResponse,
11462                 scope: this
11463             };
11464             if(this.useAjax){
11465                 Roo.applyIf(o, this.conn);
11466                 if(this.activeRequest){
11467                     Roo.Ajax.abort(this.activeRequest);
11468                 }
11469                 this.activeRequest = Roo.Ajax.request(o);
11470             }else{
11471                 this.conn.request(o);
11472             }
11473         }else{
11474             callback.call(scope||this, null, arg, false);
11475         }
11476     },
11477
11478     // private
11479     loadResponse : function(o, success, response){
11480         delete this.activeRequest;
11481         if(!success){
11482             this.fireEvent("loadexception", this, o, response);
11483             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11484             return;
11485         }
11486         var result;
11487         try {
11488             result = o.reader.read(response);
11489         }catch(e){
11490             this.fireEvent("loadexception", this, o, response, e);
11491             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11492             return;
11493         }
11494         
11495         this.fireEvent("load", this, o, o.request.arg);
11496         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11497     },
11498
11499     // private
11500     update : function(dataSet){
11501
11502     },
11503
11504     // private
11505     updateResponse : function(dataSet){
11506
11507     }
11508 });/*
11509  * Based on:
11510  * Ext JS Library 1.1.1
11511  * Copyright(c) 2006-2007, Ext JS, LLC.
11512  *
11513  * Originally Released Under LGPL - original licence link has changed is not relivant.
11514  *
11515  * Fork - LGPL
11516  * <script type="text/javascript">
11517  */
11518
11519 /**
11520  * @class Roo.data.ScriptTagProxy
11521  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11522  * other than the originating domain of the running page.<br><br>
11523  * <p>
11524  * <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
11525  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11526  * <p>
11527  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11528  * source code that is used as the source inside a &lt;script> tag.<br><br>
11529  * <p>
11530  * In order for the browser to process the returned data, the server must wrap the data object
11531  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11532  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11533  * depending on whether the callback name was passed:
11534  * <p>
11535  * <pre><code>
11536 boolean scriptTag = false;
11537 String cb = request.getParameter("callback");
11538 if (cb != null) {
11539     scriptTag = true;
11540     response.setContentType("text/javascript");
11541 } else {
11542     response.setContentType("application/x-json");
11543 }
11544 Writer out = response.getWriter();
11545 if (scriptTag) {
11546     out.write(cb + "(");
11547 }
11548 out.print(dataBlock.toJsonString());
11549 if (scriptTag) {
11550     out.write(");");
11551 }
11552 </pre></code>
11553  *
11554  * @constructor
11555  * @param {Object} config A configuration object.
11556  */
11557 Roo.data.ScriptTagProxy = function(config){
11558     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11559     Roo.apply(this, config);
11560     this.head = document.getElementsByTagName("head")[0];
11561 };
11562
11563 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11564
11565 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11566     /**
11567      * @cfg {String} url The URL from which to request the data object.
11568      */
11569     /**
11570      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11571      */
11572     timeout : 30000,
11573     /**
11574      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11575      * the server the name of the callback function set up by the load call to process the returned data object.
11576      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11577      * javascript output which calls this named function passing the data object as its only parameter.
11578      */
11579     callbackParam : "callback",
11580     /**
11581      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11582      * name to the request.
11583      */
11584     nocache : true,
11585
11586     /**
11587      * Load data from the configured URL, read the data object into
11588      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11589      * process that block using the passed callback.
11590      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11591      * for the request to the remote server.
11592      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11593      * object into a block of Roo.data.Records.
11594      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11595      * The function must be passed <ul>
11596      * <li>The Record block object</li>
11597      * <li>The "arg" argument from the load function</li>
11598      * <li>A boolean success indicator</li>
11599      * </ul>
11600      * @param {Object} scope The scope in which to call the callback
11601      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11602      */
11603     load : function(params, reader, callback, scope, arg){
11604         if(this.fireEvent("beforeload", this, params) !== false){
11605
11606             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11607
11608             var url = this.url;
11609             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11610             if(this.nocache){
11611                 url += "&_dc=" + (new Date().getTime());
11612             }
11613             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11614             var trans = {
11615                 id : transId,
11616                 cb : "stcCallback"+transId,
11617                 scriptId : "stcScript"+transId,
11618                 params : params,
11619                 arg : arg,
11620                 url : url,
11621                 callback : callback,
11622                 scope : scope,
11623                 reader : reader
11624             };
11625             var conn = this;
11626
11627             window[trans.cb] = function(o){
11628                 conn.handleResponse(o, trans);
11629             };
11630
11631             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11632
11633             if(this.autoAbort !== false){
11634                 this.abort();
11635             }
11636
11637             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11638
11639             var script = document.createElement("script");
11640             script.setAttribute("src", url);
11641             script.setAttribute("type", "text/javascript");
11642             script.setAttribute("id", trans.scriptId);
11643             this.head.appendChild(script);
11644
11645             this.trans = trans;
11646         }else{
11647             callback.call(scope||this, null, arg, false);
11648         }
11649     },
11650
11651     // private
11652     isLoading : function(){
11653         return this.trans ? true : false;
11654     },
11655
11656     /**
11657      * Abort the current server request.
11658      */
11659     abort : function(){
11660         if(this.isLoading()){
11661             this.destroyTrans(this.trans);
11662         }
11663     },
11664
11665     // private
11666     destroyTrans : function(trans, isLoaded){
11667         this.head.removeChild(document.getElementById(trans.scriptId));
11668         clearTimeout(trans.timeoutId);
11669         if(isLoaded){
11670             window[trans.cb] = undefined;
11671             try{
11672                 delete window[trans.cb];
11673             }catch(e){}
11674         }else{
11675             // if hasn't been loaded, wait for load to remove it to prevent script error
11676             window[trans.cb] = function(){
11677                 window[trans.cb] = undefined;
11678                 try{
11679                     delete window[trans.cb];
11680                 }catch(e){}
11681             };
11682         }
11683     },
11684
11685     // private
11686     handleResponse : function(o, trans){
11687         this.trans = false;
11688         this.destroyTrans(trans, true);
11689         var result;
11690         try {
11691             result = trans.reader.readRecords(o);
11692         }catch(e){
11693             this.fireEvent("loadexception", this, o, trans.arg, e);
11694             trans.callback.call(trans.scope||window, null, trans.arg, false);
11695             return;
11696         }
11697         this.fireEvent("load", this, o, trans.arg);
11698         trans.callback.call(trans.scope||window, result, trans.arg, true);
11699     },
11700
11701     // private
11702     handleFailure : function(trans){
11703         this.trans = false;
11704         this.destroyTrans(trans, false);
11705         this.fireEvent("loadexception", this, null, trans.arg);
11706         trans.callback.call(trans.scope||window, null, trans.arg, false);
11707     }
11708 });/*
11709  * Based on:
11710  * Ext JS Library 1.1.1
11711  * Copyright(c) 2006-2007, Ext JS, LLC.
11712  *
11713  * Originally Released Under LGPL - original licence link has changed is not relivant.
11714  *
11715  * Fork - LGPL
11716  * <script type="text/javascript">
11717  */
11718
11719 /**
11720  * @class Roo.data.JsonReader
11721  * @extends Roo.data.DataReader
11722  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11723  * based on mappings in a provided Roo.data.Record constructor.
11724  * 
11725  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11726  * in the reply previously. 
11727  * 
11728  * <p>
11729  * Example code:
11730  * <pre><code>
11731 var RecordDef = Roo.data.Record.create([
11732     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11733     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11734 ]);
11735 var myReader = new Roo.data.JsonReader({
11736     totalProperty: "results",    // The property which contains the total dataset size (optional)
11737     root: "rows",                // The property which contains an Array of row objects
11738     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11739 }, RecordDef);
11740 </code></pre>
11741  * <p>
11742  * This would consume a JSON file like this:
11743  * <pre><code>
11744 { 'results': 2, 'rows': [
11745     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11746     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11747 }
11748 </code></pre>
11749  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11750  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11751  * paged from the remote server.
11752  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11753  * @cfg {String} root name of the property which contains the Array of row objects.
11754  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11755  * @cfg {Array} fields Array of field definition objects
11756  * @constructor
11757  * Create a new JsonReader
11758  * @param {Object} meta Metadata configuration options
11759  * @param {Object} recordType Either an Array of field definition objects,
11760  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11761  */
11762 Roo.data.JsonReader = function(meta, recordType){
11763     
11764     meta = meta || {};
11765     // set some defaults:
11766     Roo.applyIf(meta, {
11767         totalProperty: 'total',
11768         successProperty : 'success',
11769         root : 'data',
11770         id : 'id'
11771     });
11772     
11773     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11774 };
11775 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11776     
11777     /**
11778      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11779      * Used by Store query builder to append _requestMeta to params.
11780      * 
11781      */
11782     metaFromRemote : false,
11783     /**
11784      * This method is only used by a DataProxy which has retrieved data from a remote server.
11785      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11786      * @return {Object} data A data block which is used by an Roo.data.Store object as
11787      * a cache of Roo.data.Records.
11788      */
11789     read : function(response){
11790         var json = response.responseText;
11791        
11792         var o = /* eval:var:o */ eval("("+json+")");
11793         if(!o) {
11794             throw {message: "JsonReader.read: Json object not found"};
11795         }
11796         
11797         if(o.metaData){
11798             
11799             delete this.ef;
11800             this.metaFromRemote = true;
11801             this.meta = o.metaData;
11802             this.recordType = Roo.data.Record.create(o.metaData.fields);
11803             this.onMetaChange(this.meta, this.recordType, o);
11804         }
11805         return this.readRecords(o);
11806     },
11807
11808     // private function a store will implement
11809     onMetaChange : function(meta, recordType, o){
11810
11811     },
11812
11813     /**
11814          * @ignore
11815          */
11816     simpleAccess: function(obj, subsc) {
11817         return obj[subsc];
11818     },
11819
11820         /**
11821          * @ignore
11822          */
11823     getJsonAccessor: function(){
11824         var re = /[\[\.]/;
11825         return function(expr) {
11826             try {
11827                 return(re.test(expr))
11828                     ? new Function("obj", "return obj." + expr)
11829                     : function(obj){
11830                         return obj[expr];
11831                     };
11832             } catch(e){}
11833             return Roo.emptyFn;
11834         };
11835     }(),
11836
11837     /**
11838      * Create a data block containing Roo.data.Records from an XML document.
11839      * @param {Object} o An object which contains an Array of row objects in the property specified
11840      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11841      * which contains the total size of the dataset.
11842      * @return {Object} data A data block which is used by an Roo.data.Store object as
11843      * a cache of Roo.data.Records.
11844      */
11845     readRecords : function(o){
11846         /**
11847          * After any data loads, the raw JSON data is available for further custom processing.
11848          * @type Object
11849          */
11850         this.o = o;
11851         var s = this.meta, Record = this.recordType,
11852             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11853
11854 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11855         if (!this.ef) {
11856             if(s.totalProperty) {
11857                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11858                 }
11859                 if(s.successProperty) {
11860                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11861                 }
11862                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11863                 if (s.id) {
11864                         var g = this.getJsonAccessor(s.id);
11865                         this.getId = function(rec) {
11866                                 var r = g(rec);  
11867                                 return (r === undefined || r === "") ? null : r;
11868                         };
11869                 } else {
11870                         this.getId = function(){return null;};
11871                 }
11872             this.ef = [];
11873             for(var jj = 0; jj < fl; jj++){
11874                 f = fi[jj];
11875                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11876                 this.ef[jj] = this.getJsonAccessor(map);
11877             }
11878         }
11879
11880         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11881         if(s.totalProperty){
11882             var vt = parseInt(this.getTotal(o), 10);
11883             if(!isNaN(vt)){
11884                 totalRecords = vt;
11885             }
11886         }
11887         if(s.successProperty){
11888             var vs = this.getSuccess(o);
11889             if(vs === false || vs === 'false'){
11890                 success = false;
11891             }
11892         }
11893         var records = [];
11894         for(var i = 0; i < c; i++){
11895                 var n = root[i];
11896             var values = {};
11897             var id = this.getId(n);
11898             for(var j = 0; j < fl; j++){
11899                 f = fi[j];
11900             var v = this.ef[j](n);
11901             if (!f.convert) {
11902                 Roo.log('missing convert for ' + f.name);
11903                 Roo.log(f);
11904                 continue;
11905             }
11906             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11907             }
11908             var record = new Record(values, id);
11909             record.json = n;
11910             records[i] = record;
11911         }
11912         return {
11913             raw : o,
11914             success : success,
11915             records : records,
11916             totalRecords : totalRecords
11917         };
11918     }
11919 });/*
11920  * Based on:
11921  * Ext JS Library 1.1.1
11922  * Copyright(c) 2006-2007, Ext JS, LLC.
11923  *
11924  * Originally Released Under LGPL - original licence link has changed is not relivant.
11925  *
11926  * Fork - LGPL
11927  * <script type="text/javascript">
11928  */
11929
11930 /**
11931  * @class Roo.data.ArrayReader
11932  * @extends Roo.data.DataReader
11933  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11934  * Each element of that Array represents a row of data fields. The
11935  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11936  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11937  * <p>
11938  * Example code:.
11939  * <pre><code>
11940 var RecordDef = Roo.data.Record.create([
11941     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11942     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11943 ]);
11944 var myReader = new Roo.data.ArrayReader({
11945     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11946 }, RecordDef);
11947 </code></pre>
11948  * <p>
11949  * This would consume an Array like this:
11950  * <pre><code>
11951 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11952   </code></pre>
11953  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11954  * @constructor
11955  * Create a new JsonReader
11956  * @param {Object} meta Metadata configuration options.
11957  * @param {Object} recordType Either an Array of field definition objects
11958  * as specified to {@link Roo.data.Record#create},
11959  * or an {@link Roo.data.Record} object
11960  * created using {@link Roo.data.Record#create}.
11961  */
11962 Roo.data.ArrayReader = function(meta, recordType){
11963     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11964 };
11965
11966 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11967     /**
11968      * Create a data block containing Roo.data.Records from an XML document.
11969      * @param {Object} o An Array of row objects which represents the dataset.
11970      * @return {Object} data A data block which is used by an Roo.data.Store object as
11971      * a cache of Roo.data.Records.
11972      */
11973     readRecords : function(o){
11974         var sid = this.meta ? this.meta.id : null;
11975         var recordType = this.recordType, fields = recordType.prototype.fields;
11976         var records = [];
11977         var root = o;
11978             for(var i = 0; i < root.length; i++){
11979                     var n = root[i];
11980                 var values = {};
11981                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11982                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11983                 var f = fields.items[j];
11984                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11985                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11986                 v = f.convert(v);
11987                 values[f.name] = v;
11988             }
11989                 var record = new recordType(values, id);
11990                 record.json = n;
11991                 records[records.length] = record;
11992             }
11993             return {
11994                 records : records,
11995                 totalRecords : records.length
11996             };
11997     }
11998 });/*
11999  * - LGPL
12000  * * 
12001  */
12002
12003 /**
12004  * @class Roo.bootstrap.ComboBox
12005  * @extends Roo.bootstrap.TriggerField
12006  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12007  * @cfg {Boolean} append (true|false) default false
12008  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12009  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12010  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12011  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12012  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12013  * @cfg {Boolean} animate default true
12014  * @cfg {Boolean} emptyResultText only for touch device
12015  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12016  * @constructor
12017  * Create a new ComboBox.
12018  * @param {Object} config Configuration options
12019  */
12020 Roo.bootstrap.ComboBox = function(config){
12021     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12022     this.addEvents({
12023         /**
12024          * @event expand
12025          * Fires when the dropdown list is expanded
12026              * @param {Roo.bootstrap.ComboBox} combo This combo box
12027              */
12028         'expand' : true,
12029         /**
12030          * @event collapse
12031          * Fires when the dropdown list is collapsed
12032              * @param {Roo.bootstrap.ComboBox} combo This combo box
12033              */
12034         'collapse' : true,
12035         /**
12036          * @event beforeselect
12037          * Fires before a list item is selected. Return false to cancel the selection.
12038              * @param {Roo.bootstrap.ComboBox} combo This combo box
12039              * @param {Roo.data.Record} record The data record returned from the underlying store
12040              * @param {Number} index The index of the selected item in the dropdown list
12041              */
12042         'beforeselect' : true,
12043         /**
12044          * @event select
12045          * Fires when a list item is selected
12046              * @param {Roo.bootstrap.ComboBox} combo This combo box
12047              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12048              * @param {Number} index The index of the selected item in the dropdown list
12049              */
12050         'select' : true,
12051         /**
12052          * @event beforequery
12053          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12054          * The event object passed has these properties:
12055              * @param {Roo.bootstrap.ComboBox} combo This combo box
12056              * @param {String} query The query
12057              * @param {Boolean} forceAll true to force "all" query
12058              * @param {Boolean} cancel true to cancel the query
12059              * @param {Object} e The query event object
12060              */
12061         'beforequery': true,
12062          /**
12063          * @event add
12064          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12065              * @param {Roo.bootstrap.ComboBox} combo This combo box
12066              */
12067         'add' : true,
12068         /**
12069          * @event edit
12070          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12071              * @param {Roo.bootstrap.ComboBox} combo This combo box
12072              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12073              */
12074         'edit' : true,
12075         /**
12076          * @event remove
12077          * Fires when the remove value from the combobox array
12078              * @param {Roo.bootstrap.ComboBox} combo This combo box
12079              */
12080         'remove' : true,
12081         /**
12082          * @event afterremove
12083          * Fires when the remove value from the combobox array
12084              * @param {Roo.bootstrap.ComboBox} combo This combo box
12085              */
12086         'afterremove' : true,
12087         /**
12088          * @event specialfilter
12089          * Fires when specialfilter
12090             * @param {Roo.bootstrap.ComboBox} combo This combo box
12091             */
12092         'specialfilter' : true,
12093         /**
12094          * @event tick
12095          * Fires when tick the element
12096             * @param {Roo.bootstrap.ComboBox} combo This combo box
12097             */
12098         'tick' : true,
12099         /**
12100          * @event touchviewdisplay
12101          * Fires when touch view require special display (default is using displayField)
12102             * @param {Roo.bootstrap.ComboBox} combo This combo box
12103             * @param {Object} cfg set html .
12104             */
12105         'touchviewdisplay' : true
12106         
12107     });
12108     
12109     this.item = [];
12110     this.tickItems = [];
12111     
12112     this.selectedIndex = -1;
12113     if(this.mode == 'local'){
12114         if(config.queryDelay === undefined){
12115             this.queryDelay = 10;
12116         }
12117         if(config.minChars === undefined){
12118             this.minChars = 0;
12119         }
12120     }
12121 };
12122
12123 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12124      
12125     /**
12126      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12127      * rendering into an Roo.Editor, defaults to false)
12128      */
12129     /**
12130      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12131      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12132      */
12133     /**
12134      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12135      */
12136     /**
12137      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12138      * the dropdown list (defaults to undefined, with no header element)
12139      */
12140
12141      /**
12142      * @cfg {String/Roo.Template} tpl The template to use to render the output
12143      */
12144      
12145      /**
12146      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12147      */
12148     listWidth: undefined,
12149     /**
12150      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12151      * mode = 'remote' or 'text' if mode = 'local')
12152      */
12153     displayField: undefined,
12154     
12155     /**
12156      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12157      * mode = 'remote' or 'value' if mode = 'local'). 
12158      * Note: use of a valueField requires the user make a selection
12159      * in order for a value to be mapped.
12160      */
12161     valueField: undefined,
12162     /**
12163      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12164      */
12165     modalTitle : '',
12166     
12167     /**
12168      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12169      * field's data value (defaults to the underlying DOM element's name)
12170      */
12171     hiddenName: undefined,
12172     /**
12173      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12174      */
12175     listClass: '',
12176     /**
12177      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12178      */
12179     selectedClass: 'active',
12180     
12181     /**
12182      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12183      */
12184     shadow:'sides',
12185     /**
12186      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12187      * anchor positions (defaults to 'tl-bl')
12188      */
12189     listAlign: 'tl-bl?',
12190     /**
12191      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12192      */
12193     maxHeight: 300,
12194     /**
12195      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12196      * query specified by the allQuery config option (defaults to 'query')
12197      */
12198     triggerAction: 'query',
12199     /**
12200      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12201      * (defaults to 4, does not apply if editable = false)
12202      */
12203     minChars : 4,
12204     /**
12205      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12206      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12207      */
12208     typeAhead: false,
12209     /**
12210      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12211      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12212      */
12213     queryDelay: 500,
12214     /**
12215      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12216      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12217      */
12218     pageSize: 0,
12219     /**
12220      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12221      * when editable = true (defaults to false)
12222      */
12223     selectOnFocus:false,
12224     /**
12225      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12226      */
12227     queryParam: 'query',
12228     /**
12229      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12230      * when mode = 'remote' (defaults to 'Loading...')
12231      */
12232     loadingText: 'Loading...',
12233     /**
12234      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12235      */
12236     resizable: false,
12237     /**
12238      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12239      */
12240     handleHeight : 8,
12241     /**
12242      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12243      * traditional select (defaults to true)
12244      */
12245     editable: true,
12246     /**
12247      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12248      */
12249     allQuery: '',
12250     /**
12251      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12252      */
12253     mode: 'remote',
12254     /**
12255      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12256      * listWidth has a higher value)
12257      */
12258     minListWidth : 70,
12259     /**
12260      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12261      * allow the user to set arbitrary text into the field (defaults to false)
12262      */
12263     forceSelection:false,
12264     /**
12265      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12266      * if typeAhead = true (defaults to 250)
12267      */
12268     typeAheadDelay : 250,
12269     /**
12270      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12271      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12272      */
12273     valueNotFoundText : undefined,
12274     /**
12275      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12276      */
12277     blockFocus : false,
12278     
12279     /**
12280      * @cfg {Boolean} disableClear Disable showing of clear button.
12281      */
12282     disableClear : false,
12283     /**
12284      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12285      */
12286     alwaysQuery : false,
12287     
12288     /**
12289      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12290      */
12291     multiple : false,
12292     
12293     /**
12294      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12295      */
12296     invalidClass : "has-warning",
12297     
12298     /**
12299      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12300      */
12301     validClass : "has-success",
12302     
12303     /**
12304      * @cfg {Boolean} specialFilter (true|false) special filter default false
12305      */
12306     specialFilter : false,
12307     
12308     /**
12309      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12310      */
12311     mobileTouchView : true,
12312     
12313     /**
12314      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12315      */
12316     useNativeIOS : false,
12317     
12318     ios_options : false,
12319     
12320     //private
12321     addicon : false,
12322     editicon: false,
12323     
12324     page: 0,
12325     hasQuery: false,
12326     append: false,
12327     loadNext: false,
12328     autoFocus : true,
12329     tickable : false,
12330     btnPosition : 'right',
12331     triggerList : true,
12332     showToggleBtn : true,
12333     animate : true,
12334     emptyResultText: 'Empty',
12335     triggerText : 'Select',
12336     
12337     // element that contains real text value.. (when hidden is used..)
12338     
12339     getAutoCreate : function()
12340     {
12341         var cfg = false;
12342         
12343         /*
12344          * Render classic select for iso
12345          */
12346         
12347         if(Roo.isIOS && this.useNativeIOS){
12348             cfg = this.getAutoCreateNativeIOS();
12349             return cfg;
12350         }
12351         
12352         /*
12353          * Touch Devices
12354          */
12355         
12356         if(Roo.isTouch && this.mobileTouchView){
12357             cfg = this.getAutoCreateTouchView();
12358             return cfg;;
12359         }
12360         
12361         /*
12362          *  Normal ComboBox
12363          */
12364         if(!this.tickable){
12365             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12366             return cfg;
12367         }
12368         
12369         /*
12370          *  ComboBox with tickable selections
12371          */
12372              
12373         var align = this.labelAlign || this.parentLabelAlign();
12374         
12375         cfg = {
12376             cls : 'form-group roo-combobox-tickable' //input-group
12377         };
12378         
12379         var buttons = {
12380             tag : 'div',
12381             cls : 'tickable-buttons',
12382             cn : [
12383                 {
12384                     tag : 'button',
12385                     type : 'button',
12386                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12387                     html : this.triggerText
12388                 },
12389                 {
12390                     tag : 'button',
12391                     type : 'button',
12392                     name : 'ok',
12393                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12394                     html : 'Done'
12395                 },
12396                 {
12397                     tag : 'button',
12398                     type : 'button',
12399                     name : 'cancel',
12400                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12401                     html : 'Cancel'
12402                 }
12403             ]
12404         };
12405         
12406         if(this.editable){
12407             buttons.cn.unshift({
12408                 tag: 'input',
12409                 cls: 'roo-select2-search-field-input'
12410             });
12411         }
12412         
12413         var _this = this;
12414         
12415         Roo.each(buttons.cn, function(c){
12416             if (_this.size) {
12417                 c.cls += ' btn-' + _this.size;
12418             }
12419
12420             if (_this.disabled) {
12421                 c.disabled = true;
12422             }
12423         });
12424         
12425         var box = {
12426             tag: 'div',
12427             cn: [
12428                 {
12429                     tag: 'input',
12430                     type : 'hidden',
12431                     cls: 'form-hidden-field'
12432                 },
12433                 {
12434                     tag: 'ul',
12435                     cls: 'roo-select2-choices',
12436                     cn:[
12437                         {
12438                             tag: 'li',
12439                             cls: 'roo-select2-search-field',
12440                             cn: [
12441
12442                                 buttons
12443                             ]
12444                         }
12445                     ]
12446                 }
12447             ]
12448         };
12449         
12450         var combobox = {
12451             cls: 'roo-select2-container input-group roo-select2-container-multi',
12452             cn: [
12453                 box
12454 //                {
12455 //                    tag: 'ul',
12456 //                    cls: 'typeahead typeahead-long dropdown-menu',
12457 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12458 //                }
12459             ]
12460         };
12461         
12462         if(this.hasFeedback && !this.allowBlank){
12463             
12464             var feedback = {
12465                 tag: 'span',
12466                 cls: 'glyphicon form-control-feedback'
12467             };
12468
12469             combobox.cn.push(feedback);
12470         }
12471         
12472         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12473             
12474 //                Roo.log("left and has label");
12475             cfg.cn = [
12476                 {
12477                     tag : 'i',
12478                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12479                     tooltip : 'This field is required'
12480                 },
12481                 {
12482                     tag: 'label',
12483                     'for' :  id,
12484                     cls : 'control-label col-sm-' + this.labelWidth,
12485                     html : this.fieldLabel
12486
12487                 },
12488                 {
12489                     cls : "col-sm-" + (12 - this.labelWidth), 
12490                     cn: [
12491                         combobox
12492                     ]
12493                 }
12494
12495             ];
12496
12497             if(this.indicatorpos == 'right'){
12498                 
12499                 cfg.cn = [
12500                     {
12501                         tag: 'label',
12502                         'for' :  id,
12503                         cls : 'control-label col-sm-' + this.labelWidth,
12504                         html : this.fieldLabel
12505
12506                     },
12507                     {
12508                         tag : 'i',
12509                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12510                         tooltip : 'This field is required'
12511                     },
12512                     {
12513                         cls : "col-sm-" + (12 - this.labelWidth), 
12514                         cn: [
12515                             combobox
12516                         ]
12517                     }
12518
12519                 ];
12520             
12521             }
12522                 
12523                 
12524         } else if ( this.fieldLabel.length) {
12525 //                Roo.log(" label");
12526                  cfg.cn = [
12527                     {
12528                         tag : 'i',
12529                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12530                         tooltip : 'This field is required'
12531                     },
12532                     {
12533                         tag: 'label',
12534                         //cls : 'input-group-addon',
12535                         html : this.fieldLabel
12536                         
12537                     },
12538                     
12539                     combobox
12540                     
12541                 ];
12542                 
12543                 if(this.indicatorpos == 'right'){
12544                     
12545                     cfg.cn = [
12546                         {
12547                             tag: 'label',
12548                             //cls : 'input-group-addon',
12549                             html : this.fieldLabel
12550
12551                         },
12552                         
12553                         {
12554                             tag : 'i',
12555                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12556                             tooltip : 'This field is required'
12557                         },
12558                         
12559                         combobox
12560
12561                     ];
12562                 
12563                 }
12564
12565         } else {
12566             
12567 //                Roo.log(" no label && no align");
12568                 cfg = combobox
12569                      
12570                 
12571         }
12572          
12573         var settings=this;
12574         ['xs','sm','md','lg'].map(function(size){
12575             if (settings[size]) {
12576                 cfg.cls += ' col-' + size + '-' + settings[size];
12577             }
12578         });
12579         
12580         return cfg;
12581         
12582     },
12583     
12584     _initEventsCalled : false,
12585     
12586     // private
12587     initEvents: function()
12588     {   
12589         if (this._initEventsCalled) { // as we call render... prevent looping...
12590             return;
12591         }
12592         this._initEventsCalled = true;
12593         
12594         if (!this.store) {
12595             throw "can not find store for combo";
12596         }
12597         
12598         this.store = Roo.factory(this.store, Roo.data);
12599         
12600         // if we are building from html. then this element is so complex, that we can not really
12601         // use the rendered HTML.
12602         // so we have to trash and replace the previous code.
12603         if (Roo.XComponent.build_from_html) {
12604             
12605             // remove this element....
12606             var e = this.el.dom, k=0;
12607             while (e ) { e = e.previousSibling;  ++k;}
12608
12609             this.el.remove();
12610             
12611             this.el=false;
12612             this.rendered = false;
12613             
12614             this.render(this.parent().getChildContainer(true), k);
12615             
12616             
12617             
12618         }
12619         
12620         if(Roo.isIOS && this.useNativeIOS){
12621             this.initIOSView();
12622             return;
12623         }
12624         
12625         /*
12626          * Touch Devices
12627          */
12628         
12629         if(Roo.isTouch && this.mobileTouchView){
12630             this.initTouchView();
12631             return;
12632         }
12633         
12634         if(this.tickable){
12635             this.initTickableEvents();
12636             return;
12637         }
12638         
12639         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12640         
12641         if(this.hiddenName){
12642             
12643             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12644             
12645             this.hiddenField.dom.value =
12646                 this.hiddenValue !== undefined ? this.hiddenValue :
12647                 this.value !== undefined ? this.value : '';
12648
12649             // prevent input submission
12650             this.el.dom.removeAttribute('name');
12651             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12652              
12653              
12654         }
12655         //if(Roo.isGecko){
12656         //    this.el.dom.setAttribute('autocomplete', 'off');
12657         //}
12658         
12659         var cls = 'x-combo-list';
12660         
12661         //this.list = new Roo.Layer({
12662         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12663         //});
12664         
12665         var _this = this;
12666         
12667         (function(){
12668             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12669             _this.list.setWidth(lw);
12670         }).defer(100);
12671         
12672         this.list.on('mouseover', this.onViewOver, this);
12673         this.list.on('mousemove', this.onViewMove, this);
12674         
12675         this.list.on('scroll', this.onViewScroll, this);
12676         
12677         /*
12678         this.list.swallowEvent('mousewheel');
12679         this.assetHeight = 0;
12680
12681         if(this.title){
12682             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12683             this.assetHeight += this.header.getHeight();
12684         }
12685
12686         this.innerList = this.list.createChild({cls:cls+'-inner'});
12687         this.innerList.on('mouseover', this.onViewOver, this);
12688         this.innerList.on('mousemove', this.onViewMove, this);
12689         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12690         
12691         if(this.allowBlank && !this.pageSize && !this.disableClear){
12692             this.footer = this.list.createChild({cls:cls+'-ft'});
12693             this.pageTb = new Roo.Toolbar(this.footer);
12694            
12695         }
12696         if(this.pageSize){
12697             this.footer = this.list.createChild({cls:cls+'-ft'});
12698             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12699                     {pageSize: this.pageSize});
12700             
12701         }
12702         
12703         if (this.pageTb && this.allowBlank && !this.disableClear) {
12704             var _this = this;
12705             this.pageTb.add(new Roo.Toolbar.Fill(), {
12706                 cls: 'x-btn-icon x-btn-clear',
12707                 text: '&#160;',
12708                 handler: function()
12709                 {
12710                     _this.collapse();
12711                     _this.clearValue();
12712                     _this.onSelect(false, -1);
12713                 }
12714             });
12715         }
12716         if (this.footer) {
12717             this.assetHeight += this.footer.getHeight();
12718         }
12719         */
12720             
12721         if(!this.tpl){
12722             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12723         }
12724
12725         this.view = new Roo.View(this.list, this.tpl, {
12726             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12727         });
12728         //this.view.wrapEl.setDisplayed(false);
12729         this.view.on('click', this.onViewClick, this);
12730         
12731         
12732         
12733         this.store.on('beforeload', this.onBeforeLoad, this);
12734         this.store.on('load', this.onLoad, this);
12735         this.store.on('loadexception', this.onLoadException, this);
12736         /*
12737         if(this.resizable){
12738             this.resizer = new Roo.Resizable(this.list,  {
12739                pinned:true, handles:'se'
12740             });
12741             this.resizer.on('resize', function(r, w, h){
12742                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12743                 this.listWidth = w;
12744                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12745                 this.restrictHeight();
12746             }, this);
12747             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12748         }
12749         */
12750         if(!this.editable){
12751             this.editable = true;
12752             this.setEditable(false);
12753         }
12754         
12755         /*
12756         
12757         if (typeof(this.events.add.listeners) != 'undefined') {
12758             
12759             this.addicon = this.wrap.createChild(
12760                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12761        
12762             this.addicon.on('click', function(e) {
12763                 this.fireEvent('add', this);
12764             }, this);
12765         }
12766         if (typeof(this.events.edit.listeners) != 'undefined') {
12767             
12768             this.editicon = this.wrap.createChild(
12769                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12770             if (this.addicon) {
12771                 this.editicon.setStyle('margin-left', '40px');
12772             }
12773             this.editicon.on('click', function(e) {
12774                 
12775                 // we fire even  if inothing is selected..
12776                 this.fireEvent('edit', this, this.lastData );
12777                 
12778             }, this);
12779         }
12780         */
12781         
12782         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12783             "up" : function(e){
12784                 this.inKeyMode = true;
12785                 this.selectPrev();
12786             },
12787
12788             "down" : function(e){
12789                 if(!this.isExpanded()){
12790                     this.onTriggerClick();
12791                 }else{
12792                     this.inKeyMode = true;
12793                     this.selectNext();
12794                 }
12795             },
12796
12797             "enter" : function(e){
12798 //                this.onViewClick();
12799                 //return true;
12800                 this.collapse();
12801                 
12802                 if(this.fireEvent("specialkey", this, e)){
12803                     this.onViewClick(false);
12804                 }
12805                 
12806                 return true;
12807             },
12808
12809             "esc" : function(e){
12810                 this.collapse();
12811             },
12812
12813             "tab" : function(e){
12814                 this.collapse();
12815                 
12816                 if(this.fireEvent("specialkey", this, e)){
12817                     this.onViewClick(false);
12818                 }
12819                 
12820                 return true;
12821             },
12822
12823             scope : this,
12824
12825             doRelay : function(foo, bar, hname){
12826                 if(hname == 'down' || this.scope.isExpanded()){
12827                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12828                 }
12829                 return true;
12830             },
12831
12832             forceKeyDown: true
12833         });
12834         
12835         
12836         this.queryDelay = Math.max(this.queryDelay || 10,
12837                 this.mode == 'local' ? 10 : 250);
12838         
12839         
12840         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12841         
12842         if(this.typeAhead){
12843             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12844         }
12845         if(this.editable !== false){
12846             this.inputEl().on("keyup", this.onKeyUp, this);
12847         }
12848         if(this.forceSelection){
12849             this.inputEl().on('blur', this.doForce, this);
12850         }
12851         
12852         if(this.multiple){
12853             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12854             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12855         }
12856     },
12857     
12858     initTickableEvents: function()
12859     {   
12860         this.createList();
12861         
12862         if(this.hiddenName){
12863             
12864             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12865             
12866             this.hiddenField.dom.value =
12867                 this.hiddenValue !== undefined ? this.hiddenValue :
12868                 this.value !== undefined ? this.value : '';
12869
12870             // prevent input submission
12871             this.el.dom.removeAttribute('name');
12872             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12873              
12874              
12875         }
12876         
12877 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12878         
12879         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12880         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12881         if(this.triggerList){
12882             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12883         }
12884          
12885         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12886         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12887         
12888         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12889         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12890         
12891         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12892         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12893         
12894         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12895         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12896         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12897         
12898         this.okBtn.hide();
12899         this.cancelBtn.hide();
12900         
12901         var _this = this;
12902         
12903         (function(){
12904             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12905             _this.list.setWidth(lw);
12906         }).defer(100);
12907         
12908         this.list.on('mouseover', this.onViewOver, this);
12909         this.list.on('mousemove', this.onViewMove, this);
12910         
12911         this.list.on('scroll', this.onViewScroll, this);
12912         
12913         if(!this.tpl){
12914             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>';
12915         }
12916
12917         this.view = new Roo.View(this.list, this.tpl, {
12918             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12919         });
12920         
12921         //this.view.wrapEl.setDisplayed(false);
12922         this.view.on('click', this.onViewClick, this);
12923         
12924         
12925         
12926         this.store.on('beforeload', this.onBeforeLoad, this);
12927         this.store.on('load', this.onLoad, this);
12928         this.store.on('loadexception', this.onLoadException, this);
12929         
12930         if(this.editable){
12931             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12932                 "up" : function(e){
12933                     this.inKeyMode = true;
12934                     this.selectPrev();
12935                 },
12936
12937                 "down" : function(e){
12938                     this.inKeyMode = true;
12939                     this.selectNext();
12940                 },
12941
12942                 "enter" : function(e){
12943                     if(this.fireEvent("specialkey", this, e)){
12944                         this.onViewClick(false);
12945                     }
12946                     
12947                     return true;
12948                 },
12949
12950                 "esc" : function(e){
12951                     this.onTickableFooterButtonClick(e, false, false);
12952                 },
12953
12954                 "tab" : function(e){
12955                     this.fireEvent("specialkey", this, e);
12956                     
12957                     this.onTickableFooterButtonClick(e, false, false);
12958                     
12959                     return true;
12960                 },
12961
12962                 scope : this,
12963
12964                 doRelay : function(e, fn, key){
12965                     if(this.scope.isExpanded()){
12966                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12967                     }
12968                     return true;
12969                 },
12970
12971                 forceKeyDown: true
12972             });
12973         }
12974         
12975         this.queryDelay = Math.max(this.queryDelay || 10,
12976                 this.mode == 'local' ? 10 : 250);
12977         
12978         
12979         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12980         
12981         if(this.typeAhead){
12982             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12983         }
12984         
12985         if(this.editable !== false){
12986             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12987         }
12988         
12989     },
12990
12991     onDestroy : function(){
12992         if(this.view){
12993             this.view.setStore(null);
12994             this.view.el.removeAllListeners();
12995             this.view.el.remove();
12996             this.view.purgeListeners();
12997         }
12998         if(this.list){
12999             this.list.dom.innerHTML  = '';
13000         }
13001         
13002         if(this.store){
13003             this.store.un('beforeload', this.onBeforeLoad, this);
13004             this.store.un('load', this.onLoad, this);
13005             this.store.un('loadexception', this.onLoadException, this);
13006         }
13007         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13008     },
13009
13010     // private
13011     fireKey : function(e){
13012         if(e.isNavKeyPress() && !this.list.isVisible()){
13013             this.fireEvent("specialkey", this, e);
13014         }
13015     },
13016
13017     // private
13018     onResize: function(w, h){
13019 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13020 //        
13021 //        if(typeof w != 'number'){
13022 //            // we do not handle it!?!?
13023 //            return;
13024 //        }
13025 //        var tw = this.trigger.getWidth();
13026 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13027 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13028 //        var x = w - tw;
13029 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13030 //            
13031 //        //this.trigger.setStyle('left', x+'px');
13032 //        
13033 //        if(this.list && this.listWidth === undefined){
13034 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13035 //            this.list.setWidth(lw);
13036 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13037 //        }
13038         
13039     
13040         
13041     },
13042
13043     /**
13044      * Allow or prevent the user from directly editing the field text.  If false is passed,
13045      * the user will only be able to select from the items defined in the dropdown list.  This method
13046      * is the runtime equivalent of setting the 'editable' config option at config time.
13047      * @param {Boolean} value True to allow the user to directly edit the field text
13048      */
13049     setEditable : function(value){
13050         if(value == this.editable){
13051             return;
13052         }
13053         this.editable = value;
13054         if(!value){
13055             this.inputEl().dom.setAttribute('readOnly', true);
13056             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13057             this.inputEl().addClass('x-combo-noedit');
13058         }else{
13059             this.inputEl().dom.setAttribute('readOnly', false);
13060             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13061             this.inputEl().removeClass('x-combo-noedit');
13062         }
13063     },
13064
13065     // private
13066     
13067     onBeforeLoad : function(combo,opts){
13068         if(!this.hasFocus){
13069             return;
13070         }
13071          if (!opts.add) {
13072             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13073          }
13074         this.restrictHeight();
13075         this.selectedIndex = -1;
13076     },
13077
13078     // private
13079     onLoad : function(){
13080         
13081         this.hasQuery = false;
13082         
13083         if(!this.hasFocus){
13084             return;
13085         }
13086         
13087         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13088             this.loading.hide();
13089         }
13090              
13091         if(this.store.getCount() > 0){
13092             this.expand();
13093             this.restrictHeight();
13094             if(this.lastQuery == this.allQuery){
13095                 if(this.editable && !this.tickable){
13096                     this.inputEl().dom.select();
13097                 }
13098                 
13099                 if(
13100                     !this.selectByValue(this.value, true) &&
13101                     this.autoFocus && 
13102                     (
13103                         !this.store.lastOptions ||
13104                         typeof(this.store.lastOptions.add) == 'undefined' || 
13105                         this.store.lastOptions.add != true
13106                     )
13107                 ){
13108                     this.select(0, true);
13109                 }
13110             }else{
13111                 if(this.autoFocus){
13112                     this.selectNext();
13113                 }
13114                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13115                     this.taTask.delay(this.typeAheadDelay);
13116                 }
13117             }
13118         }else{
13119             this.onEmptyResults();
13120         }
13121         
13122         //this.el.focus();
13123     },
13124     // private
13125     onLoadException : function()
13126     {
13127         this.hasQuery = false;
13128         
13129         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13130             this.loading.hide();
13131         }
13132         
13133         if(this.tickable && this.editable){
13134             return;
13135         }
13136         
13137         this.collapse();
13138         // only causes errors at present
13139         //Roo.log(this.store.reader.jsonData);
13140         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13141             // fixme
13142             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13143         //}
13144         
13145         
13146     },
13147     // private
13148     onTypeAhead : function(){
13149         if(this.store.getCount() > 0){
13150             var r = this.store.getAt(0);
13151             var newValue = r.data[this.displayField];
13152             var len = newValue.length;
13153             var selStart = this.getRawValue().length;
13154             
13155             if(selStart != len){
13156                 this.setRawValue(newValue);
13157                 this.selectText(selStart, newValue.length);
13158             }
13159         }
13160     },
13161
13162     // private
13163     onSelect : function(record, index){
13164         
13165         if(this.fireEvent('beforeselect', this, record, index) !== false){
13166         
13167             this.setFromData(index > -1 ? record.data : false);
13168             
13169             this.collapse();
13170             this.fireEvent('select', this, record, index);
13171         }
13172     },
13173
13174     /**
13175      * Returns the currently selected field value or empty string if no value is set.
13176      * @return {String} value The selected value
13177      */
13178     getValue : function()
13179     {
13180         if(Roo.isIOS && this.useNativeIOS){
13181             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13182         }
13183         
13184         if(this.multiple){
13185             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13186         }
13187         
13188         if(this.valueField){
13189             return typeof this.value != 'undefined' ? this.value : '';
13190         }else{
13191             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13192         }
13193     },
13194     
13195     getRawValue : function()
13196     {
13197         if(Roo.isIOS && this.useNativeIOS){
13198             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13199         }
13200         
13201         var v = this.inputEl().getValue();
13202         
13203         return v;
13204     },
13205
13206     /**
13207      * Clears any text/value currently set in the field
13208      */
13209     clearValue : function(){
13210         
13211         if(this.hiddenField){
13212             this.hiddenField.dom.value = '';
13213         }
13214         this.value = '';
13215         this.setRawValue('');
13216         this.lastSelectionText = '';
13217         this.lastData = false;
13218         
13219         var close = this.closeTriggerEl();
13220         
13221         if(close){
13222             close.hide();
13223         }
13224         
13225         this.validate();
13226         
13227     },
13228
13229     /**
13230      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13231      * will be displayed in the field.  If the value does not match the data value of an existing item,
13232      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13233      * Otherwise the field will be blank (although the value will still be set).
13234      * @param {String} value The value to match
13235      */
13236     setValue : function(v)
13237     {
13238         if(Roo.isIOS && this.useNativeIOS){
13239             this.setIOSValue(v);
13240             return;
13241         }
13242         
13243         if(this.multiple){
13244             this.syncValue();
13245             return;
13246         }
13247         
13248         var text = v;
13249         if(this.valueField){
13250             var r = this.findRecord(this.valueField, v);
13251             if(r){
13252                 text = r.data[this.displayField];
13253             }else if(this.valueNotFoundText !== undefined){
13254                 text = this.valueNotFoundText;
13255             }
13256         }
13257         this.lastSelectionText = text;
13258         if(this.hiddenField){
13259             this.hiddenField.dom.value = v;
13260         }
13261         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13262         this.value = v;
13263         
13264         var close = this.closeTriggerEl();
13265         
13266         if(close){
13267             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13268         }
13269         
13270         this.validate();
13271     },
13272     /**
13273      * @property {Object} the last set data for the element
13274      */
13275     
13276     lastData : false,
13277     /**
13278      * Sets the value of the field based on a object which is related to the record format for the store.
13279      * @param {Object} value the value to set as. or false on reset?
13280      */
13281     setFromData : function(o){
13282         
13283         if(this.multiple){
13284             this.addItem(o);
13285             return;
13286         }
13287             
13288         var dv = ''; // display value
13289         var vv = ''; // value value..
13290         this.lastData = o;
13291         if (this.displayField) {
13292             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13293         } else {
13294             // this is an error condition!!!
13295             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13296         }
13297         
13298         if(this.valueField){
13299             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13300         }
13301         
13302         var close = this.closeTriggerEl();
13303         
13304         if(close){
13305             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13306         }
13307         
13308         if(this.hiddenField){
13309             this.hiddenField.dom.value = vv;
13310             
13311             this.lastSelectionText = dv;
13312             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13313             this.value = vv;
13314             return;
13315         }
13316         // no hidden field.. - we store the value in 'value', but still display
13317         // display field!!!!
13318         this.lastSelectionText = dv;
13319         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13320         this.value = vv;
13321         
13322         
13323         
13324     },
13325     // private
13326     reset : function(){
13327         // overridden so that last data is reset..
13328         
13329         if(this.multiple){
13330             this.clearItem();
13331             return;
13332         }
13333         
13334         this.setValue(this.originalValue);
13335         //this.clearInvalid();
13336         this.lastData = false;
13337         if (this.view) {
13338             this.view.clearSelections();
13339         }
13340         
13341         this.validate();
13342     },
13343     // private
13344     findRecord : function(prop, value){
13345         var record;
13346         if(this.store.getCount() > 0){
13347             this.store.each(function(r){
13348                 if(r.data[prop] == value){
13349                     record = r;
13350                     return false;
13351                 }
13352                 return true;
13353             });
13354         }
13355         return record;
13356     },
13357     
13358     getName: function()
13359     {
13360         // returns hidden if it's set..
13361         if (!this.rendered) {return ''};
13362         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13363         
13364     },
13365     // private
13366     onViewMove : function(e, t){
13367         this.inKeyMode = false;
13368     },
13369
13370     // private
13371     onViewOver : function(e, t){
13372         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13373             return;
13374         }
13375         var item = this.view.findItemFromChild(t);
13376         
13377         if(item){
13378             var index = this.view.indexOf(item);
13379             this.select(index, false);
13380         }
13381     },
13382
13383     // private
13384     onViewClick : function(view, doFocus, el, e)
13385     {
13386         var index = this.view.getSelectedIndexes()[0];
13387         
13388         var r = this.store.getAt(index);
13389         
13390         if(this.tickable){
13391             
13392             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13393                 return;
13394             }
13395             
13396             var rm = false;
13397             var _this = this;
13398             
13399             Roo.each(this.tickItems, function(v,k){
13400                 
13401                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13402                     Roo.log(v);
13403                     _this.tickItems.splice(k, 1);
13404                     
13405                     if(typeof(e) == 'undefined' && view == false){
13406                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13407                     }
13408                     
13409                     rm = true;
13410                     return;
13411                 }
13412             });
13413             
13414             if(rm){
13415                 return;
13416             }
13417             
13418             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13419                 this.tickItems.push(r.data);
13420             }
13421             
13422             if(typeof(e) == 'undefined' && view == false){
13423                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13424             }
13425                     
13426             return;
13427         }
13428         
13429         if(r){
13430             this.onSelect(r, index);
13431         }
13432         if(doFocus !== false && !this.blockFocus){
13433             this.inputEl().focus();
13434         }
13435     },
13436
13437     // private
13438     restrictHeight : function(){
13439         //this.innerList.dom.style.height = '';
13440         //var inner = this.innerList.dom;
13441         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13442         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13443         //this.list.beginUpdate();
13444         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13445         this.list.alignTo(this.inputEl(), this.listAlign);
13446         this.list.alignTo(this.inputEl(), this.listAlign);
13447         //this.list.endUpdate();
13448     },
13449
13450     // private
13451     onEmptyResults : function(){
13452         
13453         if(this.tickable && this.editable){
13454             this.restrictHeight();
13455             return;
13456         }
13457         
13458         this.collapse();
13459     },
13460
13461     /**
13462      * Returns true if the dropdown list is expanded, else false.
13463      */
13464     isExpanded : function(){
13465         return this.list.isVisible();
13466     },
13467
13468     /**
13469      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13470      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13471      * @param {String} value The data value of the item to select
13472      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13473      * selected item if it is not currently in view (defaults to true)
13474      * @return {Boolean} True if the value matched an item in the list, else false
13475      */
13476     selectByValue : function(v, scrollIntoView){
13477         if(v !== undefined && v !== null){
13478             var r = this.findRecord(this.valueField || this.displayField, v);
13479             if(r){
13480                 this.select(this.store.indexOf(r), scrollIntoView);
13481                 return true;
13482             }
13483         }
13484         return false;
13485     },
13486
13487     /**
13488      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13489      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13490      * @param {Number} index The zero-based index of the list item to select
13491      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13492      * selected item if it is not currently in view (defaults to true)
13493      */
13494     select : function(index, scrollIntoView){
13495         this.selectedIndex = index;
13496         this.view.select(index);
13497         if(scrollIntoView !== false){
13498             var el = this.view.getNode(index);
13499             /*
13500              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13501              */
13502             if(el){
13503                 this.list.scrollChildIntoView(el, false);
13504             }
13505         }
13506     },
13507
13508     // private
13509     selectNext : function(){
13510         var ct = this.store.getCount();
13511         if(ct > 0){
13512             if(this.selectedIndex == -1){
13513                 this.select(0);
13514             }else if(this.selectedIndex < ct-1){
13515                 this.select(this.selectedIndex+1);
13516             }
13517         }
13518     },
13519
13520     // private
13521     selectPrev : function(){
13522         var ct = this.store.getCount();
13523         if(ct > 0){
13524             if(this.selectedIndex == -1){
13525                 this.select(0);
13526             }else if(this.selectedIndex != 0){
13527                 this.select(this.selectedIndex-1);
13528             }
13529         }
13530     },
13531
13532     // private
13533     onKeyUp : function(e){
13534         if(this.editable !== false && !e.isSpecialKey()){
13535             this.lastKey = e.getKey();
13536             this.dqTask.delay(this.queryDelay);
13537         }
13538     },
13539
13540     // private
13541     validateBlur : function(){
13542         return !this.list || !this.list.isVisible();   
13543     },
13544
13545     // private
13546     initQuery : function(){
13547         
13548         var v = this.getRawValue();
13549         
13550         if(this.tickable && this.editable){
13551             v = this.tickableInputEl().getValue();
13552         }
13553         
13554         this.doQuery(v);
13555     },
13556
13557     // private
13558     doForce : function(){
13559         if(this.inputEl().dom.value.length > 0){
13560             this.inputEl().dom.value =
13561                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13562              
13563         }
13564     },
13565
13566     /**
13567      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13568      * query allowing the query action to be canceled if needed.
13569      * @param {String} query The SQL query to execute
13570      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13571      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13572      * saved in the current store (defaults to false)
13573      */
13574     doQuery : function(q, forceAll){
13575         
13576         if(q === undefined || q === null){
13577             q = '';
13578         }
13579         var qe = {
13580             query: q,
13581             forceAll: forceAll,
13582             combo: this,
13583             cancel:false
13584         };
13585         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13586             return false;
13587         }
13588         q = qe.query;
13589         
13590         forceAll = qe.forceAll;
13591         if(forceAll === true || (q.length >= this.minChars)){
13592             
13593             this.hasQuery = true;
13594             
13595             if(this.lastQuery != q || this.alwaysQuery){
13596                 this.lastQuery = q;
13597                 if(this.mode == 'local'){
13598                     this.selectedIndex = -1;
13599                     if(forceAll){
13600                         this.store.clearFilter();
13601                     }else{
13602                         
13603                         if(this.specialFilter){
13604                             this.fireEvent('specialfilter', this);
13605                             this.onLoad();
13606                             return;
13607                         }
13608                         
13609                         this.store.filter(this.displayField, q);
13610                     }
13611                     
13612                     this.store.fireEvent("datachanged", this.store);
13613                     
13614                     this.onLoad();
13615                     
13616                     
13617                 }else{
13618                     
13619                     this.store.baseParams[this.queryParam] = q;
13620                     
13621                     var options = {params : this.getParams(q)};
13622                     
13623                     if(this.loadNext){
13624                         options.add = true;
13625                         options.params.start = this.page * this.pageSize;
13626                     }
13627                     
13628                     this.store.load(options);
13629                     
13630                     /*
13631                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13632                      *  we should expand the list on onLoad
13633                      *  so command out it
13634                      */
13635 //                    this.expand();
13636                 }
13637             }else{
13638                 this.selectedIndex = -1;
13639                 this.onLoad();   
13640             }
13641         }
13642         
13643         this.loadNext = false;
13644     },
13645     
13646     // private
13647     getParams : function(q){
13648         var p = {};
13649         //p[this.queryParam] = q;
13650         
13651         if(this.pageSize){
13652             p.start = 0;
13653             p.limit = this.pageSize;
13654         }
13655         return p;
13656     },
13657
13658     /**
13659      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13660      */
13661     collapse : function(){
13662         if(!this.isExpanded()){
13663             return;
13664         }
13665         
13666         this.list.hide();
13667         
13668         this.hasFocus = false;
13669         
13670         if(this.tickable){
13671             this.okBtn.hide();
13672             this.cancelBtn.hide();
13673             this.trigger.show();
13674             
13675             if(this.editable){
13676                 this.tickableInputEl().dom.value = '';
13677                 this.tickableInputEl().blur();
13678             }
13679             
13680         }
13681         
13682         Roo.get(document).un('mousedown', this.collapseIf, this);
13683         Roo.get(document).un('mousewheel', this.collapseIf, this);
13684         if (!this.editable) {
13685             Roo.get(document).un('keydown', this.listKeyPress, this);
13686         }
13687         this.fireEvent('collapse', this);
13688         
13689         this.validate();
13690     },
13691
13692     // private
13693     collapseIf : function(e){
13694         var in_combo  = e.within(this.el);
13695         var in_list =  e.within(this.list);
13696         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13697         
13698         if (in_combo || in_list || is_list) {
13699             //e.stopPropagation();
13700             return;
13701         }
13702         
13703         if(this.tickable){
13704             this.onTickableFooterButtonClick(e, false, false);
13705         }
13706
13707         this.collapse();
13708         
13709     },
13710
13711     /**
13712      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13713      */
13714     expand : function(){
13715        
13716         if(this.isExpanded() || !this.hasFocus){
13717             return;
13718         }
13719         
13720         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13721         this.list.setWidth(lw);
13722         
13723         Roo.log('expand');
13724         
13725         this.list.show();
13726         
13727         this.restrictHeight();
13728         
13729         if(this.tickable){
13730             
13731             this.tickItems = Roo.apply([], this.item);
13732             
13733             this.okBtn.show();
13734             this.cancelBtn.show();
13735             this.trigger.hide();
13736             
13737             if(this.editable){
13738                 this.tickableInputEl().focus();
13739             }
13740             
13741         }
13742         
13743         Roo.get(document).on('mousedown', this.collapseIf, this);
13744         Roo.get(document).on('mousewheel', this.collapseIf, this);
13745         if (!this.editable) {
13746             Roo.get(document).on('keydown', this.listKeyPress, this);
13747         }
13748         
13749         this.fireEvent('expand', this);
13750     },
13751
13752     // private
13753     // Implements the default empty TriggerField.onTriggerClick function
13754     onTriggerClick : function(e)
13755     {
13756         Roo.log('trigger click');
13757         
13758         if(this.disabled || !this.triggerList){
13759             return;
13760         }
13761         
13762         this.page = 0;
13763         this.loadNext = false;
13764         
13765         if(this.isExpanded()){
13766             this.collapse();
13767             if (!this.blockFocus) {
13768                 this.inputEl().focus();
13769             }
13770             
13771         }else {
13772             this.hasFocus = true;
13773             if(this.triggerAction == 'all') {
13774                 this.doQuery(this.allQuery, true);
13775             } else {
13776                 this.doQuery(this.getRawValue());
13777             }
13778             if (!this.blockFocus) {
13779                 this.inputEl().focus();
13780             }
13781         }
13782     },
13783     
13784     onTickableTriggerClick : function(e)
13785     {
13786         if(this.disabled){
13787             return;
13788         }
13789         
13790         this.page = 0;
13791         this.loadNext = false;
13792         this.hasFocus = true;
13793         
13794         if(this.triggerAction == 'all') {
13795             this.doQuery(this.allQuery, true);
13796         } else {
13797             this.doQuery(this.getRawValue());
13798         }
13799     },
13800     
13801     onSearchFieldClick : function(e)
13802     {
13803         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13804             this.onTickableFooterButtonClick(e, false, false);
13805             return;
13806         }
13807         
13808         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13809             return;
13810         }
13811         
13812         this.page = 0;
13813         this.loadNext = false;
13814         this.hasFocus = true;
13815         
13816         if(this.triggerAction == 'all') {
13817             this.doQuery(this.allQuery, true);
13818         } else {
13819             this.doQuery(this.getRawValue());
13820         }
13821     },
13822     
13823     listKeyPress : function(e)
13824     {
13825         //Roo.log('listkeypress');
13826         // scroll to first matching element based on key pres..
13827         if (e.isSpecialKey()) {
13828             return false;
13829         }
13830         var k = String.fromCharCode(e.getKey()).toUpperCase();
13831         //Roo.log(k);
13832         var match  = false;
13833         var csel = this.view.getSelectedNodes();
13834         var cselitem = false;
13835         if (csel.length) {
13836             var ix = this.view.indexOf(csel[0]);
13837             cselitem  = this.store.getAt(ix);
13838             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13839                 cselitem = false;
13840             }
13841             
13842         }
13843         
13844         this.store.each(function(v) { 
13845             if (cselitem) {
13846                 // start at existing selection.
13847                 if (cselitem.id == v.id) {
13848                     cselitem = false;
13849                 }
13850                 return true;
13851             }
13852                 
13853             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13854                 match = this.store.indexOf(v);
13855                 return false;
13856             }
13857             return true;
13858         }, this);
13859         
13860         if (match === false) {
13861             return true; // no more action?
13862         }
13863         // scroll to?
13864         this.view.select(match);
13865         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13866         sn.scrollIntoView(sn.dom.parentNode, false);
13867     },
13868     
13869     onViewScroll : function(e, t){
13870         
13871         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){
13872             return;
13873         }
13874         
13875         this.hasQuery = true;
13876         
13877         this.loading = this.list.select('.loading', true).first();
13878         
13879         if(this.loading === null){
13880             this.list.createChild({
13881                 tag: 'div',
13882                 cls: 'loading roo-select2-more-results roo-select2-active',
13883                 html: 'Loading more results...'
13884             });
13885             
13886             this.loading = this.list.select('.loading', true).first();
13887             
13888             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13889             
13890             this.loading.hide();
13891         }
13892         
13893         this.loading.show();
13894         
13895         var _combo = this;
13896         
13897         this.page++;
13898         this.loadNext = true;
13899         
13900         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13901         
13902         return;
13903     },
13904     
13905     addItem : function(o)
13906     {   
13907         var dv = ''; // display value
13908         
13909         if (this.displayField) {
13910             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13911         } else {
13912             // this is an error condition!!!
13913             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13914         }
13915         
13916         if(!dv.length){
13917             return;
13918         }
13919         
13920         var choice = this.choices.createChild({
13921             tag: 'li',
13922             cls: 'roo-select2-search-choice',
13923             cn: [
13924                 {
13925                     tag: 'div',
13926                     html: dv
13927                 },
13928                 {
13929                     tag: 'a',
13930                     href: '#',
13931                     cls: 'roo-select2-search-choice-close',
13932                     tabindex: '-1'
13933                 }
13934             ]
13935             
13936         }, this.searchField);
13937         
13938         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13939         
13940         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13941         
13942         this.item.push(o);
13943         
13944         this.lastData = o;
13945         
13946         this.syncValue();
13947         
13948         this.inputEl().dom.value = '';
13949         
13950         this.validate();
13951     },
13952     
13953     onRemoveItem : function(e, _self, o)
13954     {
13955         e.preventDefault();
13956         
13957         this.lastItem = Roo.apply([], this.item);
13958         
13959         var index = this.item.indexOf(o.data) * 1;
13960         
13961         if( index < 0){
13962             Roo.log('not this item?!');
13963             return;
13964         }
13965         
13966         this.item.splice(index, 1);
13967         o.item.remove();
13968         
13969         this.syncValue();
13970         
13971         this.fireEvent('remove', this, e);
13972         
13973         this.validate();
13974         
13975     },
13976     
13977     syncValue : function()
13978     {
13979         if(!this.item.length){
13980             this.clearValue();
13981             return;
13982         }
13983             
13984         var value = [];
13985         var _this = this;
13986         Roo.each(this.item, function(i){
13987             if(_this.valueField){
13988                 value.push(i[_this.valueField]);
13989                 return;
13990             }
13991
13992             value.push(i);
13993         });
13994
13995         this.value = value.join(',');
13996
13997         if(this.hiddenField){
13998             this.hiddenField.dom.value = this.value;
13999         }
14000         
14001         this.store.fireEvent("datachanged", this.store);
14002         
14003         this.validate();
14004     },
14005     
14006     clearItem : function()
14007     {
14008         if(!this.multiple){
14009             return;
14010         }
14011         
14012         this.item = [];
14013         
14014         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14015            c.remove();
14016         });
14017         
14018         this.syncValue();
14019         
14020         this.validate();
14021         
14022         if(this.tickable && !Roo.isTouch){
14023             this.view.refresh();
14024         }
14025     },
14026     
14027     inputEl: function ()
14028     {
14029         if(Roo.isIOS && this.useNativeIOS){
14030             return this.el.select('select.roo-ios-select', true).first();
14031         }
14032         
14033         if(Roo.isTouch && this.mobileTouchView){
14034             return this.el.select('input.form-control',true).first();
14035         }
14036         
14037         if(this.tickable){
14038             return this.searchField;
14039         }
14040         
14041         return this.el.select('input.form-control',true).first();
14042     },
14043     
14044     onTickableFooterButtonClick : function(e, btn, el)
14045     {
14046         e.preventDefault();
14047         
14048         this.lastItem = Roo.apply([], this.item);
14049         
14050         if(btn && btn.name == 'cancel'){
14051             this.tickItems = Roo.apply([], this.item);
14052             this.collapse();
14053             return;
14054         }
14055         
14056         this.clearItem();
14057         
14058         var _this = this;
14059         
14060         Roo.each(this.tickItems, function(o){
14061             _this.addItem(o);
14062         });
14063         
14064         this.collapse();
14065         
14066     },
14067     
14068     validate : function()
14069     {
14070         var v = this.getRawValue();
14071         
14072         if(this.multiple){
14073             v = this.getValue();
14074         }
14075         
14076         if(this.disabled || this.allowBlank || v.length){
14077             this.markValid();
14078             return true;
14079         }
14080         
14081         this.markInvalid();
14082         return false;
14083     },
14084     
14085     tickableInputEl : function()
14086     {
14087         if(!this.tickable || !this.editable){
14088             return this.inputEl();
14089         }
14090         
14091         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14092     },
14093     
14094     
14095     getAutoCreateTouchView : function()
14096     {
14097         var id = Roo.id();
14098         
14099         var cfg = {
14100             cls: 'form-group' //input-group
14101         };
14102         
14103         var input =  {
14104             tag: 'input',
14105             id : id,
14106             type : this.inputType,
14107             cls : 'form-control x-combo-noedit',
14108             autocomplete: 'new-password',
14109             placeholder : this.placeholder || '',
14110             readonly : true
14111         };
14112         
14113         if (this.name) {
14114             input.name = this.name;
14115         }
14116         
14117         if (this.size) {
14118             input.cls += ' input-' + this.size;
14119         }
14120         
14121         if (this.disabled) {
14122             input.disabled = true;
14123         }
14124         
14125         var inputblock = {
14126             cls : '',
14127             cn : [
14128                 input
14129             ]
14130         };
14131         
14132         if(this.before){
14133             inputblock.cls += ' input-group';
14134             
14135             inputblock.cn.unshift({
14136                 tag :'span',
14137                 cls : 'input-group-addon',
14138                 html : this.before
14139             });
14140         }
14141         
14142         if(this.removable && !this.multiple){
14143             inputblock.cls += ' roo-removable';
14144             
14145             inputblock.cn.push({
14146                 tag: 'button',
14147                 html : 'x',
14148                 cls : 'roo-combo-removable-btn close'
14149             });
14150         }
14151
14152         if(this.hasFeedback && !this.allowBlank){
14153             
14154             inputblock.cls += ' has-feedback';
14155             
14156             inputblock.cn.push({
14157                 tag: 'span',
14158                 cls: 'glyphicon form-control-feedback'
14159             });
14160             
14161         }
14162         
14163         if (this.after) {
14164             
14165             inputblock.cls += (this.before) ? '' : ' input-group';
14166             
14167             inputblock.cn.push({
14168                 tag :'span',
14169                 cls : 'input-group-addon',
14170                 html : this.after
14171             });
14172         }
14173
14174         var box = {
14175             tag: 'div',
14176             cn: [
14177                 {
14178                     tag: 'input',
14179                     type : 'hidden',
14180                     cls: 'form-hidden-field'
14181                 },
14182                 inputblock
14183             ]
14184             
14185         };
14186         
14187         if(this.multiple){
14188             box = {
14189                 tag: 'div',
14190                 cn: [
14191                     {
14192                         tag: 'input',
14193                         type : 'hidden',
14194                         cls: 'form-hidden-field'
14195                     },
14196                     {
14197                         tag: 'ul',
14198                         cls: 'roo-select2-choices',
14199                         cn:[
14200                             {
14201                                 tag: 'li',
14202                                 cls: 'roo-select2-search-field',
14203                                 cn: [
14204
14205                                     inputblock
14206                                 ]
14207                             }
14208                         ]
14209                     }
14210                 ]
14211             }
14212         };
14213         
14214         var combobox = {
14215             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14216             cn: [
14217                 box
14218             ]
14219         };
14220         
14221         if(!this.multiple && this.showToggleBtn){
14222             
14223             var caret = {
14224                         tag: 'span',
14225                         cls: 'caret'
14226             };
14227             
14228             if (this.caret != false) {
14229                 caret = {
14230                      tag: 'i',
14231                      cls: 'fa fa-' + this.caret
14232                 };
14233                 
14234             }
14235             
14236             combobox.cn.push({
14237                 tag :'span',
14238                 cls : 'input-group-addon btn dropdown-toggle',
14239                 cn : [
14240                     caret,
14241                     {
14242                         tag: 'span',
14243                         cls: 'combobox-clear',
14244                         cn  : [
14245                             {
14246                                 tag : 'i',
14247                                 cls: 'icon-remove'
14248                             }
14249                         ]
14250                     }
14251                 ]
14252
14253             })
14254         }
14255         
14256         if(this.multiple){
14257             combobox.cls += ' roo-select2-container-multi';
14258         }
14259         
14260         var align = this.labelAlign || this.parentLabelAlign();
14261         
14262         cfg.cn = combobox;
14263         
14264         if(this.fieldLabel.length && this.labelWidth){
14265             
14266             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14267             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14268             
14269             cfg.cn = [
14270                 {
14271                    tag : 'i',
14272                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14273                    tooltip : 'This field is required'
14274                 },
14275                 {
14276                     tag: 'label',
14277                     cls : 'control-label ' + lw,
14278                     html : this.fieldLabel
14279
14280                 },
14281                 {
14282                     cls : cw, 
14283                     cn: [
14284                         combobox
14285                     ]
14286                 }
14287             ];
14288             
14289             if(this.indicatorpos == 'right'){
14290                 cfg.cn = [
14291                     {
14292                         tag: 'label',
14293                         cls : 'control-label ' + lw,
14294                         html : this.fieldLabel
14295
14296                     },
14297                     {
14298                        tag : 'i',
14299                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14300                        tooltip : 'This field is required'
14301                     },
14302                     {
14303                         cls : cw, 
14304                         cn: [
14305                             combobox
14306                         ]
14307                     }
14308                 ];
14309             }
14310         }
14311         
14312         var settings = this;
14313         
14314         ['xs','sm','md','lg'].map(function(size){
14315             if (settings[size]) {
14316                 cfg.cls += ' col-' + size + '-' + settings[size];
14317             }
14318         });
14319         
14320         return cfg;
14321     },
14322     
14323     initTouchView : function()
14324     {
14325         this.renderTouchView();
14326         
14327         this.touchViewEl.on('scroll', function(){
14328             this.el.dom.scrollTop = 0;
14329         }, this);
14330         
14331         this.originalValue = this.getValue();
14332         
14333         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14334         
14335         this.inputEl().on("click", this.showTouchView, this);
14336         if (this.triggerEl) {
14337             this.triggerEl.on("click", this.showTouchView, this);
14338         }
14339         
14340         
14341         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14342         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14343         
14344         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14345         
14346         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14347         this.store.on('load', this.onTouchViewLoad, this);
14348         this.store.on('loadexception', this.onTouchViewLoadException, this);
14349         
14350         if(this.hiddenName){
14351             
14352             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14353             
14354             this.hiddenField.dom.value =
14355                 this.hiddenValue !== undefined ? this.hiddenValue :
14356                 this.value !== undefined ? this.value : '';
14357         
14358             this.el.dom.removeAttribute('name');
14359             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14360         }
14361         
14362         if(this.multiple){
14363             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14364             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14365         }
14366         
14367         if(this.removable && !this.multiple){
14368             var close = this.closeTriggerEl();
14369             if(close){
14370                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14371                 close.on('click', this.removeBtnClick, this, close);
14372             }
14373         }
14374         /*
14375          * fix the bug in Safari iOS8
14376          */
14377         this.inputEl().on("focus", function(e){
14378             document.activeElement.blur();
14379         }, this);
14380         
14381         return;
14382         
14383         
14384     },
14385     
14386     renderTouchView : function()
14387     {
14388         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14389         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14390         
14391         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14392         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14393         
14394         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14395         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14396         this.touchViewBodyEl.setStyle('overflow', 'auto');
14397         
14398         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14399         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14400         
14401         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14402         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14403         
14404     },
14405     
14406     showTouchView : function()
14407     {
14408         if(this.disabled){
14409             return;
14410         }
14411         
14412         this.touchViewHeaderEl.hide();
14413
14414         if(this.modalTitle.length){
14415             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14416             this.touchViewHeaderEl.show();
14417         }
14418
14419         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14420         this.touchViewEl.show();
14421
14422         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14423         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14424                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14425
14426         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14427
14428         if(this.modalTitle.length){
14429             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14430         }
14431         
14432         this.touchViewBodyEl.setHeight(bodyHeight);
14433
14434         if(this.animate){
14435             var _this = this;
14436             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14437         }else{
14438             this.touchViewEl.addClass('in');
14439         }
14440
14441         this.doTouchViewQuery();
14442         
14443     },
14444     
14445     hideTouchView : function()
14446     {
14447         this.touchViewEl.removeClass('in');
14448
14449         if(this.animate){
14450             var _this = this;
14451             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14452         }else{
14453             this.touchViewEl.setStyle('display', 'none');
14454         }
14455         
14456     },
14457     
14458     setTouchViewValue : function()
14459     {
14460         if(this.multiple){
14461             this.clearItem();
14462         
14463             var _this = this;
14464
14465             Roo.each(this.tickItems, function(o){
14466                 this.addItem(o);
14467             }, this);
14468         }
14469         
14470         this.hideTouchView();
14471     },
14472     
14473     doTouchViewQuery : function()
14474     {
14475         var qe = {
14476             query: '',
14477             forceAll: true,
14478             combo: this,
14479             cancel:false
14480         };
14481         
14482         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14483             return false;
14484         }
14485         
14486         if(!this.alwaysQuery || this.mode == 'local'){
14487             this.onTouchViewLoad();
14488             return;
14489         }
14490         
14491         this.store.load();
14492     },
14493     
14494     onTouchViewBeforeLoad : function(combo,opts)
14495     {
14496         return;
14497     },
14498
14499     // private
14500     onTouchViewLoad : function()
14501     {
14502         if(this.store.getCount() < 1){
14503             this.onTouchViewEmptyResults();
14504             return;
14505         }
14506         
14507         this.clearTouchView();
14508         
14509         var rawValue = this.getRawValue();
14510         
14511         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14512         
14513         this.tickItems = [];
14514         
14515         this.store.data.each(function(d, rowIndex){
14516             var row = this.touchViewListGroup.createChild(template);
14517             
14518             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14519                 row.addClass(d.data.cls);
14520             }
14521             
14522             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14523                 var cfg = {
14524                     data : d.data,
14525                     html : d.data[this.displayField]
14526                 };
14527                 
14528                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14529                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14530                 }
14531             }
14532             row.removeClass('selected');
14533             if(!this.multiple && this.valueField &&
14534                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14535             {
14536                 // radio buttons..
14537                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14538                 row.addClass('selected');
14539             }
14540             
14541             if(this.multiple && this.valueField &&
14542                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14543             {
14544                 
14545                 // checkboxes...
14546                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14547                 this.tickItems.push(d.data);
14548             }
14549             
14550             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14551             
14552         }, this);
14553         
14554         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14555         
14556         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14557
14558         if(this.modalTitle.length){
14559             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14560         }
14561
14562         var listHeight = this.touchViewListGroup.getHeight();
14563         
14564         var _this = this;
14565         
14566         if(firstChecked && listHeight > bodyHeight){
14567             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14568         }
14569         
14570     },
14571     
14572     onTouchViewLoadException : function()
14573     {
14574         this.hideTouchView();
14575     },
14576     
14577     onTouchViewEmptyResults : function()
14578     {
14579         this.clearTouchView();
14580         
14581         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14582         
14583         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14584         
14585     },
14586     
14587     clearTouchView : function()
14588     {
14589         this.touchViewListGroup.dom.innerHTML = '';
14590     },
14591     
14592     onTouchViewClick : function(e, el, o)
14593     {
14594         e.preventDefault();
14595         
14596         var row = o.row;
14597         var rowIndex = o.rowIndex;
14598         
14599         var r = this.store.getAt(rowIndex);
14600         
14601         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14602             
14603             if(!this.multiple){
14604                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14605                     c.dom.removeAttribute('checked');
14606                 }, this);
14607
14608                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14609
14610                 this.setFromData(r.data);
14611
14612                 var close = this.closeTriggerEl();
14613
14614                 if(close){
14615                     close.show();
14616                 }
14617
14618                 this.hideTouchView();
14619
14620                 this.fireEvent('select', this, r, rowIndex);
14621
14622                 return;
14623             }
14624
14625             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14626                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14627                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14628                 return;
14629             }
14630
14631             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14632             this.addItem(r.data);
14633             this.tickItems.push(r.data);
14634         }
14635     },
14636     
14637     getAutoCreateNativeIOS : function()
14638     {
14639         var cfg = {
14640             cls: 'form-group' //input-group,
14641         };
14642         
14643         var combobox =  {
14644             tag: 'select',
14645             cls : 'roo-ios-select'
14646         };
14647         
14648         if (this.name) {
14649             combobox.name = this.name;
14650         }
14651         
14652         if (this.disabled) {
14653             combobox.disabled = true;
14654         }
14655         
14656         var settings = this;
14657         
14658         ['xs','sm','md','lg'].map(function(size){
14659             if (settings[size]) {
14660                 cfg.cls += ' col-' + size + '-' + settings[size];
14661             }
14662         });
14663         
14664         cfg.cn = combobox;
14665         
14666         return cfg;
14667         
14668     },
14669     
14670     initIOSView : function()
14671     {
14672         this.store.on('load', this.onIOSViewLoad, this);
14673         
14674         return;
14675     },
14676     
14677     onIOSViewLoad : function()
14678     {
14679         if(this.store.getCount() < 1){
14680             return;
14681         }
14682         
14683         this.clearIOSView();
14684         
14685         if(this.allowBlank) {
14686             
14687             var default_text = '-- SELECT --';
14688             
14689             var opt = this.inputEl().createChild({
14690                 tag: 'option',
14691                 value : 0,
14692                 html : default_text
14693             });
14694             
14695             var o = {};
14696             o[this.valueField] = 0;
14697             o[this.displayField] = default_text;
14698             
14699             this.ios_options.push({
14700                 data : o,
14701                 el : opt
14702             });
14703             
14704         }
14705         
14706         this.store.data.each(function(d, rowIndex){
14707             
14708             var html = '';
14709             
14710             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14711                 html = d.data[this.displayField];
14712             }
14713             
14714             var value = '';
14715             
14716             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14717                 value = d.data[this.valueField];
14718             }
14719             
14720             var option = {
14721                 tag: 'option',
14722                 value : value,
14723                 html : html
14724             };
14725             
14726             if(this.value == d.data[this.valueField]){
14727                 option['selected'] = true;
14728             }
14729             
14730             var opt = this.inputEl().createChild(option);
14731             
14732             this.ios_options.push({
14733                 data : d.data,
14734                 el : opt
14735             });
14736             
14737         }, this);
14738         
14739         this.inputEl().on('change', function(){
14740            this.fireEvent('select', this);
14741         }, this);
14742         
14743     },
14744     
14745     clearIOSView: function()
14746     {
14747         this.inputEl().dom.innerHTML = '';
14748         
14749         this.ios_options = [];
14750     },
14751     
14752     setIOSValue: function(v)
14753     {
14754         this.value = v;
14755         
14756         if(!this.ios_options){
14757             return;
14758         }
14759         
14760         Roo.each(this.ios_options, function(opts){
14761            
14762            opts.el.dom.removeAttribute('selected');
14763            
14764            if(opts.data[this.valueField] != v){
14765                return;
14766            }
14767            
14768            opts.el.dom.setAttribute('selected', true);
14769            
14770         }, this);
14771     }
14772
14773     /** 
14774     * @cfg {Boolean} grow 
14775     * @hide 
14776     */
14777     /** 
14778     * @cfg {Number} growMin 
14779     * @hide 
14780     */
14781     /** 
14782     * @cfg {Number} growMax 
14783     * @hide 
14784     */
14785     /**
14786      * @hide
14787      * @method autoSize
14788      */
14789 });
14790
14791 Roo.apply(Roo.bootstrap.ComboBox,  {
14792     
14793     header : {
14794         tag: 'div',
14795         cls: 'modal-header',
14796         cn: [
14797             {
14798                 tag: 'h4',
14799                 cls: 'modal-title'
14800             }
14801         ]
14802     },
14803     
14804     body : {
14805         tag: 'div',
14806         cls: 'modal-body',
14807         cn: [
14808             {
14809                 tag: 'ul',
14810                 cls: 'list-group'
14811             }
14812         ]
14813     },
14814     
14815     listItemRadio : {
14816         tag: 'li',
14817         cls: 'list-group-item',
14818         cn: [
14819             {
14820                 tag: 'span',
14821                 cls: 'roo-combobox-list-group-item-value'
14822             },
14823             {
14824                 tag: 'div',
14825                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14826                 cn: [
14827                     {
14828                         tag: 'input',
14829                         type: 'radio'
14830                     },
14831                     {
14832                         tag: 'label'
14833                     }
14834                 ]
14835             }
14836         ]
14837     },
14838     
14839     listItemCheckbox : {
14840         tag: 'li',
14841         cls: 'list-group-item',
14842         cn: [
14843             {
14844                 tag: 'span',
14845                 cls: 'roo-combobox-list-group-item-value'
14846             },
14847             {
14848                 tag: 'div',
14849                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14850                 cn: [
14851                     {
14852                         tag: 'input',
14853                         type: 'checkbox'
14854                     },
14855                     {
14856                         tag: 'label'
14857                     }
14858                 ]
14859             }
14860         ]
14861     },
14862     
14863     emptyResult : {
14864         tag: 'div',
14865         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14866     },
14867     
14868     footer : {
14869         tag: 'div',
14870         cls: 'modal-footer',
14871         cn: [
14872             {
14873                 tag: 'div',
14874                 cls: 'row',
14875                 cn: [
14876                     {
14877                         tag: 'div',
14878                         cls: 'col-xs-6 text-left',
14879                         cn: {
14880                             tag: 'button',
14881                             cls: 'btn btn-danger roo-touch-view-cancel',
14882                             html: 'Cancel'
14883                         }
14884                     },
14885                     {
14886                         tag: 'div',
14887                         cls: 'col-xs-6 text-right',
14888                         cn: {
14889                             tag: 'button',
14890                             cls: 'btn btn-success roo-touch-view-ok',
14891                             html: 'OK'
14892                         }
14893                     }
14894                 ]
14895             }
14896         ]
14897         
14898     }
14899 });
14900
14901 Roo.apply(Roo.bootstrap.ComboBox,  {
14902     
14903     touchViewTemplate : {
14904         tag: 'div',
14905         cls: 'modal fade roo-combobox-touch-view',
14906         cn: [
14907             {
14908                 tag: 'div',
14909                 cls: 'modal-dialog',
14910                 style : 'position:fixed', // we have to fix position....
14911                 cn: [
14912                     {
14913                         tag: 'div',
14914                         cls: 'modal-content',
14915                         cn: [
14916                             Roo.bootstrap.ComboBox.header,
14917                             Roo.bootstrap.ComboBox.body,
14918                             Roo.bootstrap.ComboBox.footer
14919                         ]
14920                     }
14921                 ]
14922             }
14923         ]
14924     }
14925 });/*
14926  * Based on:
14927  * Ext JS Library 1.1.1
14928  * Copyright(c) 2006-2007, Ext JS, LLC.
14929  *
14930  * Originally Released Under LGPL - original licence link has changed is not relivant.
14931  *
14932  * Fork - LGPL
14933  * <script type="text/javascript">
14934  */
14935
14936 /**
14937  * @class Roo.View
14938  * @extends Roo.util.Observable
14939  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14940  * This class also supports single and multi selection modes. <br>
14941  * Create a data model bound view:
14942  <pre><code>
14943  var store = new Roo.data.Store(...);
14944
14945  var view = new Roo.View({
14946     el : "my-element",
14947     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14948  
14949     singleSelect: true,
14950     selectedClass: "ydataview-selected",
14951     store: store
14952  });
14953
14954  // listen for node click?
14955  view.on("click", function(vw, index, node, e){
14956  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14957  });
14958
14959  // load XML data
14960  dataModel.load("foobar.xml");
14961  </code></pre>
14962  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14963  * <br><br>
14964  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14965  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14966  * 
14967  * Note: old style constructor is still suported (container, template, config)
14968  * 
14969  * @constructor
14970  * Create a new View
14971  * @param {Object} config The config object
14972  * 
14973  */
14974 Roo.View = function(config, depreciated_tpl, depreciated_config){
14975     
14976     this.parent = false;
14977     
14978     if (typeof(depreciated_tpl) == 'undefined') {
14979         // new way.. - universal constructor.
14980         Roo.apply(this, config);
14981         this.el  = Roo.get(this.el);
14982     } else {
14983         // old format..
14984         this.el  = Roo.get(config);
14985         this.tpl = depreciated_tpl;
14986         Roo.apply(this, depreciated_config);
14987     }
14988     this.wrapEl  = this.el.wrap().wrap();
14989     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14990     
14991     
14992     if(typeof(this.tpl) == "string"){
14993         this.tpl = new Roo.Template(this.tpl);
14994     } else {
14995         // support xtype ctors..
14996         this.tpl = new Roo.factory(this.tpl, Roo);
14997     }
14998     
14999     
15000     this.tpl.compile();
15001     
15002     /** @private */
15003     this.addEvents({
15004         /**
15005          * @event beforeclick
15006          * Fires before a click is processed. Returns false to cancel the default action.
15007          * @param {Roo.View} this
15008          * @param {Number} index The index of the target node
15009          * @param {HTMLElement} node The target node
15010          * @param {Roo.EventObject} e The raw event object
15011          */
15012             "beforeclick" : true,
15013         /**
15014          * @event click
15015          * Fires when a template node is clicked.
15016          * @param {Roo.View} this
15017          * @param {Number} index The index of the target node
15018          * @param {HTMLElement} node The target node
15019          * @param {Roo.EventObject} e The raw event object
15020          */
15021             "click" : true,
15022         /**
15023          * @event dblclick
15024          * Fires when a template node is double clicked.
15025          * @param {Roo.View} this
15026          * @param {Number} index The index of the target node
15027          * @param {HTMLElement} node The target node
15028          * @param {Roo.EventObject} e The raw event object
15029          */
15030             "dblclick" : true,
15031         /**
15032          * @event contextmenu
15033          * Fires when a template node is right clicked.
15034          * @param {Roo.View} this
15035          * @param {Number} index The index of the target node
15036          * @param {HTMLElement} node The target node
15037          * @param {Roo.EventObject} e The raw event object
15038          */
15039             "contextmenu" : true,
15040         /**
15041          * @event selectionchange
15042          * Fires when the selected nodes change.
15043          * @param {Roo.View} this
15044          * @param {Array} selections Array of the selected nodes
15045          */
15046             "selectionchange" : true,
15047     
15048         /**
15049          * @event beforeselect
15050          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15051          * @param {Roo.View} this
15052          * @param {HTMLElement} node The node to be selected
15053          * @param {Array} selections Array of currently selected nodes
15054          */
15055             "beforeselect" : true,
15056         /**
15057          * @event preparedata
15058          * Fires on every row to render, to allow you to change the data.
15059          * @param {Roo.View} this
15060          * @param {Object} data to be rendered (change this)
15061          */
15062           "preparedata" : true
15063           
15064           
15065         });
15066
15067
15068
15069     this.el.on({
15070         "click": this.onClick,
15071         "dblclick": this.onDblClick,
15072         "contextmenu": this.onContextMenu,
15073         scope:this
15074     });
15075
15076     this.selections = [];
15077     this.nodes = [];
15078     this.cmp = new Roo.CompositeElementLite([]);
15079     if(this.store){
15080         this.store = Roo.factory(this.store, Roo.data);
15081         this.setStore(this.store, true);
15082     }
15083     
15084     if ( this.footer && this.footer.xtype) {
15085            
15086          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15087         
15088         this.footer.dataSource = this.store;
15089         this.footer.container = fctr;
15090         this.footer = Roo.factory(this.footer, Roo);
15091         fctr.insertFirst(this.el);
15092         
15093         // this is a bit insane - as the paging toolbar seems to detach the el..
15094 //        dom.parentNode.parentNode.parentNode
15095          // they get detached?
15096     }
15097     
15098     
15099     Roo.View.superclass.constructor.call(this);
15100     
15101     
15102 };
15103
15104 Roo.extend(Roo.View, Roo.util.Observable, {
15105     
15106      /**
15107      * @cfg {Roo.data.Store} store Data store to load data from.
15108      */
15109     store : false,
15110     
15111     /**
15112      * @cfg {String|Roo.Element} el The container element.
15113      */
15114     el : '',
15115     
15116     /**
15117      * @cfg {String|Roo.Template} tpl The template used by this View 
15118      */
15119     tpl : false,
15120     /**
15121      * @cfg {String} dataName the named area of the template to use as the data area
15122      *                          Works with domtemplates roo-name="name"
15123      */
15124     dataName: false,
15125     /**
15126      * @cfg {String} selectedClass The css class to add to selected nodes
15127      */
15128     selectedClass : "x-view-selected",
15129      /**
15130      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15131      */
15132     emptyText : "",
15133     
15134     /**
15135      * @cfg {String} text to display on mask (default Loading)
15136      */
15137     mask : false,
15138     /**
15139      * @cfg {Boolean} multiSelect Allow multiple selection
15140      */
15141     multiSelect : false,
15142     /**
15143      * @cfg {Boolean} singleSelect Allow single selection
15144      */
15145     singleSelect:  false,
15146     
15147     /**
15148      * @cfg {Boolean} toggleSelect - selecting 
15149      */
15150     toggleSelect : false,
15151     
15152     /**
15153      * @cfg {Boolean} tickable - selecting 
15154      */
15155     tickable : false,
15156     
15157     /**
15158      * Returns the element this view is bound to.
15159      * @return {Roo.Element}
15160      */
15161     getEl : function(){
15162         return this.wrapEl;
15163     },
15164     
15165     
15166
15167     /**
15168      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15169      */
15170     refresh : function(){
15171         //Roo.log('refresh');
15172         var t = this.tpl;
15173         
15174         // if we are using something like 'domtemplate', then
15175         // the what gets used is:
15176         // t.applySubtemplate(NAME, data, wrapping data..)
15177         // the outer template then get' applied with
15178         //     the store 'extra data'
15179         // and the body get's added to the
15180         //      roo-name="data" node?
15181         //      <span class='roo-tpl-{name}'></span> ?????
15182         
15183         
15184         
15185         this.clearSelections();
15186         this.el.update("");
15187         var html = [];
15188         var records = this.store.getRange();
15189         if(records.length < 1) {
15190             
15191             // is this valid??  = should it render a template??
15192             
15193             this.el.update(this.emptyText);
15194             return;
15195         }
15196         var el = this.el;
15197         if (this.dataName) {
15198             this.el.update(t.apply(this.store.meta)); //????
15199             el = this.el.child('.roo-tpl-' + this.dataName);
15200         }
15201         
15202         for(var i = 0, len = records.length; i < len; i++){
15203             var data = this.prepareData(records[i].data, i, records[i]);
15204             this.fireEvent("preparedata", this, data, i, records[i]);
15205             
15206             var d = Roo.apply({}, data);
15207             
15208             if(this.tickable){
15209                 Roo.apply(d, {'roo-id' : Roo.id()});
15210                 
15211                 var _this = this;
15212             
15213                 Roo.each(this.parent.item, function(item){
15214                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15215                         return;
15216                     }
15217                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15218                 });
15219             }
15220             
15221             html[html.length] = Roo.util.Format.trim(
15222                 this.dataName ?
15223                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15224                     t.apply(d)
15225             );
15226         }
15227         
15228         
15229         
15230         el.update(html.join(""));
15231         this.nodes = el.dom.childNodes;
15232         this.updateIndexes(0);
15233     },
15234     
15235
15236     /**
15237      * Function to override to reformat the data that is sent to
15238      * the template for each node.
15239      * DEPRICATED - use the preparedata event handler.
15240      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15241      * a JSON object for an UpdateManager bound view).
15242      */
15243     prepareData : function(data, index, record)
15244     {
15245         this.fireEvent("preparedata", this, data, index, record);
15246         return data;
15247     },
15248
15249     onUpdate : function(ds, record){
15250         // Roo.log('on update');   
15251         this.clearSelections();
15252         var index = this.store.indexOf(record);
15253         var n = this.nodes[index];
15254         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15255         n.parentNode.removeChild(n);
15256         this.updateIndexes(index, index);
15257     },
15258
15259     
15260     
15261 // --------- FIXME     
15262     onAdd : function(ds, records, index)
15263     {
15264         //Roo.log(['on Add', ds, records, index] );        
15265         this.clearSelections();
15266         if(this.nodes.length == 0){
15267             this.refresh();
15268             return;
15269         }
15270         var n = this.nodes[index];
15271         for(var i = 0, len = records.length; i < len; i++){
15272             var d = this.prepareData(records[i].data, i, records[i]);
15273             if(n){
15274                 this.tpl.insertBefore(n, d);
15275             }else{
15276                 
15277                 this.tpl.append(this.el, d);
15278             }
15279         }
15280         this.updateIndexes(index);
15281     },
15282
15283     onRemove : function(ds, record, index){
15284        // Roo.log('onRemove');
15285         this.clearSelections();
15286         var el = this.dataName  ?
15287             this.el.child('.roo-tpl-' + this.dataName) :
15288             this.el; 
15289         
15290         el.dom.removeChild(this.nodes[index]);
15291         this.updateIndexes(index);
15292     },
15293
15294     /**
15295      * Refresh an individual node.
15296      * @param {Number} index
15297      */
15298     refreshNode : function(index){
15299         this.onUpdate(this.store, this.store.getAt(index));
15300     },
15301
15302     updateIndexes : function(startIndex, endIndex){
15303         var ns = this.nodes;
15304         startIndex = startIndex || 0;
15305         endIndex = endIndex || ns.length - 1;
15306         for(var i = startIndex; i <= endIndex; i++){
15307             ns[i].nodeIndex = i;
15308         }
15309     },
15310
15311     /**
15312      * Changes the data store this view uses and refresh the view.
15313      * @param {Store} store
15314      */
15315     setStore : function(store, initial){
15316         if(!initial && this.store){
15317             this.store.un("datachanged", this.refresh);
15318             this.store.un("add", this.onAdd);
15319             this.store.un("remove", this.onRemove);
15320             this.store.un("update", this.onUpdate);
15321             this.store.un("clear", this.refresh);
15322             this.store.un("beforeload", this.onBeforeLoad);
15323             this.store.un("load", this.onLoad);
15324             this.store.un("loadexception", this.onLoad);
15325         }
15326         if(store){
15327           
15328             store.on("datachanged", this.refresh, this);
15329             store.on("add", this.onAdd, this);
15330             store.on("remove", this.onRemove, this);
15331             store.on("update", this.onUpdate, this);
15332             store.on("clear", this.refresh, this);
15333             store.on("beforeload", this.onBeforeLoad, this);
15334             store.on("load", this.onLoad, this);
15335             store.on("loadexception", this.onLoad, this);
15336         }
15337         
15338         if(store){
15339             this.refresh();
15340         }
15341     },
15342     /**
15343      * onbeforeLoad - masks the loading area.
15344      *
15345      */
15346     onBeforeLoad : function(store,opts)
15347     {
15348          //Roo.log('onBeforeLoad');   
15349         if (!opts.add) {
15350             this.el.update("");
15351         }
15352         this.el.mask(this.mask ? this.mask : "Loading" ); 
15353     },
15354     onLoad : function ()
15355     {
15356         this.el.unmask();
15357     },
15358     
15359
15360     /**
15361      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15362      * @param {HTMLElement} node
15363      * @return {HTMLElement} The template node
15364      */
15365     findItemFromChild : function(node){
15366         var el = this.dataName  ?
15367             this.el.child('.roo-tpl-' + this.dataName,true) :
15368             this.el.dom; 
15369         
15370         if(!node || node.parentNode == el){
15371                     return node;
15372             }
15373             var p = node.parentNode;
15374             while(p && p != el){
15375             if(p.parentNode == el){
15376                 return p;
15377             }
15378             p = p.parentNode;
15379         }
15380             return null;
15381     },
15382
15383     /** @ignore */
15384     onClick : function(e){
15385         var item = this.findItemFromChild(e.getTarget());
15386         if(item){
15387             var index = this.indexOf(item);
15388             if(this.onItemClick(item, index, e) !== false){
15389                 this.fireEvent("click", this, index, item, e);
15390             }
15391         }else{
15392             this.clearSelections();
15393         }
15394     },
15395
15396     /** @ignore */
15397     onContextMenu : function(e){
15398         var item = this.findItemFromChild(e.getTarget());
15399         if(item){
15400             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15401         }
15402     },
15403
15404     /** @ignore */
15405     onDblClick : function(e){
15406         var item = this.findItemFromChild(e.getTarget());
15407         if(item){
15408             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15409         }
15410     },
15411
15412     onItemClick : function(item, index, e)
15413     {
15414         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15415             return false;
15416         }
15417         if (this.toggleSelect) {
15418             var m = this.isSelected(item) ? 'unselect' : 'select';
15419             //Roo.log(m);
15420             var _t = this;
15421             _t[m](item, true, false);
15422             return true;
15423         }
15424         if(this.multiSelect || this.singleSelect){
15425             if(this.multiSelect && e.shiftKey && this.lastSelection){
15426                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15427             }else{
15428                 this.select(item, this.multiSelect && e.ctrlKey);
15429                 this.lastSelection = item;
15430             }
15431             
15432             if(!this.tickable){
15433                 e.preventDefault();
15434             }
15435             
15436         }
15437         return true;
15438     },
15439
15440     /**
15441      * Get the number of selected nodes.
15442      * @return {Number}
15443      */
15444     getSelectionCount : function(){
15445         return this.selections.length;
15446     },
15447
15448     /**
15449      * Get the currently selected nodes.
15450      * @return {Array} An array of HTMLElements
15451      */
15452     getSelectedNodes : function(){
15453         return this.selections;
15454     },
15455
15456     /**
15457      * Get the indexes of the selected nodes.
15458      * @return {Array}
15459      */
15460     getSelectedIndexes : function(){
15461         var indexes = [], s = this.selections;
15462         for(var i = 0, len = s.length; i < len; i++){
15463             indexes.push(s[i].nodeIndex);
15464         }
15465         return indexes;
15466     },
15467
15468     /**
15469      * Clear all selections
15470      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15471      */
15472     clearSelections : function(suppressEvent){
15473         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15474             this.cmp.elements = this.selections;
15475             this.cmp.removeClass(this.selectedClass);
15476             this.selections = [];
15477             if(!suppressEvent){
15478                 this.fireEvent("selectionchange", this, this.selections);
15479             }
15480         }
15481     },
15482
15483     /**
15484      * Returns true if the passed node is selected
15485      * @param {HTMLElement/Number} node The node or node index
15486      * @return {Boolean}
15487      */
15488     isSelected : function(node){
15489         var s = this.selections;
15490         if(s.length < 1){
15491             return false;
15492         }
15493         node = this.getNode(node);
15494         return s.indexOf(node) !== -1;
15495     },
15496
15497     /**
15498      * Selects nodes.
15499      * @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
15500      * @param {Boolean} keepExisting (optional) true to keep existing selections
15501      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15502      */
15503     select : function(nodeInfo, keepExisting, suppressEvent){
15504         if(nodeInfo instanceof Array){
15505             if(!keepExisting){
15506                 this.clearSelections(true);
15507             }
15508             for(var i = 0, len = nodeInfo.length; i < len; i++){
15509                 this.select(nodeInfo[i], true, true);
15510             }
15511             return;
15512         } 
15513         var node = this.getNode(nodeInfo);
15514         if(!node || this.isSelected(node)){
15515             return; // already selected.
15516         }
15517         if(!keepExisting){
15518             this.clearSelections(true);
15519         }
15520         
15521         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15522             Roo.fly(node).addClass(this.selectedClass);
15523             this.selections.push(node);
15524             if(!suppressEvent){
15525                 this.fireEvent("selectionchange", this, this.selections);
15526             }
15527         }
15528         
15529         
15530     },
15531       /**
15532      * Unselects nodes.
15533      * @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
15534      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15536      */
15537     unselect : function(nodeInfo, keepExisting, suppressEvent)
15538     {
15539         if(nodeInfo instanceof Array){
15540             Roo.each(this.selections, function(s) {
15541                 this.unselect(s, nodeInfo);
15542             }, this);
15543             return;
15544         }
15545         var node = this.getNode(nodeInfo);
15546         if(!node || !this.isSelected(node)){
15547             //Roo.log("not selected");
15548             return; // not selected.
15549         }
15550         // fireevent???
15551         var ns = [];
15552         Roo.each(this.selections, function(s) {
15553             if (s == node ) {
15554                 Roo.fly(node).removeClass(this.selectedClass);
15555
15556                 return;
15557             }
15558             ns.push(s);
15559         },this);
15560         
15561         this.selections= ns;
15562         this.fireEvent("selectionchange", this, this.selections);
15563     },
15564
15565     /**
15566      * Gets a template node.
15567      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15568      * @return {HTMLElement} The node or null if it wasn't found
15569      */
15570     getNode : function(nodeInfo){
15571         if(typeof nodeInfo == "string"){
15572             return document.getElementById(nodeInfo);
15573         }else if(typeof nodeInfo == "number"){
15574             return this.nodes[nodeInfo];
15575         }
15576         return nodeInfo;
15577     },
15578
15579     /**
15580      * Gets a range template nodes.
15581      * @param {Number} startIndex
15582      * @param {Number} endIndex
15583      * @return {Array} An array of nodes
15584      */
15585     getNodes : function(start, end){
15586         var ns = this.nodes;
15587         start = start || 0;
15588         end = typeof end == "undefined" ? ns.length - 1 : end;
15589         var nodes = [];
15590         if(start <= end){
15591             for(var i = start; i <= end; i++){
15592                 nodes.push(ns[i]);
15593             }
15594         } else{
15595             for(var i = start; i >= end; i--){
15596                 nodes.push(ns[i]);
15597             }
15598         }
15599         return nodes;
15600     },
15601
15602     /**
15603      * Finds the index of the passed node
15604      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15605      * @return {Number} The index of the node or -1
15606      */
15607     indexOf : function(node){
15608         node = this.getNode(node);
15609         if(typeof node.nodeIndex == "number"){
15610             return node.nodeIndex;
15611         }
15612         var ns = this.nodes;
15613         for(var i = 0, len = ns.length; i < len; i++){
15614             if(ns[i] == node){
15615                 return i;
15616             }
15617         }
15618         return -1;
15619     }
15620 });
15621 /*
15622  * - LGPL
15623  *
15624  * based on jquery fullcalendar
15625  * 
15626  */
15627
15628 Roo.bootstrap = Roo.bootstrap || {};
15629 /**
15630  * @class Roo.bootstrap.Calendar
15631  * @extends Roo.bootstrap.Component
15632  * Bootstrap Calendar class
15633  * @cfg {Boolean} loadMask (true|false) default false
15634  * @cfg {Object} header generate the user specific header of the calendar, default false
15635
15636  * @constructor
15637  * Create a new Container
15638  * @param {Object} config The config object
15639  */
15640
15641
15642
15643 Roo.bootstrap.Calendar = function(config){
15644     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15645      this.addEvents({
15646         /**
15647              * @event select
15648              * Fires when a date is selected
15649              * @param {DatePicker} this
15650              * @param {Date} date The selected date
15651              */
15652         'select': true,
15653         /**
15654              * @event monthchange
15655              * Fires when the displayed month changes 
15656              * @param {DatePicker} this
15657              * @param {Date} date The selected month
15658              */
15659         'monthchange': true,
15660         /**
15661              * @event evententer
15662              * Fires when mouse over an event
15663              * @param {Calendar} this
15664              * @param {event} Event
15665              */
15666         'evententer': true,
15667         /**
15668              * @event eventleave
15669              * Fires when the mouse leaves an
15670              * @param {Calendar} this
15671              * @param {event}
15672              */
15673         'eventleave': true,
15674         /**
15675              * @event eventclick
15676              * Fires when the mouse click an
15677              * @param {Calendar} this
15678              * @param {event}
15679              */
15680         'eventclick': true
15681         
15682     });
15683
15684 };
15685
15686 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15687     
15688      /**
15689      * @cfg {Number} startDay
15690      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15691      */
15692     startDay : 0,
15693     
15694     loadMask : false,
15695     
15696     header : false,
15697       
15698     getAutoCreate : function(){
15699         
15700         
15701         var fc_button = function(name, corner, style, content ) {
15702             return Roo.apply({},{
15703                 tag : 'span',
15704                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15705                          (corner.length ?
15706                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15707                             ''
15708                         ),
15709                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15710                 unselectable: 'on'
15711             });
15712         };
15713         
15714         var header = {};
15715         
15716         if(!this.header){
15717             header = {
15718                 tag : 'table',
15719                 cls : 'fc-header',
15720                 style : 'width:100%',
15721                 cn : [
15722                     {
15723                         tag: 'tr',
15724                         cn : [
15725                             {
15726                                 tag : 'td',
15727                                 cls : 'fc-header-left',
15728                                 cn : [
15729                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15730                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15731                                     { tag: 'span', cls: 'fc-header-space' },
15732                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15733
15734
15735                                 ]
15736                             },
15737
15738                             {
15739                                 tag : 'td',
15740                                 cls : 'fc-header-center',
15741                                 cn : [
15742                                     {
15743                                         tag: 'span',
15744                                         cls: 'fc-header-title',
15745                                         cn : {
15746                                             tag: 'H2',
15747                                             html : 'month / year'
15748                                         }
15749                                     }
15750
15751                                 ]
15752                             },
15753                             {
15754                                 tag : 'td',
15755                                 cls : 'fc-header-right',
15756                                 cn : [
15757                               /*      fc_button('month', 'left', '', 'month' ),
15758                                     fc_button('week', '', '', 'week' ),
15759                                     fc_button('day', 'right', '', 'day' )
15760                                 */    
15761
15762                                 ]
15763                             }
15764
15765                         ]
15766                     }
15767                 ]
15768             };
15769         }
15770         
15771         header = this.header;
15772         
15773        
15774         var cal_heads = function() {
15775             var ret = [];
15776             // fixme - handle this.
15777             
15778             for (var i =0; i < Date.dayNames.length; i++) {
15779                 var d = Date.dayNames[i];
15780                 ret.push({
15781                     tag: 'th',
15782                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15783                     html : d.substring(0,3)
15784                 });
15785                 
15786             }
15787             ret[0].cls += ' fc-first';
15788             ret[6].cls += ' fc-last';
15789             return ret;
15790         };
15791         var cal_cell = function(n) {
15792             return  {
15793                 tag: 'td',
15794                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15795                 cn : [
15796                     {
15797                         cn : [
15798                             {
15799                                 cls: 'fc-day-number',
15800                                 html: 'D'
15801                             },
15802                             {
15803                                 cls: 'fc-day-content',
15804                              
15805                                 cn : [
15806                                      {
15807                                         style: 'position: relative;' // height: 17px;
15808                                     }
15809                                 ]
15810                             }
15811                             
15812                             
15813                         ]
15814                     }
15815                 ]
15816                 
15817             }
15818         };
15819         var cal_rows = function() {
15820             
15821             var ret = [];
15822             for (var r = 0; r < 6; r++) {
15823                 var row= {
15824                     tag : 'tr',
15825                     cls : 'fc-week',
15826                     cn : []
15827                 };
15828                 
15829                 for (var i =0; i < Date.dayNames.length; i++) {
15830                     var d = Date.dayNames[i];
15831                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15832
15833                 }
15834                 row.cn[0].cls+=' fc-first';
15835                 row.cn[0].cn[0].style = 'min-height:90px';
15836                 row.cn[6].cls+=' fc-last';
15837                 ret.push(row);
15838                 
15839             }
15840             ret[0].cls += ' fc-first';
15841             ret[4].cls += ' fc-prev-last';
15842             ret[5].cls += ' fc-last';
15843             return ret;
15844             
15845         };
15846         
15847         var cal_table = {
15848             tag: 'table',
15849             cls: 'fc-border-separate',
15850             style : 'width:100%',
15851             cellspacing  : 0,
15852             cn : [
15853                 { 
15854                     tag: 'thead',
15855                     cn : [
15856                         { 
15857                             tag: 'tr',
15858                             cls : 'fc-first fc-last',
15859                             cn : cal_heads()
15860                         }
15861                     ]
15862                 },
15863                 { 
15864                     tag: 'tbody',
15865                     cn : cal_rows()
15866                 }
15867                   
15868             ]
15869         };
15870          
15871          var cfg = {
15872             cls : 'fc fc-ltr',
15873             cn : [
15874                 header,
15875                 {
15876                     cls : 'fc-content',
15877                     style : "position: relative;",
15878                     cn : [
15879                         {
15880                             cls : 'fc-view fc-view-month fc-grid',
15881                             style : 'position: relative',
15882                             unselectable : 'on',
15883                             cn : [
15884                                 {
15885                                     cls : 'fc-event-container',
15886                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15887                                 },
15888                                 cal_table
15889                             ]
15890                         }
15891                     ]
15892     
15893                 }
15894            ] 
15895             
15896         };
15897         
15898          
15899         
15900         return cfg;
15901     },
15902     
15903     
15904     initEvents : function()
15905     {
15906         if(!this.store){
15907             throw "can not find store for calendar";
15908         }
15909         
15910         var mark = {
15911             tag: "div",
15912             cls:"x-dlg-mask",
15913             style: "text-align:center",
15914             cn: [
15915                 {
15916                     tag: "div",
15917                     style: "background-color:white;width:50%;margin:250 auto",
15918                     cn: [
15919                         {
15920                             tag: "img",
15921                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15922                         },
15923                         {
15924                             tag: "span",
15925                             html: "Loading"
15926                         }
15927                         
15928                     ]
15929                 }
15930             ]
15931         };
15932         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15933         
15934         var size = this.el.select('.fc-content', true).first().getSize();
15935         this.maskEl.setSize(size.width, size.height);
15936         this.maskEl.enableDisplayMode("block");
15937         if(!this.loadMask){
15938             this.maskEl.hide();
15939         }
15940         
15941         this.store = Roo.factory(this.store, Roo.data);
15942         this.store.on('load', this.onLoad, this);
15943         this.store.on('beforeload', this.onBeforeLoad, this);
15944         
15945         this.resize();
15946         
15947         this.cells = this.el.select('.fc-day',true);
15948         //Roo.log(this.cells);
15949         this.textNodes = this.el.query('.fc-day-number');
15950         this.cells.addClassOnOver('fc-state-hover');
15951         
15952         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15953         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15954         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15955         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15956         
15957         this.on('monthchange', this.onMonthChange, this);
15958         
15959         this.update(new Date().clearTime());
15960     },
15961     
15962     resize : function() {
15963         var sz  = this.el.getSize();
15964         
15965         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15966         this.el.select('.fc-day-content div',true).setHeight(34);
15967     },
15968     
15969     
15970     // private
15971     showPrevMonth : function(e){
15972         this.update(this.activeDate.add("mo", -1));
15973     },
15974     showToday : function(e){
15975         this.update(new Date().clearTime());
15976     },
15977     // private
15978     showNextMonth : function(e){
15979         this.update(this.activeDate.add("mo", 1));
15980     },
15981
15982     // private
15983     showPrevYear : function(){
15984         this.update(this.activeDate.add("y", -1));
15985     },
15986
15987     // private
15988     showNextYear : function(){
15989         this.update(this.activeDate.add("y", 1));
15990     },
15991
15992     
15993    // private
15994     update : function(date)
15995     {
15996         var vd = this.activeDate;
15997         this.activeDate = date;
15998 //        if(vd && this.el){
15999 //            var t = date.getTime();
16000 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16001 //                Roo.log('using add remove');
16002 //                
16003 //                this.fireEvent('monthchange', this, date);
16004 //                
16005 //                this.cells.removeClass("fc-state-highlight");
16006 //                this.cells.each(function(c){
16007 //                   if(c.dateValue == t){
16008 //                       c.addClass("fc-state-highlight");
16009 //                       setTimeout(function(){
16010 //                            try{c.dom.firstChild.focus();}catch(e){}
16011 //                       }, 50);
16012 //                       return false;
16013 //                   }
16014 //                   return true;
16015 //                });
16016 //                return;
16017 //            }
16018 //        }
16019         
16020         var days = date.getDaysInMonth();
16021         
16022         var firstOfMonth = date.getFirstDateOfMonth();
16023         var startingPos = firstOfMonth.getDay()-this.startDay;
16024         
16025         if(startingPos < this.startDay){
16026             startingPos += 7;
16027         }
16028         
16029         var pm = date.add(Date.MONTH, -1);
16030         var prevStart = pm.getDaysInMonth()-startingPos;
16031 //        
16032         this.cells = this.el.select('.fc-day',true);
16033         this.textNodes = this.el.query('.fc-day-number');
16034         this.cells.addClassOnOver('fc-state-hover');
16035         
16036         var cells = this.cells.elements;
16037         var textEls = this.textNodes;
16038         
16039         Roo.each(cells, function(cell){
16040             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16041         });
16042         
16043         days += startingPos;
16044
16045         // convert everything to numbers so it's fast
16046         var day = 86400000;
16047         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16048         //Roo.log(d);
16049         //Roo.log(pm);
16050         //Roo.log(prevStart);
16051         
16052         var today = new Date().clearTime().getTime();
16053         var sel = date.clearTime().getTime();
16054         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16055         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16056         var ddMatch = this.disabledDatesRE;
16057         var ddText = this.disabledDatesText;
16058         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16059         var ddaysText = this.disabledDaysText;
16060         var format = this.format;
16061         
16062         var setCellClass = function(cal, cell){
16063             cell.row = 0;
16064             cell.events = [];
16065             cell.more = [];
16066             //Roo.log('set Cell Class');
16067             cell.title = "";
16068             var t = d.getTime();
16069             
16070             //Roo.log(d);
16071             
16072             cell.dateValue = t;
16073             if(t == today){
16074                 cell.className += " fc-today";
16075                 cell.className += " fc-state-highlight";
16076                 cell.title = cal.todayText;
16077             }
16078             if(t == sel){
16079                 // disable highlight in other month..
16080                 //cell.className += " fc-state-highlight";
16081                 
16082             }
16083             // disabling
16084             if(t < min) {
16085                 cell.className = " fc-state-disabled";
16086                 cell.title = cal.minText;
16087                 return;
16088             }
16089             if(t > max) {
16090                 cell.className = " fc-state-disabled";
16091                 cell.title = cal.maxText;
16092                 return;
16093             }
16094             if(ddays){
16095                 if(ddays.indexOf(d.getDay()) != -1){
16096                     cell.title = ddaysText;
16097                     cell.className = " fc-state-disabled";
16098                 }
16099             }
16100             if(ddMatch && format){
16101                 var fvalue = d.dateFormat(format);
16102                 if(ddMatch.test(fvalue)){
16103                     cell.title = ddText.replace("%0", fvalue);
16104                     cell.className = " fc-state-disabled";
16105                 }
16106             }
16107             
16108             if (!cell.initialClassName) {
16109                 cell.initialClassName = cell.dom.className;
16110             }
16111             
16112             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16113         };
16114
16115         var i = 0;
16116         
16117         for(; i < startingPos; i++) {
16118             textEls[i].innerHTML = (++prevStart);
16119             d.setDate(d.getDate()+1);
16120             
16121             cells[i].className = "fc-past fc-other-month";
16122             setCellClass(this, cells[i]);
16123         }
16124         
16125         var intDay = 0;
16126         
16127         for(; i < days; i++){
16128             intDay = i - startingPos + 1;
16129             textEls[i].innerHTML = (intDay);
16130             d.setDate(d.getDate()+1);
16131             
16132             cells[i].className = ''; // "x-date-active";
16133             setCellClass(this, cells[i]);
16134         }
16135         var extraDays = 0;
16136         
16137         for(; i < 42; i++) {
16138             textEls[i].innerHTML = (++extraDays);
16139             d.setDate(d.getDate()+1);
16140             
16141             cells[i].className = "fc-future fc-other-month";
16142             setCellClass(this, cells[i]);
16143         }
16144         
16145         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16146         
16147         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16148         
16149         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16150         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16151         
16152         if(totalRows != 6){
16153             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16154             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16155         }
16156         
16157         this.fireEvent('monthchange', this, date);
16158         
16159         
16160         /*
16161         if(!this.internalRender){
16162             var main = this.el.dom.firstChild;
16163             var w = main.offsetWidth;
16164             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16165             Roo.fly(main).setWidth(w);
16166             this.internalRender = true;
16167             // opera does not respect the auto grow header center column
16168             // then, after it gets a width opera refuses to recalculate
16169             // without a second pass
16170             if(Roo.isOpera && !this.secondPass){
16171                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16172                 this.secondPass = true;
16173                 this.update.defer(10, this, [date]);
16174             }
16175         }
16176         */
16177         
16178     },
16179     
16180     findCell : function(dt) {
16181         dt = dt.clearTime().getTime();
16182         var ret = false;
16183         this.cells.each(function(c){
16184             //Roo.log("check " +c.dateValue + '?=' + dt);
16185             if(c.dateValue == dt){
16186                 ret = c;
16187                 return false;
16188             }
16189             return true;
16190         });
16191         
16192         return ret;
16193     },
16194     
16195     findCells : function(ev) {
16196         var s = ev.start.clone().clearTime().getTime();
16197        // Roo.log(s);
16198         var e= ev.end.clone().clearTime().getTime();
16199        // Roo.log(e);
16200         var ret = [];
16201         this.cells.each(function(c){
16202              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16203             
16204             if(c.dateValue > e){
16205                 return ;
16206             }
16207             if(c.dateValue < s){
16208                 return ;
16209             }
16210             ret.push(c);
16211         });
16212         
16213         return ret;    
16214     },
16215     
16216 //    findBestRow: function(cells)
16217 //    {
16218 //        var ret = 0;
16219 //        
16220 //        for (var i =0 ; i < cells.length;i++) {
16221 //            ret  = Math.max(cells[i].rows || 0,ret);
16222 //        }
16223 //        return ret;
16224 //        
16225 //    },
16226     
16227     
16228     addItem : function(ev)
16229     {
16230         // look for vertical location slot in
16231         var cells = this.findCells(ev);
16232         
16233 //        ev.row = this.findBestRow(cells);
16234         
16235         // work out the location.
16236         
16237         var crow = false;
16238         var rows = [];
16239         for(var i =0; i < cells.length; i++) {
16240             
16241             cells[i].row = cells[0].row;
16242             
16243             if(i == 0){
16244                 cells[i].row = cells[i].row + 1;
16245             }
16246             
16247             if (!crow) {
16248                 crow = {
16249                     start : cells[i],
16250                     end :  cells[i]
16251                 };
16252                 continue;
16253             }
16254             if (crow.start.getY() == cells[i].getY()) {
16255                 // on same row.
16256                 crow.end = cells[i];
16257                 continue;
16258             }
16259             // different row.
16260             rows.push(crow);
16261             crow = {
16262                 start: cells[i],
16263                 end : cells[i]
16264             };
16265             
16266         }
16267         
16268         rows.push(crow);
16269         ev.els = [];
16270         ev.rows = rows;
16271         ev.cells = cells;
16272         
16273         cells[0].events.push(ev);
16274         
16275         this.calevents.push(ev);
16276     },
16277     
16278     clearEvents: function() {
16279         
16280         if(!this.calevents){
16281             return;
16282         }
16283         
16284         Roo.each(this.cells.elements, function(c){
16285             c.row = 0;
16286             c.events = [];
16287             c.more = [];
16288         });
16289         
16290         Roo.each(this.calevents, function(e) {
16291             Roo.each(e.els, function(el) {
16292                 el.un('mouseenter' ,this.onEventEnter, this);
16293                 el.un('mouseleave' ,this.onEventLeave, this);
16294                 el.remove();
16295             },this);
16296         },this);
16297         
16298         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16299             e.remove();
16300         });
16301         
16302     },
16303     
16304     renderEvents: function()
16305     {   
16306         var _this = this;
16307         
16308         this.cells.each(function(c) {
16309             
16310             if(c.row < 5){
16311                 return;
16312             }
16313             
16314             var ev = c.events;
16315             
16316             var r = 4;
16317             if(c.row != c.events.length){
16318                 r = 4 - (4 - (c.row - c.events.length));
16319             }
16320             
16321             c.events = ev.slice(0, r);
16322             c.more = ev.slice(r);
16323             
16324             if(c.more.length && c.more.length == 1){
16325                 c.events.push(c.more.pop());
16326             }
16327             
16328             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16329             
16330         });
16331             
16332         this.cells.each(function(c) {
16333             
16334             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16335             
16336             
16337             for (var e = 0; e < c.events.length; e++){
16338                 var ev = c.events[e];
16339                 var rows = ev.rows;
16340                 
16341                 for(var i = 0; i < rows.length; i++) {
16342                 
16343                     // how many rows should it span..
16344
16345                     var  cfg = {
16346                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16347                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16348
16349                         unselectable : "on",
16350                         cn : [
16351                             {
16352                                 cls: 'fc-event-inner',
16353                                 cn : [
16354     //                                {
16355     //                                  tag:'span',
16356     //                                  cls: 'fc-event-time',
16357     //                                  html : cells.length > 1 ? '' : ev.time
16358     //                                },
16359                                     {
16360                                       tag:'span',
16361                                       cls: 'fc-event-title',
16362                                       html : String.format('{0}', ev.title)
16363                                     }
16364
16365
16366                                 ]
16367                             },
16368                             {
16369                                 cls: 'ui-resizable-handle ui-resizable-e',
16370                                 html : '&nbsp;&nbsp;&nbsp'
16371                             }
16372
16373                         ]
16374                     };
16375
16376                     if (i == 0) {
16377                         cfg.cls += ' fc-event-start';
16378                     }
16379                     if ((i+1) == rows.length) {
16380                         cfg.cls += ' fc-event-end';
16381                     }
16382
16383                     var ctr = _this.el.select('.fc-event-container',true).first();
16384                     var cg = ctr.createChild(cfg);
16385
16386                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16387                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16388
16389                     var r = (c.more.length) ? 1 : 0;
16390                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16391                     cg.setWidth(ebox.right - sbox.x -2);
16392
16393                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16394                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16395                     cg.on('click', _this.onEventClick, _this, ev);
16396
16397                     ev.els.push(cg);
16398                     
16399                 }
16400                 
16401             }
16402             
16403             
16404             if(c.more.length){
16405                 var  cfg = {
16406                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16407                     style : 'position: absolute',
16408                     unselectable : "on",
16409                     cn : [
16410                         {
16411                             cls: 'fc-event-inner',
16412                             cn : [
16413                                 {
16414                                   tag:'span',
16415                                   cls: 'fc-event-title',
16416                                   html : 'More'
16417                                 }
16418
16419
16420                             ]
16421                         },
16422                         {
16423                             cls: 'ui-resizable-handle ui-resizable-e',
16424                             html : '&nbsp;&nbsp;&nbsp'
16425                         }
16426
16427                     ]
16428                 };
16429
16430                 var ctr = _this.el.select('.fc-event-container',true).first();
16431                 var cg = ctr.createChild(cfg);
16432
16433                 var sbox = c.select('.fc-day-content',true).first().getBox();
16434                 var ebox = c.select('.fc-day-content',true).first().getBox();
16435                 //Roo.log(cg);
16436                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16437                 cg.setWidth(ebox.right - sbox.x -2);
16438
16439                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16440                 
16441             }
16442             
16443         });
16444         
16445         
16446         
16447     },
16448     
16449     onEventEnter: function (e, el,event,d) {
16450         this.fireEvent('evententer', this, el, event);
16451     },
16452     
16453     onEventLeave: function (e, el,event,d) {
16454         this.fireEvent('eventleave', this, el, event);
16455     },
16456     
16457     onEventClick: function (e, el,event,d) {
16458         this.fireEvent('eventclick', this, el, event);
16459     },
16460     
16461     onMonthChange: function () {
16462         this.store.load();
16463     },
16464     
16465     onMoreEventClick: function(e, el, more)
16466     {
16467         var _this = this;
16468         
16469         this.calpopover.placement = 'right';
16470         this.calpopover.setTitle('More');
16471         
16472         this.calpopover.setContent('');
16473         
16474         var ctr = this.calpopover.el.select('.popover-content', true).first();
16475         
16476         Roo.each(more, function(m){
16477             var cfg = {
16478                 cls : 'fc-event-hori fc-event-draggable',
16479                 html : m.title
16480             };
16481             var cg = ctr.createChild(cfg);
16482             
16483             cg.on('click', _this.onEventClick, _this, m);
16484         });
16485         
16486         this.calpopover.show(el);
16487         
16488         
16489     },
16490     
16491     onLoad: function () 
16492     {   
16493         this.calevents = [];
16494         var cal = this;
16495         
16496         if(this.store.getCount() > 0){
16497             this.store.data.each(function(d){
16498                cal.addItem({
16499                     id : d.data.id,
16500                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16501                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16502                     time : d.data.start_time,
16503                     title : d.data.title,
16504                     description : d.data.description,
16505                     venue : d.data.venue
16506                 });
16507             });
16508         }
16509         
16510         this.renderEvents();
16511         
16512         if(this.calevents.length && this.loadMask){
16513             this.maskEl.hide();
16514         }
16515     },
16516     
16517     onBeforeLoad: function()
16518     {
16519         this.clearEvents();
16520         if(this.loadMask){
16521             this.maskEl.show();
16522         }
16523     }
16524 });
16525
16526  
16527  /*
16528  * - LGPL
16529  *
16530  * element
16531  * 
16532  */
16533
16534 /**
16535  * @class Roo.bootstrap.Popover
16536  * @extends Roo.bootstrap.Component
16537  * Bootstrap Popover class
16538  * @cfg {String} html contents of the popover   (or false to use children..)
16539  * @cfg {String} title of popover (or false to hide)
16540  * @cfg {String} placement how it is placed
16541  * @cfg {String} trigger click || hover (or false to trigger manually)
16542  * @cfg {String} over what (parent or false to trigger manually.)
16543  * @cfg {Number} delay - delay before showing
16544  
16545  * @constructor
16546  * Create a new Popover
16547  * @param {Object} config The config object
16548  */
16549
16550 Roo.bootstrap.Popover = function(config){
16551     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16552     
16553     this.addEvents({
16554         // raw events
16555          /**
16556          * @event show
16557          * After the popover show
16558          * 
16559          * @param {Roo.bootstrap.Popover} this
16560          */
16561         "show" : true,
16562         /**
16563          * @event hide
16564          * After the popover hide
16565          * 
16566          * @param {Roo.bootstrap.Popover} this
16567          */
16568         "hide" : true
16569     });
16570 };
16571
16572 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16573     
16574     title: 'Fill in a title',
16575     html: false,
16576     
16577     placement : 'right',
16578     trigger : 'hover', // hover
16579     
16580     delay : 0,
16581     
16582     over: 'parent',
16583     
16584     can_build_overlaid : false,
16585     
16586     getChildContainer : function()
16587     {
16588         return this.el.select('.popover-content',true).first();
16589     },
16590     
16591     getAutoCreate : function(){
16592          
16593         var cfg = {
16594            cls : 'popover roo-dynamic',
16595            style: 'display:block',
16596            cn : [
16597                 {
16598                     cls : 'arrow'
16599                 },
16600                 {
16601                     cls : 'popover-inner',
16602                     cn : [
16603                         {
16604                             tag: 'h3',
16605                             cls: 'popover-title',
16606                             html : this.title
16607                         },
16608                         {
16609                             cls : 'popover-content',
16610                             html : this.html
16611                         }
16612                     ]
16613                     
16614                 }
16615            ]
16616         };
16617         
16618         return cfg;
16619     },
16620     setTitle: function(str)
16621     {
16622         this.title = str;
16623         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16624     },
16625     setContent: function(str)
16626     {
16627         this.html = str;
16628         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16629     },
16630     // as it get's added to the bottom of the page.
16631     onRender : function(ct, position)
16632     {
16633         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16634         if(!this.el){
16635             var cfg = Roo.apply({},  this.getAutoCreate());
16636             cfg.id = Roo.id();
16637             
16638             if (this.cls) {
16639                 cfg.cls += ' ' + this.cls;
16640             }
16641             if (this.style) {
16642                 cfg.style = this.style;
16643             }
16644             //Roo.log("adding to ");
16645             this.el = Roo.get(document.body).createChild(cfg, position);
16646 //            Roo.log(this.el);
16647         }
16648         this.initEvents();
16649     },
16650     
16651     initEvents : function()
16652     {
16653         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16654         this.el.enableDisplayMode('block');
16655         this.el.hide();
16656         if (this.over === false) {
16657             return; 
16658         }
16659         if (this.triggers === false) {
16660             return;
16661         }
16662         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16663         var triggers = this.trigger ? this.trigger.split(' ') : [];
16664         Roo.each(triggers, function(trigger) {
16665         
16666             if (trigger == 'click') {
16667                 on_el.on('click', this.toggle, this);
16668             } else if (trigger != 'manual') {
16669                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16670                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16671       
16672                 on_el.on(eventIn  ,this.enter, this);
16673                 on_el.on(eventOut, this.leave, this);
16674             }
16675         }, this);
16676         
16677     },
16678     
16679     
16680     // private
16681     timeout : null,
16682     hoverState : null,
16683     
16684     toggle : function () {
16685         this.hoverState == 'in' ? this.leave() : this.enter();
16686     },
16687     
16688     enter : function () {
16689         
16690         clearTimeout(this.timeout);
16691     
16692         this.hoverState = 'in';
16693     
16694         if (!this.delay || !this.delay.show) {
16695             this.show();
16696             return;
16697         }
16698         var _t = this;
16699         this.timeout = setTimeout(function () {
16700             if (_t.hoverState == 'in') {
16701                 _t.show();
16702             }
16703         }, this.delay.show)
16704     },
16705     
16706     leave : function() {
16707         clearTimeout(this.timeout);
16708     
16709         this.hoverState = 'out';
16710     
16711         if (!this.delay || !this.delay.hide) {
16712             this.hide();
16713             return;
16714         }
16715         var _t = this;
16716         this.timeout = setTimeout(function () {
16717             if (_t.hoverState == 'out') {
16718                 _t.hide();
16719             }
16720         }, this.delay.hide)
16721     },
16722     
16723     show : function (on_el)
16724     {
16725         if (!on_el) {
16726             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16727         }
16728         
16729         // set content.
16730         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16731         if (this.html !== false) {
16732             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16733         }
16734         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16735         if (!this.title.length) {
16736             this.el.select('.popover-title',true).hide();
16737         }
16738         
16739         var placement = typeof this.placement == 'function' ?
16740             this.placement.call(this, this.el, on_el) :
16741             this.placement;
16742             
16743         var autoToken = /\s?auto?\s?/i;
16744         var autoPlace = autoToken.test(placement);
16745         if (autoPlace) {
16746             placement = placement.replace(autoToken, '') || 'top';
16747         }
16748         
16749         //this.el.detach()
16750         //this.el.setXY([0,0]);
16751         this.el.show();
16752         this.el.dom.style.display='block';
16753         this.el.addClass(placement);
16754         
16755         //this.el.appendTo(on_el);
16756         
16757         var p = this.getPosition();
16758         var box = this.el.getBox();
16759         
16760         if (autoPlace) {
16761             // fixme..
16762         }
16763         var align = Roo.bootstrap.Popover.alignment[placement];
16764         this.el.alignTo(on_el, align[0],align[1]);
16765         //var arrow = this.el.select('.arrow',true).first();
16766         //arrow.set(align[2], 
16767         
16768         this.el.addClass('in');
16769         
16770         
16771         if (this.el.hasClass('fade')) {
16772             // fade it?
16773         }
16774         
16775         this.hoverState = 'in';
16776         
16777         this.fireEvent('show', this);
16778         
16779     },
16780     hide : function()
16781     {
16782         this.el.setXY([0,0]);
16783         this.el.removeClass('in');
16784         this.el.hide();
16785         this.hoverState = null;
16786         
16787         this.fireEvent('hide', this);
16788     }
16789     
16790 });
16791
16792 Roo.bootstrap.Popover.alignment = {
16793     'left' : ['r-l', [-10,0], 'right'],
16794     'right' : ['l-r', [10,0], 'left'],
16795     'bottom' : ['t-b', [0,10], 'top'],
16796     'top' : [ 'b-t', [0,-10], 'bottom']
16797 };
16798
16799  /*
16800  * - LGPL
16801  *
16802  * Progress
16803  * 
16804  */
16805
16806 /**
16807  * @class Roo.bootstrap.Progress
16808  * @extends Roo.bootstrap.Component
16809  * Bootstrap Progress class
16810  * @cfg {Boolean} striped striped of the progress bar
16811  * @cfg {Boolean} active animated of the progress bar
16812  * 
16813  * 
16814  * @constructor
16815  * Create a new Progress
16816  * @param {Object} config The config object
16817  */
16818
16819 Roo.bootstrap.Progress = function(config){
16820     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16821 };
16822
16823 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16824     
16825     striped : false,
16826     active: false,
16827     
16828     getAutoCreate : function(){
16829         var cfg = {
16830             tag: 'div',
16831             cls: 'progress'
16832         };
16833         
16834         
16835         if(this.striped){
16836             cfg.cls += ' progress-striped';
16837         }
16838       
16839         if(this.active){
16840             cfg.cls += ' active';
16841         }
16842         
16843         
16844         return cfg;
16845     }
16846    
16847 });
16848
16849  
16850
16851  /*
16852  * - LGPL
16853  *
16854  * ProgressBar
16855  * 
16856  */
16857
16858 /**
16859  * @class Roo.bootstrap.ProgressBar
16860  * @extends Roo.bootstrap.Component
16861  * Bootstrap ProgressBar class
16862  * @cfg {Number} aria_valuenow aria-value now
16863  * @cfg {Number} aria_valuemin aria-value min
16864  * @cfg {Number} aria_valuemax aria-value max
16865  * @cfg {String} label label for the progress bar
16866  * @cfg {String} panel (success | info | warning | danger )
16867  * @cfg {String} role role of the progress bar
16868  * @cfg {String} sr_only text
16869  * 
16870  * 
16871  * @constructor
16872  * Create a new ProgressBar
16873  * @param {Object} config The config object
16874  */
16875
16876 Roo.bootstrap.ProgressBar = function(config){
16877     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16878 };
16879
16880 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16881     
16882     aria_valuenow : 0,
16883     aria_valuemin : 0,
16884     aria_valuemax : 100,
16885     label : false,
16886     panel : false,
16887     role : false,
16888     sr_only: false,
16889     
16890     getAutoCreate : function()
16891     {
16892         
16893         var cfg = {
16894             tag: 'div',
16895             cls: 'progress-bar',
16896             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16897         };
16898         
16899         if(this.sr_only){
16900             cfg.cn = {
16901                 tag: 'span',
16902                 cls: 'sr-only',
16903                 html: this.sr_only
16904             }
16905         }
16906         
16907         if(this.role){
16908             cfg.role = this.role;
16909         }
16910         
16911         if(this.aria_valuenow){
16912             cfg['aria-valuenow'] = this.aria_valuenow;
16913         }
16914         
16915         if(this.aria_valuemin){
16916             cfg['aria-valuemin'] = this.aria_valuemin;
16917         }
16918         
16919         if(this.aria_valuemax){
16920             cfg['aria-valuemax'] = this.aria_valuemax;
16921         }
16922         
16923         if(this.label && !this.sr_only){
16924             cfg.html = this.label;
16925         }
16926         
16927         if(this.panel){
16928             cfg.cls += ' progress-bar-' + this.panel;
16929         }
16930         
16931         return cfg;
16932     },
16933     
16934     update : function(aria_valuenow)
16935     {
16936         this.aria_valuenow = aria_valuenow;
16937         
16938         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16939     }
16940    
16941 });
16942
16943  
16944
16945  /*
16946  * - LGPL
16947  *
16948  * column
16949  * 
16950  */
16951
16952 /**
16953  * @class Roo.bootstrap.TabGroup
16954  * @extends Roo.bootstrap.Column
16955  * Bootstrap Column class
16956  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16957  * @cfg {Boolean} carousel true to make the group behave like a carousel
16958  * @cfg {Boolean} bullets show bullets for the panels
16959  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16960  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16961  * @cfg {Boolean} showarrow (true|false) show arrow default true
16962  * 
16963  * @constructor
16964  * Create a new TabGroup
16965  * @param {Object} config The config object
16966  */
16967
16968 Roo.bootstrap.TabGroup = function(config){
16969     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16970     if (!this.navId) {
16971         this.navId = Roo.id();
16972     }
16973     this.tabs = [];
16974     Roo.bootstrap.TabGroup.register(this);
16975     
16976 };
16977
16978 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16979     
16980     carousel : false,
16981     transition : false,
16982     bullets : 0,
16983     timer : 0,
16984     autoslide : false,
16985     slideFn : false,
16986     slideOnTouch : false,
16987     showarrow : true,
16988     
16989     getAutoCreate : function()
16990     {
16991         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16992         
16993         cfg.cls += ' tab-content';
16994         
16995         if (this.carousel) {
16996             cfg.cls += ' carousel slide';
16997             
16998             cfg.cn = [{
16999                cls : 'carousel-inner',
17000                cn : []
17001             }];
17002         
17003             if(this.bullets  && !Roo.isTouch){
17004                 
17005                 var bullets = {
17006                     cls : 'carousel-bullets',
17007                     cn : []
17008                 };
17009                
17010                 if(this.bullets_cls){
17011                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17012                 }
17013                 
17014                 bullets.cn.push({
17015                     cls : 'clear'
17016                 });
17017                 
17018                 cfg.cn[0].cn.push(bullets);
17019             }
17020             
17021             if(this.showarrow){
17022                 cfg.cn[0].cn.push({
17023                     tag : 'div',
17024                     class : 'carousel-arrow',
17025                     cn : [
17026                         {
17027                             tag : 'div',
17028                             class : 'carousel-prev',
17029                             cn : [
17030                                 {
17031                                     tag : 'i',
17032                                     class : 'fa fa-chevron-left'
17033                                 }
17034                             ]
17035                         },
17036                         {
17037                             tag : 'div',
17038                             class : 'carousel-next',
17039                             cn : [
17040                                 {
17041                                     tag : 'i',
17042                                     class : 'fa fa-chevron-right'
17043                                 }
17044                             ]
17045                         }
17046                     ]
17047                 });
17048             }
17049             
17050         }
17051         
17052         return cfg;
17053     },
17054     
17055     initEvents:  function()
17056     {
17057 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17058 //            this.el.on("touchstart", this.onTouchStart, this);
17059 //        }
17060         
17061         if(this.autoslide){
17062             var _this = this;
17063             
17064             this.slideFn = window.setInterval(function() {
17065                 _this.showPanelNext();
17066             }, this.timer);
17067         }
17068         
17069         if(this.showarrow){
17070             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17071             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17072         }
17073         
17074         
17075     },
17076     
17077 //    onTouchStart : function(e, el, o)
17078 //    {
17079 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17080 //            return;
17081 //        }
17082 //        
17083 //        this.showPanelNext();
17084 //    },
17085     
17086     
17087     getChildContainer : function()
17088     {
17089         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17090     },
17091     
17092     /**
17093     * register a Navigation item
17094     * @param {Roo.bootstrap.NavItem} the navitem to add
17095     */
17096     register : function(item)
17097     {
17098         this.tabs.push( item);
17099         item.navId = this.navId; // not really needed..
17100         this.addBullet();
17101     
17102     },
17103     
17104     getActivePanel : function()
17105     {
17106         var r = false;
17107         Roo.each(this.tabs, function(t) {
17108             if (t.active) {
17109                 r = t;
17110                 return false;
17111             }
17112             return null;
17113         });
17114         return r;
17115         
17116     },
17117     getPanelByName : function(n)
17118     {
17119         var r = false;
17120         Roo.each(this.tabs, function(t) {
17121             if (t.tabId == n) {
17122                 r = t;
17123                 return false;
17124             }
17125             return null;
17126         });
17127         return r;
17128     },
17129     indexOfPanel : function(p)
17130     {
17131         var r = false;
17132         Roo.each(this.tabs, function(t,i) {
17133             if (t.tabId == p.tabId) {
17134                 r = i;
17135                 return false;
17136             }
17137             return null;
17138         });
17139         return r;
17140     },
17141     /**
17142      * show a specific panel
17143      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17144      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17145      */
17146     showPanel : function (pan)
17147     {
17148         if(this.transition || typeof(pan) == 'undefined'){
17149             Roo.log("waiting for the transitionend");
17150             return;
17151         }
17152         
17153         if (typeof(pan) == 'number') {
17154             pan = this.tabs[pan];
17155         }
17156         
17157         if (typeof(pan) == 'string') {
17158             pan = this.getPanelByName(pan);
17159         }
17160         
17161         var cur = this.getActivePanel();
17162         
17163         if(!pan || !cur){
17164             Roo.log('pan or acitve pan is undefined');
17165             return false;
17166         }
17167         
17168         if (pan.tabId == this.getActivePanel().tabId) {
17169             return true;
17170         }
17171         
17172         if (false === cur.fireEvent('beforedeactivate')) {
17173             return false;
17174         }
17175         
17176         if(this.bullets > 0 && !Roo.isTouch){
17177             this.setActiveBullet(this.indexOfPanel(pan));
17178         }
17179         
17180         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17181             
17182             this.transition = true;
17183             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17184             var lr = dir == 'next' ? 'left' : 'right';
17185             pan.el.addClass(dir); // or prev
17186             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17187             cur.el.addClass(lr); // or right
17188             pan.el.addClass(lr);
17189             
17190             var _this = this;
17191             cur.el.on('transitionend', function() {
17192                 Roo.log("trans end?");
17193                 
17194                 pan.el.removeClass([lr,dir]);
17195                 pan.setActive(true);
17196                 
17197                 cur.el.removeClass([lr]);
17198                 cur.setActive(false);
17199                 
17200                 _this.transition = false;
17201                 
17202             }, this, { single:  true } );
17203             
17204             return true;
17205         }
17206         
17207         cur.setActive(false);
17208         pan.setActive(true);
17209         
17210         return true;
17211         
17212     },
17213     showPanelNext : function()
17214     {
17215         var i = this.indexOfPanel(this.getActivePanel());
17216         
17217         if (i >= this.tabs.length - 1 && !this.autoslide) {
17218             return;
17219         }
17220         
17221         if (i >= this.tabs.length - 1 && this.autoslide) {
17222             i = -1;
17223         }
17224         
17225         this.showPanel(this.tabs[i+1]);
17226     },
17227     
17228     showPanelPrev : function()
17229     {
17230         var i = this.indexOfPanel(this.getActivePanel());
17231         
17232         if (i  < 1 && !this.autoslide) {
17233             return;
17234         }
17235         
17236         if (i < 1 && this.autoslide) {
17237             i = this.tabs.length;
17238         }
17239         
17240         this.showPanel(this.tabs[i-1]);
17241     },
17242     
17243     
17244     addBullet: function()
17245     {
17246         if(!this.bullets || Roo.isTouch){
17247             return;
17248         }
17249         var ctr = this.el.select('.carousel-bullets',true).first();
17250         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17251         var bullet = ctr.createChild({
17252             cls : 'bullet bullet-' + i
17253         },ctr.dom.lastChild);
17254         
17255         
17256         var _this = this;
17257         
17258         bullet.on('click', (function(e, el, o, ii, t){
17259
17260             e.preventDefault();
17261
17262             this.showPanel(ii);
17263
17264             if(this.autoslide && this.slideFn){
17265                 clearInterval(this.slideFn);
17266                 this.slideFn = window.setInterval(function() {
17267                     _this.showPanelNext();
17268                 }, this.timer);
17269             }
17270
17271         }).createDelegate(this, [i, bullet], true));
17272                 
17273         
17274     },
17275      
17276     setActiveBullet : function(i)
17277     {
17278         if(Roo.isTouch){
17279             return;
17280         }
17281         
17282         Roo.each(this.el.select('.bullet', true).elements, function(el){
17283             el.removeClass('selected');
17284         });
17285
17286         var bullet = this.el.select('.bullet-' + i, true).first();
17287         
17288         if(!bullet){
17289             return;
17290         }
17291         
17292         bullet.addClass('selected');
17293     }
17294     
17295     
17296   
17297 });
17298
17299  
17300
17301  
17302  
17303 Roo.apply(Roo.bootstrap.TabGroup, {
17304     
17305     groups: {},
17306      /**
17307     * register a Navigation Group
17308     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17309     */
17310     register : function(navgrp)
17311     {
17312         this.groups[navgrp.navId] = navgrp;
17313         
17314     },
17315     /**
17316     * fetch a Navigation Group based on the navigation ID
17317     * if one does not exist , it will get created.
17318     * @param {string} the navgroup to add
17319     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17320     */
17321     get: function(navId) {
17322         if (typeof(this.groups[navId]) == 'undefined') {
17323             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17324         }
17325         return this.groups[navId] ;
17326     }
17327     
17328     
17329     
17330 });
17331
17332  /*
17333  * - LGPL
17334  *
17335  * TabPanel
17336  * 
17337  */
17338
17339 /**
17340  * @class Roo.bootstrap.TabPanel
17341  * @extends Roo.bootstrap.Component
17342  * Bootstrap TabPanel class
17343  * @cfg {Boolean} active panel active
17344  * @cfg {String} html panel content
17345  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17346  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17347  * @cfg {String} href click to link..
17348  * 
17349  * 
17350  * @constructor
17351  * Create a new TabPanel
17352  * @param {Object} config The config object
17353  */
17354
17355 Roo.bootstrap.TabPanel = function(config){
17356     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17357     this.addEvents({
17358         /**
17359              * @event changed
17360              * Fires when the active status changes
17361              * @param {Roo.bootstrap.TabPanel} this
17362              * @param {Boolean} state the new state
17363             
17364          */
17365         'changed': true,
17366         /**
17367              * @event beforedeactivate
17368              * Fires before a tab is de-activated - can be used to do validation on a form.
17369              * @param {Roo.bootstrap.TabPanel} this
17370              * @return {Boolean} false if there is an error
17371             
17372          */
17373         'beforedeactivate': true
17374      });
17375     
17376     this.tabId = this.tabId || Roo.id();
17377   
17378 };
17379
17380 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17381     
17382     active: false,
17383     html: false,
17384     tabId: false,
17385     navId : false,
17386     href : '',
17387     
17388     getAutoCreate : function(){
17389         var cfg = {
17390             tag: 'div',
17391             // item is needed for carousel - not sure if it has any effect otherwise
17392             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17393             html: this.html || ''
17394         };
17395         
17396         if(this.active){
17397             cfg.cls += ' active';
17398         }
17399         
17400         if(this.tabId){
17401             cfg.tabId = this.tabId;
17402         }
17403         
17404         
17405         return cfg;
17406     },
17407     
17408     initEvents:  function()
17409     {
17410         var p = this.parent();
17411         
17412         this.navId = this.navId || p.navId;
17413         
17414         if (typeof(this.navId) != 'undefined') {
17415             // not really needed.. but just in case.. parent should be a NavGroup.
17416             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17417             
17418             tg.register(this);
17419             
17420             var i = tg.tabs.length - 1;
17421             
17422             if(this.active && tg.bullets > 0 && i < tg.bullets){
17423                 tg.setActiveBullet(i);
17424             }
17425         }
17426         
17427         this.el.on('click', this.onClick, this);
17428         
17429         if(Roo.isTouch){
17430             this.el.on("touchstart", this.onTouchStart, this);
17431             this.el.on("touchmove", this.onTouchMove, this);
17432             this.el.on("touchend", this.onTouchEnd, this);
17433         }
17434         
17435     },
17436     
17437     onRender : function(ct, position)
17438     {
17439         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17440     },
17441     
17442     setActive : function(state)
17443     {
17444         Roo.log("panel - set active " + this.tabId + "=" + state);
17445         
17446         this.active = state;
17447         if (!state) {
17448             this.el.removeClass('active');
17449             
17450         } else  if (!this.el.hasClass('active')) {
17451             this.el.addClass('active');
17452         }
17453         
17454         this.fireEvent('changed', this, state);
17455     },
17456     
17457     onClick : function(e)
17458     {
17459         e.preventDefault();
17460         
17461         if(!this.href.length){
17462             return;
17463         }
17464         
17465         window.location.href = this.href;
17466     },
17467     
17468     startX : 0,
17469     startY : 0,
17470     endX : 0,
17471     endY : 0,
17472     swiping : false,
17473     
17474     onTouchStart : function(e)
17475     {
17476         this.swiping = false;
17477         
17478         this.startX = e.browserEvent.touches[0].clientX;
17479         this.startY = e.browserEvent.touches[0].clientY;
17480     },
17481     
17482     onTouchMove : function(e)
17483     {
17484         this.swiping = true;
17485         
17486         this.endX = e.browserEvent.touches[0].clientX;
17487         this.endY = e.browserEvent.touches[0].clientY;
17488     },
17489     
17490     onTouchEnd : function(e)
17491     {
17492         if(!this.swiping){
17493             this.onClick(e);
17494             return;
17495         }
17496         
17497         var tabGroup = this.parent();
17498         
17499         if(this.endX > this.startX){ // swiping right
17500             tabGroup.showPanelPrev();
17501             return;
17502         }
17503         
17504         if(this.startX > this.endX){ // swiping left
17505             tabGroup.showPanelNext();
17506             return;
17507         }
17508     }
17509     
17510     
17511 });
17512  
17513
17514  
17515
17516  /*
17517  * - LGPL
17518  *
17519  * DateField
17520  * 
17521  */
17522
17523 /**
17524  * @class Roo.bootstrap.DateField
17525  * @extends Roo.bootstrap.Input
17526  * Bootstrap DateField class
17527  * @cfg {Number} weekStart default 0
17528  * @cfg {String} viewMode default empty, (months|years)
17529  * @cfg {String} minViewMode default empty, (months|years)
17530  * @cfg {Number} startDate default -Infinity
17531  * @cfg {Number} endDate default Infinity
17532  * @cfg {Boolean} todayHighlight default false
17533  * @cfg {Boolean} todayBtn default false
17534  * @cfg {Boolean} calendarWeeks default false
17535  * @cfg {Object} daysOfWeekDisabled default empty
17536  * @cfg {Boolean} singleMode default false (true | false)
17537  * 
17538  * @cfg {Boolean} keyboardNavigation default true
17539  * @cfg {String} language default en
17540  * 
17541  * @constructor
17542  * Create a new DateField
17543  * @param {Object} config The config object
17544  */
17545
17546 Roo.bootstrap.DateField = function(config){
17547     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17548      this.addEvents({
17549             /**
17550              * @event show
17551              * Fires when this field show.
17552              * @param {Roo.bootstrap.DateField} this
17553              * @param {Mixed} date The date value
17554              */
17555             show : true,
17556             /**
17557              * @event show
17558              * Fires when this field hide.
17559              * @param {Roo.bootstrap.DateField} this
17560              * @param {Mixed} date The date value
17561              */
17562             hide : true,
17563             /**
17564              * @event select
17565              * Fires when select a date.
17566              * @param {Roo.bootstrap.DateField} this
17567              * @param {Mixed} date The date value
17568              */
17569             select : true,
17570             /**
17571              * @event beforeselect
17572              * Fires when before select a date.
17573              * @param {Roo.bootstrap.DateField} this
17574              * @param {Mixed} date The date value
17575              */
17576             beforeselect : true
17577         });
17578 };
17579
17580 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17581     
17582     /**
17583      * @cfg {String} format
17584      * The default date format string which can be overriden for localization support.  The format must be
17585      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17586      */
17587     format : "m/d/y",
17588     /**
17589      * @cfg {String} altFormats
17590      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17591      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17592      */
17593     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17594     
17595     weekStart : 0,
17596     
17597     viewMode : '',
17598     
17599     minViewMode : '',
17600     
17601     todayHighlight : false,
17602     
17603     todayBtn: false,
17604     
17605     language: 'en',
17606     
17607     keyboardNavigation: true,
17608     
17609     calendarWeeks: false,
17610     
17611     startDate: -Infinity,
17612     
17613     endDate: Infinity,
17614     
17615     daysOfWeekDisabled: [],
17616     
17617     _events: [],
17618     
17619     singleMode : false,
17620     
17621     UTCDate: function()
17622     {
17623         return new Date(Date.UTC.apply(Date, arguments));
17624     },
17625     
17626     UTCToday: function()
17627     {
17628         var today = new Date();
17629         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17630     },
17631     
17632     getDate: function() {
17633             var d = this.getUTCDate();
17634             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17635     },
17636     
17637     getUTCDate: function() {
17638             return this.date;
17639     },
17640     
17641     setDate: function(d) {
17642             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17643     },
17644     
17645     setUTCDate: function(d) {
17646             this.date = d;
17647             this.setValue(this.formatDate(this.date));
17648     },
17649         
17650     onRender: function(ct, position)
17651     {
17652         
17653         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17654         
17655         this.language = this.language || 'en';
17656         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17657         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17658         
17659         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17660         this.format = this.format || 'm/d/y';
17661         this.isInline = false;
17662         this.isInput = true;
17663         this.component = this.el.select('.add-on', true).first() || false;
17664         this.component = (this.component && this.component.length === 0) ? false : this.component;
17665         this.hasInput = this.component && this.inputEl().length;
17666         
17667         if (typeof(this.minViewMode === 'string')) {
17668             switch (this.minViewMode) {
17669                 case 'months':
17670                     this.minViewMode = 1;
17671                     break;
17672                 case 'years':
17673                     this.minViewMode = 2;
17674                     break;
17675                 default:
17676                     this.minViewMode = 0;
17677                     break;
17678             }
17679         }
17680         
17681         if (typeof(this.viewMode === 'string')) {
17682             switch (this.viewMode) {
17683                 case 'months':
17684                     this.viewMode = 1;
17685                     break;
17686                 case 'years':
17687                     this.viewMode = 2;
17688                     break;
17689                 default:
17690                     this.viewMode = 0;
17691                     break;
17692             }
17693         }
17694                 
17695         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17696         
17697 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17698         
17699         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17700         
17701         this.picker().on('mousedown', this.onMousedown, this);
17702         this.picker().on('click', this.onClick, this);
17703         
17704         this.picker().addClass('datepicker-dropdown');
17705         
17706         this.startViewMode = this.viewMode;
17707         
17708         if(this.singleMode){
17709             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17710                 v.setVisibilityMode(Roo.Element.DISPLAY);
17711                 v.hide();
17712             });
17713             
17714             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17715                 v.setStyle('width', '189px');
17716             });
17717         }
17718         
17719         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17720             if(!this.calendarWeeks){
17721                 v.remove();
17722                 return;
17723             }
17724             
17725             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17726             v.attr('colspan', function(i, val){
17727                 return parseInt(val) + 1;
17728             });
17729         });
17730                         
17731         
17732         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17733         
17734         this.setStartDate(this.startDate);
17735         this.setEndDate(this.endDate);
17736         
17737         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17738         
17739         this.fillDow();
17740         this.fillMonths();
17741         this.update();
17742         this.showMode();
17743         
17744         if(this.isInline) {
17745             this.show();
17746         }
17747     },
17748     
17749     picker : function()
17750     {
17751         return this.pickerEl;
17752 //        return this.el.select('.datepicker', true).first();
17753     },
17754     
17755     fillDow: function()
17756     {
17757         var dowCnt = this.weekStart;
17758         
17759         var dow = {
17760             tag: 'tr',
17761             cn: [
17762                 
17763             ]
17764         };
17765         
17766         if(this.calendarWeeks){
17767             dow.cn.push({
17768                 tag: 'th',
17769                 cls: 'cw',
17770                 html: '&nbsp;'
17771             })
17772         }
17773         
17774         while (dowCnt < this.weekStart + 7) {
17775             dow.cn.push({
17776                 tag: 'th',
17777                 cls: 'dow',
17778                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17779             });
17780         }
17781         
17782         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17783     },
17784     
17785     fillMonths: function()
17786     {    
17787         var i = 0;
17788         var months = this.picker().select('>.datepicker-months td', true).first();
17789         
17790         months.dom.innerHTML = '';
17791         
17792         while (i < 12) {
17793             var month = {
17794                 tag: 'span',
17795                 cls: 'month',
17796                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17797             };
17798             
17799             months.createChild(month);
17800         }
17801         
17802     },
17803     
17804     update: function()
17805     {
17806         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;
17807         
17808         if (this.date < this.startDate) {
17809             this.viewDate = new Date(this.startDate);
17810         } else if (this.date > this.endDate) {
17811             this.viewDate = new Date(this.endDate);
17812         } else {
17813             this.viewDate = new Date(this.date);
17814         }
17815         
17816         this.fill();
17817     },
17818     
17819     fill: function() 
17820     {
17821         var d = new Date(this.viewDate),
17822                 year = d.getUTCFullYear(),
17823                 month = d.getUTCMonth(),
17824                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17825                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17826                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17827                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17828                 currentDate = this.date && this.date.valueOf(),
17829                 today = this.UTCToday();
17830         
17831         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17832         
17833 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17834         
17835 //        this.picker.select('>tfoot th.today').
17836 //                                              .text(dates[this.language].today)
17837 //                                              .toggle(this.todayBtn !== false);
17838     
17839         this.updateNavArrows();
17840         this.fillMonths();
17841                                                 
17842         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17843         
17844         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17845          
17846         prevMonth.setUTCDate(day);
17847         
17848         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17849         
17850         var nextMonth = new Date(prevMonth);
17851         
17852         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17853         
17854         nextMonth = nextMonth.valueOf();
17855         
17856         var fillMonths = false;
17857         
17858         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17859         
17860         while(prevMonth.valueOf() < nextMonth) {
17861             var clsName = '';
17862             
17863             if (prevMonth.getUTCDay() === this.weekStart) {
17864                 if(fillMonths){
17865                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17866                 }
17867                     
17868                 fillMonths = {
17869                     tag: 'tr',
17870                     cn: []
17871                 };
17872                 
17873                 if(this.calendarWeeks){
17874                     // ISO 8601: First week contains first thursday.
17875                     // ISO also states week starts on Monday, but we can be more abstract here.
17876                     var
17877                     // Start of current week: based on weekstart/current date
17878                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17879                     // Thursday of this week
17880                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17881                     // First Thursday of year, year from thursday
17882                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17883                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17884                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17885                     
17886                     fillMonths.cn.push({
17887                         tag: 'td',
17888                         cls: 'cw',
17889                         html: calWeek
17890                     });
17891                 }
17892             }
17893             
17894             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17895                 clsName += ' old';
17896             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17897                 clsName += ' new';
17898             }
17899             if (this.todayHighlight &&
17900                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17901                 prevMonth.getUTCMonth() == today.getMonth() &&
17902                 prevMonth.getUTCDate() == today.getDate()) {
17903                 clsName += ' today';
17904             }
17905             
17906             if (currentDate && prevMonth.valueOf() === currentDate) {
17907                 clsName += ' active';
17908             }
17909             
17910             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17911                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17912                     clsName += ' disabled';
17913             }
17914             
17915             fillMonths.cn.push({
17916                 tag: 'td',
17917                 cls: 'day ' + clsName,
17918                 html: prevMonth.getDate()
17919             });
17920             
17921             prevMonth.setDate(prevMonth.getDate()+1);
17922         }
17923           
17924         var currentYear = this.date && this.date.getUTCFullYear();
17925         var currentMonth = this.date && this.date.getUTCMonth();
17926         
17927         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17928         
17929         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17930             v.removeClass('active');
17931             
17932             if(currentYear === year && k === currentMonth){
17933                 v.addClass('active');
17934             }
17935             
17936             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17937                 v.addClass('disabled');
17938             }
17939             
17940         });
17941         
17942         
17943         year = parseInt(year/10, 10) * 10;
17944         
17945         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17946         
17947         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17948         
17949         year -= 1;
17950         for (var i = -1; i < 11; i++) {
17951             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17952                 tag: 'span',
17953                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17954                 html: year
17955             });
17956             
17957             year += 1;
17958         }
17959     },
17960     
17961     showMode: function(dir) 
17962     {
17963         if (dir) {
17964             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17965         }
17966         
17967         Roo.each(this.picker().select('>div',true).elements, function(v){
17968             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17969             v.hide();
17970         });
17971         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17972     },
17973     
17974     place: function()
17975     {
17976         if(this.isInline) {
17977             return;
17978         }
17979         
17980         this.picker().removeClass(['bottom', 'top']);
17981         
17982         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17983             /*
17984              * place to the top of element!
17985              *
17986              */
17987             
17988             this.picker().addClass('top');
17989             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17990             
17991             return;
17992         }
17993         
17994         this.picker().addClass('bottom');
17995         
17996         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17997     },
17998     
17999     parseDate : function(value)
18000     {
18001         if(!value || value instanceof Date){
18002             return value;
18003         }
18004         var v = Date.parseDate(value, this.format);
18005         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18006             v = Date.parseDate(value, 'Y-m-d');
18007         }
18008         if(!v && this.altFormats){
18009             if(!this.altFormatsArray){
18010                 this.altFormatsArray = this.altFormats.split("|");
18011             }
18012             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18013                 v = Date.parseDate(value, this.altFormatsArray[i]);
18014             }
18015         }
18016         return v;
18017     },
18018     
18019     formatDate : function(date, fmt)
18020     {   
18021         return (!date || !(date instanceof Date)) ?
18022         date : date.dateFormat(fmt || this.format);
18023     },
18024     
18025     onFocus : function()
18026     {
18027         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18028         this.show();
18029     },
18030     
18031     onBlur : function()
18032     {
18033         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18034         
18035         var d = this.inputEl().getValue();
18036         
18037         this.setValue(d);
18038                 
18039         this.hide();
18040     },
18041     
18042     show : function()
18043     {
18044         this.picker().show();
18045         this.update();
18046         this.place();
18047         
18048         this.fireEvent('show', this, this.date);
18049     },
18050     
18051     hide : function()
18052     {
18053         if(this.isInline) {
18054             return;
18055         }
18056         this.picker().hide();
18057         this.viewMode = this.startViewMode;
18058         this.showMode();
18059         
18060         this.fireEvent('hide', this, this.date);
18061         
18062     },
18063     
18064     onMousedown: function(e)
18065     {
18066         e.stopPropagation();
18067         e.preventDefault();
18068     },
18069     
18070     keyup: function(e)
18071     {
18072         Roo.bootstrap.DateField.superclass.keyup.call(this);
18073         this.update();
18074     },
18075
18076     setValue: function(v)
18077     {
18078         if(this.fireEvent('beforeselect', this, v) !== false){
18079             var d = new Date(this.parseDate(v) ).clearTime();
18080         
18081             if(isNaN(d.getTime())){
18082                 this.date = this.viewDate = '';
18083                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18084                 return;
18085             }
18086
18087             v = this.formatDate(d);
18088
18089             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18090
18091             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18092
18093             this.update();
18094
18095             this.fireEvent('select', this, this.date);
18096         }
18097     },
18098     
18099     getValue: function()
18100     {
18101         return this.formatDate(this.date);
18102     },
18103     
18104     fireKey: function(e)
18105     {
18106         if (!this.picker().isVisible()){
18107             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18108                 this.show();
18109             }
18110             return;
18111         }
18112         
18113         var dateChanged = false,
18114         dir, day, month,
18115         newDate, newViewDate;
18116         
18117         switch(e.keyCode){
18118             case 27: // escape
18119                 this.hide();
18120                 e.preventDefault();
18121                 break;
18122             case 37: // left
18123             case 39: // right
18124                 if (!this.keyboardNavigation) {
18125                     break;
18126                 }
18127                 dir = e.keyCode == 37 ? -1 : 1;
18128                 
18129                 if (e.ctrlKey){
18130                     newDate = this.moveYear(this.date, dir);
18131                     newViewDate = this.moveYear(this.viewDate, dir);
18132                 } else if (e.shiftKey){
18133                     newDate = this.moveMonth(this.date, dir);
18134                     newViewDate = this.moveMonth(this.viewDate, dir);
18135                 } else {
18136                     newDate = new Date(this.date);
18137                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18138                     newViewDate = new Date(this.viewDate);
18139                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18140                 }
18141                 if (this.dateWithinRange(newDate)){
18142                     this.date = newDate;
18143                     this.viewDate = newViewDate;
18144                     this.setValue(this.formatDate(this.date));
18145 //                    this.update();
18146                     e.preventDefault();
18147                     dateChanged = true;
18148                 }
18149                 break;
18150             case 38: // up
18151             case 40: // down
18152                 if (!this.keyboardNavigation) {
18153                     break;
18154                 }
18155                 dir = e.keyCode == 38 ? -1 : 1;
18156                 if (e.ctrlKey){
18157                     newDate = this.moveYear(this.date, dir);
18158                     newViewDate = this.moveYear(this.viewDate, dir);
18159                 } else if (e.shiftKey){
18160                     newDate = this.moveMonth(this.date, dir);
18161                     newViewDate = this.moveMonth(this.viewDate, dir);
18162                 } else {
18163                     newDate = new Date(this.date);
18164                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18165                     newViewDate = new Date(this.viewDate);
18166                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18167                 }
18168                 if (this.dateWithinRange(newDate)){
18169                     this.date = newDate;
18170                     this.viewDate = newViewDate;
18171                     this.setValue(this.formatDate(this.date));
18172 //                    this.update();
18173                     e.preventDefault();
18174                     dateChanged = true;
18175                 }
18176                 break;
18177             case 13: // enter
18178                 this.setValue(this.formatDate(this.date));
18179                 this.hide();
18180                 e.preventDefault();
18181                 break;
18182             case 9: // tab
18183                 this.setValue(this.formatDate(this.date));
18184                 this.hide();
18185                 break;
18186             case 16: // shift
18187             case 17: // ctrl
18188             case 18: // alt
18189                 break;
18190             default :
18191                 this.hide();
18192                 
18193         }
18194     },
18195     
18196     
18197     onClick: function(e) 
18198     {
18199         e.stopPropagation();
18200         e.preventDefault();
18201         
18202         var target = e.getTarget();
18203         
18204         if(target.nodeName.toLowerCase() === 'i'){
18205             target = Roo.get(target).dom.parentNode;
18206         }
18207         
18208         var nodeName = target.nodeName;
18209         var className = target.className;
18210         var html = target.innerHTML;
18211         //Roo.log(nodeName);
18212         
18213         switch(nodeName.toLowerCase()) {
18214             case 'th':
18215                 switch(className) {
18216                     case 'switch':
18217                         this.showMode(1);
18218                         break;
18219                     case 'prev':
18220                     case 'next':
18221                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18222                         switch(this.viewMode){
18223                                 case 0:
18224                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18225                                         break;
18226                                 case 1:
18227                                 case 2:
18228                                         this.viewDate = this.moveYear(this.viewDate, dir);
18229                                         break;
18230                         }
18231                         this.fill();
18232                         break;
18233                     case 'today':
18234                         var date = new Date();
18235                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18236 //                        this.fill()
18237                         this.setValue(this.formatDate(this.date));
18238                         
18239                         this.hide();
18240                         break;
18241                 }
18242                 break;
18243             case 'span':
18244                 if (className.indexOf('disabled') < 0) {
18245                     this.viewDate.setUTCDate(1);
18246                     if (className.indexOf('month') > -1) {
18247                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18248                     } else {
18249                         var year = parseInt(html, 10) || 0;
18250                         this.viewDate.setUTCFullYear(year);
18251                         
18252                     }
18253                     
18254                     if(this.singleMode){
18255                         this.setValue(this.formatDate(this.viewDate));
18256                         this.hide();
18257                         return;
18258                     }
18259                     
18260                     this.showMode(-1);
18261                     this.fill();
18262                 }
18263                 break;
18264                 
18265             case 'td':
18266                 //Roo.log(className);
18267                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18268                     var day = parseInt(html, 10) || 1;
18269                     var year = this.viewDate.getUTCFullYear(),
18270                         month = this.viewDate.getUTCMonth();
18271
18272                     if (className.indexOf('old') > -1) {
18273                         if(month === 0 ){
18274                             month = 11;
18275                             year -= 1;
18276                         }else{
18277                             month -= 1;
18278                         }
18279                     } else if (className.indexOf('new') > -1) {
18280                         if (month == 11) {
18281                             month = 0;
18282                             year += 1;
18283                         } else {
18284                             month += 1;
18285                         }
18286                     }
18287                     //Roo.log([year,month,day]);
18288                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18289                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18290 //                    this.fill();
18291                     //Roo.log(this.formatDate(this.date));
18292                     this.setValue(this.formatDate(this.date));
18293                     this.hide();
18294                 }
18295                 break;
18296         }
18297     },
18298     
18299     setStartDate: function(startDate)
18300     {
18301         this.startDate = startDate || -Infinity;
18302         if (this.startDate !== -Infinity) {
18303             this.startDate = this.parseDate(this.startDate);
18304         }
18305         this.update();
18306         this.updateNavArrows();
18307     },
18308
18309     setEndDate: function(endDate)
18310     {
18311         this.endDate = endDate || Infinity;
18312         if (this.endDate !== Infinity) {
18313             this.endDate = this.parseDate(this.endDate);
18314         }
18315         this.update();
18316         this.updateNavArrows();
18317     },
18318     
18319     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18320     {
18321         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18322         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18323             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18324         }
18325         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18326             return parseInt(d, 10);
18327         });
18328         this.update();
18329         this.updateNavArrows();
18330     },
18331     
18332     updateNavArrows: function() 
18333     {
18334         if(this.singleMode){
18335             return;
18336         }
18337         
18338         var d = new Date(this.viewDate),
18339         year = d.getUTCFullYear(),
18340         month = d.getUTCMonth();
18341         
18342         Roo.each(this.picker().select('.prev', true).elements, function(v){
18343             v.show();
18344             switch (this.viewMode) {
18345                 case 0:
18346
18347                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18348                         v.hide();
18349                     }
18350                     break;
18351                 case 1:
18352                 case 2:
18353                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18354                         v.hide();
18355                     }
18356                     break;
18357             }
18358         });
18359         
18360         Roo.each(this.picker().select('.next', true).elements, function(v){
18361             v.show();
18362             switch (this.viewMode) {
18363                 case 0:
18364
18365                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18366                         v.hide();
18367                     }
18368                     break;
18369                 case 1:
18370                 case 2:
18371                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18372                         v.hide();
18373                     }
18374                     break;
18375             }
18376         })
18377     },
18378     
18379     moveMonth: function(date, dir)
18380     {
18381         if (!dir) {
18382             return date;
18383         }
18384         var new_date = new Date(date.valueOf()),
18385         day = new_date.getUTCDate(),
18386         month = new_date.getUTCMonth(),
18387         mag = Math.abs(dir),
18388         new_month, test;
18389         dir = dir > 0 ? 1 : -1;
18390         if (mag == 1){
18391             test = dir == -1
18392             // If going back one month, make sure month is not current month
18393             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18394             ? function(){
18395                 return new_date.getUTCMonth() == month;
18396             }
18397             // If going forward one month, make sure month is as expected
18398             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18399             : function(){
18400                 return new_date.getUTCMonth() != new_month;
18401             };
18402             new_month = month + dir;
18403             new_date.setUTCMonth(new_month);
18404             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18405             if (new_month < 0 || new_month > 11) {
18406                 new_month = (new_month + 12) % 12;
18407             }
18408         } else {
18409             // For magnitudes >1, move one month at a time...
18410             for (var i=0; i<mag; i++) {
18411                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18412                 new_date = this.moveMonth(new_date, dir);
18413             }
18414             // ...then reset the day, keeping it in the new month
18415             new_month = new_date.getUTCMonth();
18416             new_date.setUTCDate(day);
18417             test = function(){
18418                 return new_month != new_date.getUTCMonth();
18419             };
18420         }
18421         // Common date-resetting loop -- if date is beyond end of month, make it
18422         // end of month
18423         while (test()){
18424             new_date.setUTCDate(--day);
18425             new_date.setUTCMonth(new_month);
18426         }
18427         return new_date;
18428     },
18429
18430     moveYear: function(date, dir)
18431     {
18432         return this.moveMonth(date, dir*12);
18433     },
18434
18435     dateWithinRange: function(date)
18436     {
18437         return date >= this.startDate && date <= this.endDate;
18438     },
18439
18440     
18441     remove: function() 
18442     {
18443         this.picker().remove();
18444     },
18445     
18446     validateValue : function(value)
18447     {
18448         if(value.length < 1)  {
18449             if(this.allowBlank){
18450                 return true;
18451             }
18452             return false;
18453         }
18454         
18455         if(value.length < this.minLength){
18456             return false;
18457         }
18458         if(value.length > this.maxLength){
18459             return false;
18460         }
18461         if(this.vtype){
18462             var vt = Roo.form.VTypes;
18463             if(!vt[this.vtype](value, this)){
18464                 return false;
18465             }
18466         }
18467         if(typeof this.validator == "function"){
18468             var msg = this.validator(value);
18469             if(msg !== true){
18470                 return false;
18471             }
18472         }
18473         
18474         if(this.regex && !this.regex.test(value)){
18475             return false;
18476         }
18477         
18478         if(typeof(this.parseDate(value)) == 'undefined'){
18479             return false;
18480         }
18481         
18482         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18483             return false;
18484         }      
18485         
18486         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18487             return false;
18488         } 
18489         
18490         
18491         return true;
18492     }
18493    
18494 });
18495
18496 Roo.apply(Roo.bootstrap.DateField,  {
18497     
18498     head : {
18499         tag: 'thead',
18500         cn: [
18501         {
18502             tag: 'tr',
18503             cn: [
18504             {
18505                 tag: 'th',
18506                 cls: 'prev',
18507                 html: '<i class="fa fa-arrow-left"/>'
18508             },
18509             {
18510                 tag: 'th',
18511                 cls: 'switch',
18512                 colspan: '5'
18513             },
18514             {
18515                 tag: 'th',
18516                 cls: 'next',
18517                 html: '<i class="fa fa-arrow-right"/>'
18518             }
18519
18520             ]
18521         }
18522         ]
18523     },
18524     
18525     content : {
18526         tag: 'tbody',
18527         cn: [
18528         {
18529             tag: 'tr',
18530             cn: [
18531             {
18532                 tag: 'td',
18533                 colspan: '7'
18534             }
18535             ]
18536         }
18537         ]
18538     },
18539     
18540     footer : {
18541         tag: 'tfoot',
18542         cn: [
18543         {
18544             tag: 'tr',
18545             cn: [
18546             {
18547                 tag: 'th',
18548                 colspan: '7',
18549                 cls: 'today'
18550             }
18551                     
18552             ]
18553         }
18554         ]
18555     },
18556     
18557     dates:{
18558         en: {
18559             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18560             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18561             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18562             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18563             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18564             today: "Today"
18565         }
18566     },
18567     
18568     modes: [
18569     {
18570         clsName: 'days',
18571         navFnc: 'Month',
18572         navStep: 1
18573     },
18574     {
18575         clsName: 'months',
18576         navFnc: 'FullYear',
18577         navStep: 1
18578     },
18579     {
18580         clsName: 'years',
18581         navFnc: 'FullYear',
18582         navStep: 10
18583     }]
18584 });
18585
18586 Roo.apply(Roo.bootstrap.DateField,  {
18587   
18588     template : {
18589         tag: 'div',
18590         cls: 'datepicker dropdown-menu roo-dynamic',
18591         cn: [
18592         {
18593             tag: 'div',
18594             cls: 'datepicker-days',
18595             cn: [
18596             {
18597                 tag: 'table',
18598                 cls: 'table-condensed',
18599                 cn:[
18600                 Roo.bootstrap.DateField.head,
18601                 {
18602                     tag: 'tbody'
18603                 },
18604                 Roo.bootstrap.DateField.footer
18605                 ]
18606             }
18607             ]
18608         },
18609         {
18610             tag: 'div',
18611             cls: 'datepicker-months',
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             tag: 'div',
18626             cls: 'datepicker-years',
18627             cn: [
18628             {
18629                 tag: 'table',
18630                 cls: 'table-condensed',
18631                 cn:[
18632                 Roo.bootstrap.DateField.head,
18633                 Roo.bootstrap.DateField.content,
18634                 Roo.bootstrap.DateField.footer
18635                 ]
18636             }
18637             ]
18638         }
18639         ]
18640     }
18641 });
18642
18643  
18644
18645  /*
18646  * - LGPL
18647  *
18648  * TimeField
18649  * 
18650  */
18651
18652 /**
18653  * @class Roo.bootstrap.TimeField
18654  * @extends Roo.bootstrap.Input
18655  * Bootstrap DateField class
18656  * 
18657  * 
18658  * @constructor
18659  * Create a new TimeField
18660  * @param {Object} config The config object
18661  */
18662
18663 Roo.bootstrap.TimeField = function(config){
18664     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18665     this.addEvents({
18666             /**
18667              * @event show
18668              * Fires when this field show.
18669              * @param {Roo.bootstrap.DateField} thisthis
18670              * @param {Mixed} date The date value
18671              */
18672             show : true,
18673             /**
18674              * @event show
18675              * Fires when this field hide.
18676              * @param {Roo.bootstrap.DateField} this
18677              * @param {Mixed} date The date value
18678              */
18679             hide : true,
18680             /**
18681              * @event select
18682              * Fires when select a date.
18683              * @param {Roo.bootstrap.DateField} this
18684              * @param {Mixed} date The date value
18685              */
18686             select : true
18687         });
18688 };
18689
18690 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18691     
18692     /**
18693      * @cfg {String} format
18694      * The default time format string which can be overriden for localization support.  The format must be
18695      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18696      */
18697     format : "H:i",
18698        
18699     onRender: function(ct, position)
18700     {
18701         
18702         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18703                 
18704         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18705         
18706         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18707         
18708         this.pop = this.picker().select('>.datepicker-time',true).first();
18709         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18710         
18711         this.picker().on('mousedown', this.onMousedown, this);
18712         this.picker().on('click', this.onClick, this);
18713         
18714         this.picker().addClass('datepicker-dropdown');
18715     
18716         this.fillTime();
18717         this.update();
18718             
18719         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18720         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18721         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18722         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18723         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18724         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18725
18726     },
18727     
18728     fireKey: function(e){
18729         if (!this.picker().isVisible()){
18730             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18731                 this.show();
18732             }
18733             return;
18734         }
18735
18736         e.preventDefault();
18737         
18738         switch(e.keyCode){
18739             case 27: // escape
18740                 this.hide();
18741                 break;
18742             case 37: // left
18743             case 39: // right
18744                 this.onTogglePeriod();
18745                 break;
18746             case 38: // up
18747                 this.onIncrementMinutes();
18748                 break;
18749             case 40: // down
18750                 this.onDecrementMinutes();
18751                 break;
18752             case 13: // enter
18753             case 9: // tab
18754                 this.setTime();
18755                 break;
18756         }
18757     },
18758     
18759     onClick: function(e) {
18760         e.stopPropagation();
18761         e.preventDefault();
18762     },
18763     
18764     picker : function()
18765     {
18766         return this.el.select('.datepicker', true).first();
18767     },
18768     
18769     fillTime: function()
18770     {    
18771         var time = this.pop.select('tbody', true).first();
18772         
18773         time.dom.innerHTML = '';
18774         
18775         time.createChild({
18776             tag: 'tr',
18777             cn: [
18778                 {
18779                     tag: 'td',
18780                     cn: [
18781                         {
18782                             tag: 'a',
18783                             href: '#',
18784                             cls: 'btn',
18785                             cn: [
18786                                 {
18787                                     tag: 'span',
18788                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18789                                 }
18790                             ]
18791                         } 
18792                     ]
18793                 },
18794                 {
18795                     tag: 'td',
18796                     cls: 'separator'
18797                 },
18798                 {
18799                     tag: 'td',
18800                     cn: [
18801                         {
18802                             tag: 'a',
18803                             href: '#',
18804                             cls: 'btn',
18805                             cn: [
18806                                 {
18807                                     tag: 'span',
18808                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18809                                 }
18810                             ]
18811                         }
18812                     ]
18813                 },
18814                 {
18815                     tag: 'td',
18816                     cls: 'separator'
18817                 }
18818             ]
18819         });
18820         
18821         time.createChild({
18822             tag: 'tr',
18823             cn: [
18824                 {
18825                     tag: 'td',
18826                     cn: [
18827                         {
18828                             tag: 'span',
18829                             cls: 'timepicker-hour',
18830                             html: '00'
18831                         }  
18832                     ]
18833                 },
18834                 {
18835                     tag: 'td',
18836                     cls: 'separator',
18837                     html: ':'
18838                 },
18839                 {
18840                     tag: 'td',
18841                     cn: [
18842                         {
18843                             tag: 'span',
18844                             cls: 'timepicker-minute',
18845                             html: '00'
18846                         }  
18847                     ]
18848                 },
18849                 {
18850                     tag: 'td',
18851                     cls: 'separator'
18852                 },
18853                 {
18854                     tag: 'td',
18855                     cn: [
18856                         {
18857                             tag: 'button',
18858                             type: 'button',
18859                             cls: 'btn btn-primary period',
18860                             html: 'AM'
18861                             
18862                         }
18863                     ]
18864                 }
18865             ]
18866         });
18867         
18868         time.createChild({
18869             tag: 'tr',
18870             cn: [
18871                 {
18872                     tag: 'td',
18873                     cn: [
18874                         {
18875                             tag: 'a',
18876                             href: '#',
18877                             cls: 'btn',
18878                             cn: [
18879                                 {
18880                                     tag: 'span',
18881                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18882                                 }
18883                             ]
18884                         }
18885                     ]
18886                 },
18887                 {
18888                     tag: 'td',
18889                     cls: 'separator'
18890                 },
18891                 {
18892                     tag: 'td',
18893                     cn: [
18894                         {
18895                             tag: 'a',
18896                             href: '#',
18897                             cls: 'btn',
18898                             cn: [
18899                                 {
18900                                     tag: 'span',
18901                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18902                                 }
18903                             ]
18904                         }
18905                     ]
18906                 },
18907                 {
18908                     tag: 'td',
18909                     cls: 'separator'
18910                 }
18911             ]
18912         });
18913         
18914     },
18915     
18916     update: function()
18917     {
18918         
18919         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18920         
18921         this.fill();
18922     },
18923     
18924     fill: function() 
18925     {
18926         var hours = this.time.getHours();
18927         var minutes = this.time.getMinutes();
18928         var period = 'AM';
18929         
18930         if(hours > 11){
18931             period = 'PM';
18932         }
18933         
18934         if(hours == 0){
18935             hours = 12;
18936         }
18937         
18938         
18939         if(hours > 12){
18940             hours = hours - 12;
18941         }
18942         
18943         if(hours < 10){
18944             hours = '0' + hours;
18945         }
18946         
18947         if(minutes < 10){
18948             minutes = '0' + minutes;
18949         }
18950         
18951         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18952         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18953         this.pop.select('button', true).first().dom.innerHTML = period;
18954         
18955     },
18956     
18957     place: function()
18958     {   
18959         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18960         
18961         var cls = ['bottom'];
18962         
18963         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18964             cls.pop();
18965             cls.push('top');
18966         }
18967         
18968         cls.push('right');
18969         
18970         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18971             cls.pop();
18972             cls.push('left');
18973         }
18974         
18975         this.picker().addClass(cls.join('-'));
18976         
18977         var _this = this;
18978         
18979         Roo.each(cls, function(c){
18980             if(c == 'bottom'){
18981                 _this.picker().setTop(_this.inputEl().getHeight());
18982                 return;
18983             }
18984             if(c == 'top'){
18985                 _this.picker().setTop(0 - _this.picker().getHeight());
18986                 return;
18987             }
18988             
18989             if(c == 'left'){
18990                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18991                 return;
18992             }
18993             if(c == 'right'){
18994                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18995                 return;
18996             }
18997         });
18998         
18999     },
19000   
19001     onFocus : function()
19002     {
19003         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19004         this.show();
19005     },
19006     
19007     onBlur : function()
19008     {
19009         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19010         this.hide();
19011     },
19012     
19013     show : function()
19014     {
19015         this.picker().show();
19016         this.pop.show();
19017         this.update();
19018         this.place();
19019         
19020         this.fireEvent('show', this, this.date);
19021     },
19022     
19023     hide : function()
19024     {
19025         this.picker().hide();
19026         this.pop.hide();
19027         
19028         this.fireEvent('hide', this, this.date);
19029     },
19030     
19031     setTime : function()
19032     {
19033         this.hide();
19034         this.setValue(this.time.format(this.format));
19035         
19036         this.fireEvent('select', this, this.date);
19037         
19038         
19039     },
19040     
19041     onMousedown: function(e){
19042         e.stopPropagation();
19043         e.preventDefault();
19044     },
19045     
19046     onIncrementHours: function()
19047     {
19048         Roo.log('onIncrementHours');
19049         this.time = this.time.add(Date.HOUR, 1);
19050         this.update();
19051         
19052     },
19053     
19054     onDecrementHours: function()
19055     {
19056         Roo.log('onDecrementHours');
19057         this.time = this.time.add(Date.HOUR, -1);
19058         this.update();
19059     },
19060     
19061     onIncrementMinutes: function()
19062     {
19063         Roo.log('onIncrementMinutes');
19064         this.time = this.time.add(Date.MINUTE, 1);
19065         this.update();
19066     },
19067     
19068     onDecrementMinutes: function()
19069     {
19070         Roo.log('onDecrementMinutes');
19071         this.time = this.time.add(Date.MINUTE, -1);
19072         this.update();
19073     },
19074     
19075     onTogglePeriod: function()
19076     {
19077         Roo.log('onTogglePeriod');
19078         this.time = this.time.add(Date.HOUR, 12);
19079         this.update();
19080     }
19081     
19082    
19083 });
19084
19085 Roo.apply(Roo.bootstrap.TimeField,  {
19086     
19087     content : {
19088         tag: 'tbody',
19089         cn: [
19090             {
19091                 tag: 'tr',
19092                 cn: [
19093                 {
19094                     tag: 'td',
19095                     colspan: '7'
19096                 }
19097                 ]
19098             }
19099         ]
19100     },
19101     
19102     footer : {
19103         tag: 'tfoot',
19104         cn: [
19105             {
19106                 tag: 'tr',
19107                 cn: [
19108                 {
19109                     tag: 'th',
19110                     colspan: '7',
19111                     cls: '',
19112                     cn: [
19113                         {
19114                             tag: 'button',
19115                             cls: 'btn btn-info ok',
19116                             html: 'OK'
19117                         }
19118                     ]
19119                 }
19120
19121                 ]
19122             }
19123         ]
19124     }
19125 });
19126
19127 Roo.apply(Roo.bootstrap.TimeField,  {
19128   
19129     template : {
19130         tag: 'div',
19131         cls: 'datepicker dropdown-menu',
19132         cn: [
19133             {
19134                 tag: 'div',
19135                 cls: 'datepicker-time',
19136                 cn: [
19137                 {
19138                     tag: 'table',
19139                     cls: 'table-condensed',
19140                     cn:[
19141                     Roo.bootstrap.TimeField.content,
19142                     Roo.bootstrap.TimeField.footer
19143                     ]
19144                 }
19145                 ]
19146             }
19147         ]
19148     }
19149 });
19150
19151  
19152
19153  /*
19154  * - LGPL
19155  *
19156  * MonthField
19157  * 
19158  */
19159
19160 /**
19161  * @class Roo.bootstrap.MonthField
19162  * @extends Roo.bootstrap.Input
19163  * Bootstrap MonthField class
19164  * 
19165  * @cfg {String} language default en
19166  * 
19167  * @constructor
19168  * Create a new MonthField
19169  * @param {Object} config The config object
19170  */
19171
19172 Roo.bootstrap.MonthField = function(config){
19173     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19174     
19175     this.addEvents({
19176         /**
19177          * @event show
19178          * Fires when this field show.
19179          * @param {Roo.bootstrap.MonthField} this
19180          * @param {Mixed} date The date value
19181          */
19182         show : true,
19183         /**
19184          * @event show
19185          * Fires when this field hide.
19186          * @param {Roo.bootstrap.MonthField} this
19187          * @param {Mixed} date The date value
19188          */
19189         hide : true,
19190         /**
19191          * @event select
19192          * Fires when select a date.
19193          * @param {Roo.bootstrap.MonthField} this
19194          * @param {String} oldvalue The old value
19195          * @param {String} newvalue The new value
19196          */
19197         select : true
19198     });
19199 };
19200
19201 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19202     
19203     onRender: function(ct, position)
19204     {
19205         
19206         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19207         
19208         this.language = this.language || 'en';
19209         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19210         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19211         
19212         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19213         this.isInline = false;
19214         this.isInput = true;
19215         this.component = this.el.select('.add-on', true).first() || false;
19216         this.component = (this.component && this.component.length === 0) ? false : this.component;
19217         this.hasInput = this.component && this.inputEL().length;
19218         
19219         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19220         
19221         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19222         
19223         this.picker().on('mousedown', this.onMousedown, this);
19224         this.picker().on('click', this.onClick, this);
19225         
19226         this.picker().addClass('datepicker-dropdown');
19227         
19228         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19229             v.setStyle('width', '189px');
19230         });
19231         
19232         this.fillMonths();
19233         
19234         this.update();
19235         
19236         if(this.isInline) {
19237             this.show();
19238         }
19239         
19240     },
19241     
19242     setValue: function(v, suppressEvent)
19243     {   
19244         var o = this.getValue();
19245         
19246         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19247         
19248         this.update();
19249
19250         if(suppressEvent !== true){
19251             this.fireEvent('select', this, o, v);
19252         }
19253         
19254     },
19255     
19256     getValue: function()
19257     {
19258         return this.value;
19259     },
19260     
19261     onClick: function(e) 
19262     {
19263         e.stopPropagation();
19264         e.preventDefault();
19265         
19266         var target = e.getTarget();
19267         
19268         if(target.nodeName.toLowerCase() === 'i'){
19269             target = Roo.get(target).dom.parentNode;
19270         }
19271         
19272         var nodeName = target.nodeName;
19273         var className = target.className;
19274         var html = target.innerHTML;
19275         
19276         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19277             return;
19278         }
19279         
19280         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19281         
19282         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19283         
19284         this.hide();
19285                         
19286     },
19287     
19288     picker : function()
19289     {
19290         return this.pickerEl;
19291     },
19292     
19293     fillMonths: function()
19294     {    
19295         var i = 0;
19296         var months = this.picker().select('>.datepicker-months td', true).first();
19297         
19298         months.dom.innerHTML = '';
19299         
19300         while (i < 12) {
19301             var month = {
19302                 tag: 'span',
19303                 cls: 'month',
19304                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19305             };
19306             
19307             months.createChild(month);
19308         }
19309         
19310     },
19311     
19312     update: function()
19313     {
19314         var _this = this;
19315         
19316         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19317             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19318         }
19319         
19320         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19321             e.removeClass('active');
19322             
19323             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19324                 e.addClass('active');
19325             }
19326         })
19327     },
19328     
19329     place: function()
19330     {
19331         if(this.isInline) {
19332             return;
19333         }
19334         
19335         this.picker().removeClass(['bottom', 'top']);
19336         
19337         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19338             /*
19339              * place to the top of element!
19340              *
19341              */
19342             
19343             this.picker().addClass('top');
19344             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19345             
19346             return;
19347         }
19348         
19349         this.picker().addClass('bottom');
19350         
19351         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19352     },
19353     
19354     onFocus : function()
19355     {
19356         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19357         this.show();
19358     },
19359     
19360     onBlur : function()
19361     {
19362         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19363         
19364         var d = this.inputEl().getValue();
19365         
19366         this.setValue(d);
19367                 
19368         this.hide();
19369     },
19370     
19371     show : function()
19372     {
19373         this.picker().show();
19374         this.picker().select('>.datepicker-months', true).first().show();
19375         this.update();
19376         this.place();
19377         
19378         this.fireEvent('show', this, this.date);
19379     },
19380     
19381     hide : function()
19382     {
19383         if(this.isInline) {
19384             return;
19385         }
19386         this.picker().hide();
19387         this.fireEvent('hide', this, this.date);
19388         
19389     },
19390     
19391     onMousedown: function(e)
19392     {
19393         e.stopPropagation();
19394         e.preventDefault();
19395     },
19396     
19397     keyup: function(e)
19398     {
19399         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19400         this.update();
19401     },
19402
19403     fireKey: function(e)
19404     {
19405         if (!this.picker().isVisible()){
19406             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19407                 this.show();
19408             }
19409             return;
19410         }
19411         
19412         var dir;
19413         
19414         switch(e.keyCode){
19415             case 27: // escape
19416                 this.hide();
19417                 e.preventDefault();
19418                 break;
19419             case 37: // left
19420             case 39: // right
19421                 dir = e.keyCode == 37 ? -1 : 1;
19422                 
19423                 this.vIndex = this.vIndex + dir;
19424                 
19425                 if(this.vIndex < 0){
19426                     this.vIndex = 0;
19427                 }
19428                 
19429                 if(this.vIndex > 11){
19430                     this.vIndex = 11;
19431                 }
19432                 
19433                 if(isNaN(this.vIndex)){
19434                     this.vIndex = 0;
19435                 }
19436                 
19437                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19438                 
19439                 break;
19440             case 38: // up
19441             case 40: // down
19442                 
19443                 dir = e.keyCode == 38 ? -1 : 1;
19444                 
19445                 this.vIndex = this.vIndex + dir * 4;
19446                 
19447                 if(this.vIndex < 0){
19448                     this.vIndex = 0;
19449                 }
19450                 
19451                 if(this.vIndex > 11){
19452                     this.vIndex = 11;
19453                 }
19454                 
19455                 if(isNaN(this.vIndex)){
19456                     this.vIndex = 0;
19457                 }
19458                 
19459                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19460                 break;
19461                 
19462             case 13: // enter
19463                 
19464                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19465                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19466                 }
19467                 
19468                 this.hide();
19469                 e.preventDefault();
19470                 break;
19471             case 9: // tab
19472                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19473                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19474                 }
19475                 this.hide();
19476                 break;
19477             case 16: // shift
19478             case 17: // ctrl
19479             case 18: // alt
19480                 break;
19481             default :
19482                 this.hide();
19483                 
19484         }
19485     },
19486     
19487     remove: function() 
19488     {
19489         this.picker().remove();
19490     }
19491    
19492 });
19493
19494 Roo.apply(Roo.bootstrap.MonthField,  {
19495     
19496     content : {
19497         tag: 'tbody',
19498         cn: [
19499         {
19500             tag: 'tr',
19501             cn: [
19502             {
19503                 tag: 'td',
19504                 colspan: '7'
19505             }
19506             ]
19507         }
19508         ]
19509     },
19510     
19511     dates:{
19512         en: {
19513             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19514             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19515         }
19516     }
19517 });
19518
19519 Roo.apply(Roo.bootstrap.MonthField,  {
19520   
19521     template : {
19522         tag: 'div',
19523         cls: 'datepicker dropdown-menu roo-dynamic',
19524         cn: [
19525             {
19526                 tag: 'div',
19527                 cls: 'datepicker-months',
19528                 cn: [
19529                 {
19530                     tag: 'table',
19531                     cls: 'table-condensed',
19532                     cn:[
19533                         Roo.bootstrap.DateField.content
19534                     ]
19535                 }
19536                 ]
19537             }
19538         ]
19539     }
19540 });
19541
19542  
19543
19544  
19545  /*
19546  * - LGPL
19547  *
19548  * CheckBox
19549  * 
19550  */
19551
19552 /**
19553  * @class Roo.bootstrap.CheckBox
19554  * @extends Roo.bootstrap.Input
19555  * Bootstrap CheckBox class
19556  * 
19557  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19558  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19559  * @cfg {String} boxLabel The text that appears beside the checkbox
19560  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19561  * @cfg {Boolean} checked initnal the element
19562  * @cfg {Boolean} inline inline the element (default false)
19563  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19564  * 
19565  * @constructor
19566  * Create a new CheckBox
19567  * @param {Object} config The config object
19568  */
19569
19570 Roo.bootstrap.CheckBox = function(config){
19571     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19572    
19573     this.addEvents({
19574         /**
19575         * @event check
19576         * Fires when the element is checked or unchecked.
19577         * @param {Roo.bootstrap.CheckBox} this This input
19578         * @param {Boolean} checked The new checked value
19579         */
19580        check : true
19581     });
19582     
19583 };
19584
19585 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19586   
19587     inputType: 'checkbox',
19588     inputValue: 1,
19589     valueOff: 0,
19590     boxLabel: false,
19591     checked: false,
19592     weight : false,
19593     inline: false,
19594     
19595     getAutoCreate : function()
19596     {
19597         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19598         
19599         var id = Roo.id();
19600         
19601         var cfg = {};
19602         
19603         cfg.cls = 'form-group ' + this.inputType; //input-group
19604         
19605         if(this.inline){
19606             cfg.cls += ' ' + this.inputType + '-inline';
19607         }
19608         
19609         var input =  {
19610             tag: 'input',
19611             id : id,
19612             type : this.inputType,
19613             value : this.inputValue,
19614             cls : 'roo-' + this.inputType, //'form-box',
19615             placeholder : this.placeholder || ''
19616             
19617         };
19618         
19619         if(this.inputType != 'radio'){
19620             var hidden =  {
19621                 tag: 'input',
19622                 type : 'hidden',
19623                 cls : 'roo-hidden-value',
19624                 value : this.checked ? this.valueOff : this.inputValue
19625             };
19626         }
19627         
19628             
19629         if (this.weight) { // Validity check?
19630             cfg.cls += " " + this.inputType + "-" + this.weight;
19631         }
19632         
19633         if (this.disabled) {
19634             input.disabled=true;
19635         }
19636         
19637         if(this.checked){
19638             input.checked = this.checked;
19639             
19640         }
19641         
19642         
19643         if (this.name) {
19644             
19645             input.name = this.name;
19646             
19647             if(this.inputType != 'radio'){
19648                 hidden.name = this.name;
19649                 input.name = '_hidden_' + this.name;
19650             }
19651         }
19652         
19653         if (this.size) {
19654             input.cls += ' input-' + this.size;
19655         }
19656         
19657         var settings=this;
19658         
19659         ['xs','sm','md','lg'].map(function(size){
19660             if (settings[size]) {
19661                 cfg.cls += ' col-' + size + '-' + settings[size];
19662             }
19663         });
19664         
19665         var inputblock = input;
19666          
19667         if (this.before || this.after) {
19668             
19669             inputblock = {
19670                 cls : 'input-group',
19671                 cn :  [] 
19672             };
19673             
19674             if (this.before) {
19675                 inputblock.cn.push({
19676                     tag :'span',
19677                     cls : 'input-group-addon',
19678                     html : this.before
19679                 });
19680             }
19681             
19682             inputblock.cn.push(input);
19683             
19684             if(this.inputType != 'radio'){
19685                 inputblock.cn.push(hidden);
19686             }
19687             
19688             if (this.after) {
19689                 inputblock.cn.push({
19690                     tag :'span',
19691                     cls : 'input-group-addon',
19692                     html : this.after
19693                 });
19694             }
19695             
19696         }
19697         
19698         if (align ==='left' && this.fieldLabel.length) {
19699 //                Roo.log("left and has label");
19700                 cfg.cn = [
19701                     
19702                     {
19703                         tag: 'label',
19704                         'for' :  id,
19705                         cls : 'control-label col-md-' + this.labelWidth,
19706                         html : this.fieldLabel
19707                         
19708                     },
19709                     {
19710                         cls : "col-md-" + (12 - this.labelWidth), 
19711                         cn: [
19712                             inputblock
19713                         ]
19714                     }
19715                     
19716                 ];
19717         } else if ( this.fieldLabel.length) {
19718 //                Roo.log(" label");
19719                 cfg.cn = [
19720                    
19721                     {
19722                         tag: this.boxLabel ? 'span' : 'label',
19723                         'for': id,
19724                         cls: 'control-label box-input-label',
19725                         //cls : 'input-group-addon',
19726                         html : this.fieldLabel
19727                         
19728                     },
19729                     
19730                     inputblock
19731                     
19732                 ];
19733
19734         } else {
19735             
19736 //                Roo.log(" no label && no align");
19737                 cfg.cn = [  inputblock ] ;
19738                 
19739                 
19740         }
19741         
19742         if(this.boxLabel){
19743              var boxLabelCfg = {
19744                 tag: 'label',
19745                 //'for': id, // box label is handled by onclick - so no for...
19746                 cls: 'box-label',
19747                 html: this.boxLabel
19748             };
19749             
19750             if(this.tooltip){
19751                 boxLabelCfg.tooltip = this.tooltip;
19752             }
19753              
19754             cfg.cn.push(boxLabelCfg);
19755         }
19756         
19757         if(this.inputType != 'radio'){
19758             cfg.cn.push(hidden);
19759         }
19760         
19761         return cfg;
19762         
19763     },
19764     
19765     /**
19766      * return the real input element.
19767      */
19768     inputEl: function ()
19769     {
19770         return this.el.select('input.roo-' + this.inputType,true).first();
19771     },
19772     hiddenEl: function ()
19773     {
19774         return this.el.select('input.roo-hidden-value',true).first();
19775     },
19776     
19777     labelEl: function()
19778     {
19779         return this.el.select('label.control-label',true).first();
19780     },
19781     /* depricated... */
19782     
19783     label: function()
19784     {
19785         return this.labelEl();
19786     },
19787     
19788     boxLabelEl: function()
19789     {
19790         return this.el.select('label.box-label',true).first();
19791     },
19792     
19793     initEvents : function()
19794     {
19795 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19796         
19797         this.inputEl().on('click', this.onClick,  this);
19798         
19799         if (this.boxLabel) { 
19800             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19801         }
19802         
19803         this.startValue = this.getValue();
19804         
19805         if(this.groupId){
19806             Roo.bootstrap.CheckBox.register(this);
19807         }
19808     },
19809     
19810     onClick : function()
19811     {   
19812         this.setChecked(!this.checked);
19813     },
19814     
19815     setChecked : function(state,suppressEvent)
19816     {
19817         this.startValue = this.getValue();
19818
19819         if(this.inputType == 'radio'){
19820             
19821             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19822                 e.dom.checked = false;
19823             });
19824             
19825             this.inputEl().dom.checked = true;
19826             
19827             this.inputEl().dom.value = this.inputValue;
19828             
19829             if(suppressEvent !== true){
19830                 this.fireEvent('check', this, true);
19831             }
19832             
19833             this.validate();
19834             
19835             return;
19836         }
19837         
19838         this.checked = state;
19839         
19840         this.inputEl().dom.checked = state;
19841         
19842         
19843         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19844         
19845         if(suppressEvent !== true){
19846             this.fireEvent('check', this, state);
19847         }
19848         
19849         this.validate();
19850     },
19851     
19852     getValue : function()
19853     {
19854         if(this.inputType == 'radio'){
19855             return this.getGroupValue();
19856         }
19857         
19858         return this.hiddenEl().dom.value;
19859         
19860     },
19861     
19862     getGroupValue : function()
19863     {
19864         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19865             return '';
19866         }
19867         
19868         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19869     },
19870     
19871     setValue : function(v,suppressEvent)
19872     {
19873         if(this.inputType == 'radio'){
19874             this.setGroupValue(v, suppressEvent);
19875             return;
19876         }
19877         
19878         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19879         
19880         this.validate();
19881     },
19882     
19883     setGroupValue : function(v, suppressEvent)
19884     {
19885         this.startValue = this.getValue();
19886         
19887         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19888             e.dom.checked = false;
19889             
19890             if(e.dom.value == v){
19891                 e.dom.checked = true;
19892             }
19893         });
19894         
19895         if(suppressEvent !== true){
19896             this.fireEvent('check', this, true);
19897         }
19898
19899         this.validate();
19900         
19901         return;
19902     },
19903     
19904     validate : function()
19905     {
19906         if(
19907                 this.disabled || 
19908                 (this.inputType == 'radio' && this.validateRadio()) ||
19909                 (this.inputType == 'checkbox' && this.validateCheckbox())
19910         ){
19911             this.markValid();
19912             return true;
19913         }
19914         
19915         this.markInvalid();
19916         return false;
19917     },
19918     
19919     validateRadio : function()
19920     {
19921         if(this.allowBlank){
19922             return true;
19923         }
19924         
19925         var valid = false;
19926         
19927         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19928             if(!e.dom.checked){
19929                 return;
19930             }
19931             
19932             valid = true;
19933             
19934             return false;
19935         });
19936         
19937         return valid;
19938     },
19939     
19940     validateCheckbox : function()
19941     {
19942         if(!this.groupId){
19943             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19944         }
19945         
19946         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19947         
19948         if(!group){
19949             return false;
19950         }
19951         
19952         var r = false;
19953         
19954         for(var i in group){
19955             if(r){
19956                 break;
19957             }
19958             
19959             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19960         }
19961         
19962         return r;
19963     },
19964     
19965     /**
19966      * Mark this field as valid
19967      */
19968     markValid : function()
19969     {
19970         var _this = this;
19971         
19972         this.fireEvent('valid', this);
19973         
19974         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19975         
19976         if(this.groupId){
19977             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19978         }
19979         
19980         if(label){
19981             label.markValid();
19982         }
19983
19984         if(this.inputType == 'radio'){
19985             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19986                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19987                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19988             });
19989             
19990             return;
19991         }
19992         
19993         if(!this.groupId){
19994             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19995             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19996             return;
19997         }
19998         
19999         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20000             
20001         if(!group){
20002             return;
20003         }
20004         
20005         for(var i in group){
20006             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20007             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20008         }
20009     },
20010     
20011      /**
20012      * Mark this field as invalid
20013      * @param {String} msg The validation message
20014      */
20015     markInvalid : function(msg)
20016     {
20017         if(this.allowBlank){
20018             return;
20019         }
20020         
20021         var _this = this;
20022         
20023         this.fireEvent('invalid', this, msg);
20024         
20025         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20026         
20027         if(this.groupId){
20028             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20029         }
20030         
20031         if(label){
20032             label.markInvalid();
20033         }
20034             
20035         if(this.inputType == 'radio'){
20036             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20037                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20038                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20039             });
20040             
20041             return;
20042         }
20043         
20044         if(!this.groupId){
20045             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20046             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20047             return;
20048         }
20049         
20050         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20051         
20052         if(!group){
20053             return;
20054         }
20055         
20056         for(var i in group){
20057             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20058             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20059         }
20060         
20061     },
20062     
20063     clearInvalid : function()
20064     {
20065         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20066         
20067         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20068         
20069         if (label) {
20070             label.iconEl.removeClass(label.validClass);
20071             label.iconEl.removeClass(label.invalidClass);
20072         }
20073     },
20074     
20075     disable : function()
20076     {
20077         if(this.inputType != 'radio'){
20078             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20079             return;
20080         }
20081         
20082         var _this = this;
20083         
20084         if(this.rendered){
20085             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20086                 _this.getActionEl().addClass(this.disabledClass);
20087                 e.dom.disabled = true;
20088             });
20089         }
20090         
20091         this.disabled = true;
20092         this.fireEvent("disable", this);
20093         return this;
20094     },
20095
20096     enable : function()
20097     {
20098         if(this.inputType != 'radio'){
20099             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20100             return;
20101         }
20102         
20103         var _this = this;
20104         
20105         if(this.rendered){
20106             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20107                 _this.getActionEl().removeClass(this.disabledClass);
20108                 e.dom.disabled = false;
20109             });
20110         }
20111         
20112         this.disabled = false;
20113         this.fireEvent("enable", this);
20114         return this;
20115     }
20116
20117 });
20118
20119 Roo.apply(Roo.bootstrap.CheckBox, {
20120     
20121     groups: {},
20122     
20123      /**
20124     * register a CheckBox Group
20125     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20126     */
20127     register : function(checkbox)
20128     {
20129         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20130             this.groups[checkbox.groupId] = {};
20131         }
20132         
20133         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20134             return;
20135         }
20136         
20137         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20138         
20139     },
20140     /**
20141     * fetch a CheckBox Group based on the group ID
20142     * @param {string} the group ID
20143     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20144     */
20145     get: function(groupId) {
20146         if (typeof(this.groups[groupId]) == 'undefined') {
20147             return false;
20148         }
20149         
20150         return this.groups[groupId] ;
20151     }
20152     
20153     
20154 });
20155 /*
20156  * - LGPL
20157  *
20158  * Radio
20159  *
20160  *
20161  * not inline
20162  *<div class="radio">
20163   <label>
20164     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20165     Option one is this and that&mdash;be sure to include why it's great
20166   </label>
20167 </div>
20168  *
20169  *
20170  *inline
20171  *<span>
20172  *<label class="radio-inline">fieldLabel</label>
20173  *<label class="radio-inline">
20174   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20175 </label>
20176 <span>
20177  *
20178  *
20179  */
20180
20181 /**
20182  * @class Roo.bootstrap.Radio
20183  * @extends Roo.bootstrap.CheckBox
20184  * Bootstrap Radio class
20185
20186  * @constructor
20187  * Create a new Radio
20188  * @param {Object} config The config object
20189  */
20190
20191 Roo.bootstrap.Radio = function(config){
20192     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20193
20194 };
20195
20196 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20197
20198     inputType: 'radio',
20199     inputValue: '',
20200     valueOff: '',
20201
20202     getAutoCreate : function()
20203     {
20204         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20205         align = align || 'left'; // default...
20206
20207
20208
20209         var id = Roo.id();
20210
20211         var cfg = {
20212                 tag : this.inline ? 'span' : 'div',
20213                 cls : 'form-group',
20214                 cn : []
20215         };
20216
20217         var inline = this.inline ? ' radio-inline' : '';
20218
20219         var lbl = {
20220                 tag: 'label' ,
20221                 // does not need for, as we wrap the input with it..
20222                 'for' : id,
20223                 cls : 'control-label box-label' + inline,
20224                 cn : []
20225         };
20226         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20227
20228         var fieldLabel = {
20229             tag: 'label' ,
20230             //cls : 'control-label' + inline,
20231             html : this.fieldLabel,
20232             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20233         };
20234
20235         var input =  {
20236             tag: 'input',
20237             id : id,
20238             type : this.inputType,
20239             //value : (!this.checked) ? this.valueOff : this.inputValue,
20240             value : this.inputValue,
20241             cls : 'roo-radio',
20242             placeholder : this.placeholder || '' // ?? needed????
20243
20244         };
20245         if (this.weight) { // Validity check?
20246             input.cls += " radio-" + this.weight;
20247         }
20248         if (this.disabled) {
20249             input.disabled=true;
20250         }
20251
20252         if(this.checked){
20253             input.checked = this.checked;
20254         }
20255
20256         if (this.name) {
20257             input.name = this.name;
20258         }
20259
20260         if (this.size) {
20261             input.cls += ' input-' + this.size;
20262         }
20263
20264         //?? can span's inline have a width??
20265
20266         var settings=this;
20267         ['xs','sm','md','lg'].map(function(size){
20268             if (settings[size]) {
20269                 cfg.cls += ' col-' + size + '-' + settings[size];
20270             }
20271         });
20272
20273         var inputblock = input;
20274
20275         if (this.before || this.after) {
20276
20277             inputblock = {
20278                 cls : 'input-group',
20279                 tag : 'span',
20280                 cn :  []
20281             };
20282             if (this.before) {
20283                 inputblock.cn.push({
20284                     tag :'span',
20285                     cls : 'input-group-addon',
20286                     html : this.before
20287                 });
20288             }
20289             inputblock.cn.push(input);
20290             if (this.after) {
20291                 inputblock.cn.push({
20292                     tag :'span',
20293                     cls : 'input-group-addon',
20294                     html : this.after
20295                 });
20296             }
20297
20298         };
20299
20300
20301         if (this.fieldLabel && this.fieldLabel.length) {
20302             cfg.cn.push(fieldLabel);
20303         }
20304
20305         // normal bootstrap puts the input inside the label.
20306         // however with our styled version - it has to go after the input.
20307
20308         //lbl.cn.push(inputblock);
20309
20310         var lblwrap =  {
20311             tag: 'span',
20312             cls: 'radio' + inline,
20313             cn: [
20314                 inputblock,
20315                 lbl
20316             ]
20317         };
20318
20319         cfg.cn.push( lblwrap);
20320
20321         if(this.boxLabel){
20322             lbl.cn.push({
20323                 tag: 'span',
20324                 html: this.boxLabel
20325             })
20326         }
20327
20328
20329         return cfg;
20330
20331     },
20332
20333     initEvents : function()
20334     {
20335 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20336
20337         this.inputEl().on('click', this.onClick,  this);
20338         if (this.boxLabel) {
20339             //Roo.log('find label');
20340             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20341         }
20342
20343     },
20344
20345     inputEl: function ()
20346     {
20347         return this.el.select('input.roo-radio',true).first();
20348     },
20349     onClick : function()
20350     {
20351         Roo.log("click");
20352         this.setChecked(true);
20353     },
20354
20355     setChecked : function(state,suppressEvent)
20356     {
20357         if(state){
20358             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20359                 v.dom.checked = false;
20360             });
20361         }
20362         this.checked = state;
20363         this.inputEl().dom.checked = state;
20364
20365         if(suppressEvent !== true){
20366             this.fireEvent('check', this, state);
20367         }
20368         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20369         this.validate()
20370     },
20371
20372     getGroupValue : function()
20373     {
20374         var value = '';
20375         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20376             if(v.dom.checked == true){
20377                 value = v.dom.value;
20378             }
20379         });
20380
20381         return value;
20382     },
20383
20384     /**
20385      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20386      * @return {Mixed} value The field value
20387      */
20388     getValue : function(){
20389         return this.getGroupValue();
20390     }
20391
20392 });
20393 //<script type="text/javascript">
20394
20395 /*
20396  * Based  Ext JS Library 1.1.1
20397  * Copyright(c) 2006-2007, Ext JS, LLC.
20398  * LGPL
20399  *
20400  */
20401  
20402 /**
20403  * @class Roo.HtmlEditorCore
20404  * @extends Roo.Component
20405  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20406  *
20407  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20408  */
20409
20410 Roo.HtmlEditorCore = function(config){
20411     
20412     
20413     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20414     
20415     
20416     this.addEvents({
20417         /**
20418          * @event initialize
20419          * Fires when the editor is fully initialized (including the iframe)
20420          * @param {Roo.HtmlEditorCore} this
20421          */
20422         initialize: true,
20423         /**
20424          * @event activate
20425          * Fires when the editor is first receives the focus. Any insertion must wait
20426          * until after this event.
20427          * @param {Roo.HtmlEditorCore} this
20428          */
20429         activate: true,
20430          /**
20431          * @event beforesync
20432          * Fires before the textarea is updated with content from the editor iframe. Return false
20433          * to cancel the sync.
20434          * @param {Roo.HtmlEditorCore} this
20435          * @param {String} html
20436          */
20437         beforesync: true,
20438          /**
20439          * @event beforepush
20440          * Fires before the iframe editor is updated with content from the textarea. Return false
20441          * to cancel the push.
20442          * @param {Roo.HtmlEditorCore} this
20443          * @param {String} html
20444          */
20445         beforepush: true,
20446          /**
20447          * @event sync
20448          * Fires when the textarea is updated with content from the editor iframe.
20449          * @param {Roo.HtmlEditorCore} this
20450          * @param {String} html
20451          */
20452         sync: true,
20453          /**
20454          * @event push
20455          * Fires when the iframe editor is updated with content from the textarea.
20456          * @param {Roo.HtmlEditorCore} this
20457          * @param {String} html
20458          */
20459         push: true,
20460         
20461         /**
20462          * @event editorevent
20463          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20464          * @param {Roo.HtmlEditorCore} this
20465          */
20466         editorevent: true
20467         
20468     });
20469     
20470     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20471     
20472     // defaults : white / black...
20473     this.applyBlacklists();
20474     
20475     
20476     
20477 };
20478
20479
20480 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20481
20482
20483      /**
20484      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20485      */
20486     
20487     owner : false,
20488     
20489      /**
20490      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20491      *                        Roo.resizable.
20492      */
20493     resizable : false,
20494      /**
20495      * @cfg {Number} height (in pixels)
20496      */   
20497     height: 300,
20498    /**
20499      * @cfg {Number} width (in pixels)
20500      */   
20501     width: 500,
20502     
20503     /**
20504      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20505      * 
20506      */
20507     stylesheets: false,
20508     
20509     // id of frame..
20510     frameId: false,
20511     
20512     // private properties
20513     validationEvent : false,
20514     deferHeight: true,
20515     initialized : false,
20516     activated : false,
20517     sourceEditMode : false,
20518     onFocus : Roo.emptyFn,
20519     iframePad:3,
20520     hideMode:'offsets',
20521     
20522     clearUp: true,
20523     
20524     // blacklist + whitelisted elements..
20525     black: false,
20526     white: false,
20527      
20528     
20529
20530     /**
20531      * Protected method that will not generally be called directly. It
20532      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20533      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20534      */
20535     getDocMarkup : function(){
20536         // body styles..
20537         var st = '';
20538         
20539         // inherit styels from page...?? 
20540         if (this.stylesheets === false) {
20541             
20542             Roo.get(document.head).select('style').each(function(node) {
20543                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20544             });
20545             
20546             Roo.get(document.head).select('link').each(function(node) { 
20547                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20548             });
20549             
20550         } else if (!this.stylesheets.length) {
20551                 // simple..
20552                 st = '<style type="text/css">' +
20553                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20554                    '</style>';
20555         } else { 
20556             
20557         }
20558         
20559         st +=  '<style type="text/css">' +
20560             'IMG { cursor: pointer } ' +
20561         '</style>';
20562
20563         
20564         return '<html><head>' + st  +
20565             //<style type="text/css">' +
20566             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20567             //'</style>' +
20568             ' </head><body class="roo-htmleditor-body"></body></html>';
20569     },
20570
20571     // private
20572     onRender : function(ct, position)
20573     {
20574         var _t = this;
20575         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20576         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20577         
20578         
20579         this.el.dom.style.border = '0 none';
20580         this.el.dom.setAttribute('tabIndex', -1);
20581         this.el.addClass('x-hidden hide');
20582         
20583         
20584         
20585         if(Roo.isIE){ // fix IE 1px bogus margin
20586             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20587         }
20588        
20589         
20590         this.frameId = Roo.id();
20591         
20592          
20593         
20594         var iframe = this.owner.wrap.createChild({
20595             tag: 'iframe',
20596             cls: 'form-control', // bootstrap..
20597             id: this.frameId,
20598             name: this.frameId,
20599             frameBorder : 'no',
20600             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20601         }, this.el
20602         );
20603         
20604         
20605         this.iframe = iframe.dom;
20606
20607          this.assignDocWin();
20608         
20609         this.doc.designMode = 'on';
20610        
20611         this.doc.open();
20612         this.doc.write(this.getDocMarkup());
20613         this.doc.close();
20614
20615         
20616         var task = { // must defer to wait for browser to be ready
20617             run : function(){
20618                 //console.log("run task?" + this.doc.readyState);
20619                 this.assignDocWin();
20620                 if(this.doc.body || this.doc.readyState == 'complete'){
20621                     try {
20622                         this.doc.designMode="on";
20623                     } catch (e) {
20624                         return;
20625                     }
20626                     Roo.TaskMgr.stop(task);
20627                     this.initEditor.defer(10, this);
20628                 }
20629             },
20630             interval : 10,
20631             duration: 10000,
20632             scope: this
20633         };
20634         Roo.TaskMgr.start(task);
20635
20636     },
20637
20638     // private
20639     onResize : function(w, h)
20640     {
20641          Roo.log('resize: ' +w + ',' + h );
20642         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20643         if(!this.iframe){
20644             return;
20645         }
20646         if(typeof w == 'number'){
20647             
20648             this.iframe.style.width = w + 'px';
20649         }
20650         if(typeof h == 'number'){
20651             
20652             this.iframe.style.height = h + 'px';
20653             if(this.doc){
20654                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20655             }
20656         }
20657         
20658     },
20659
20660     /**
20661      * Toggles the editor between standard and source edit mode.
20662      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20663      */
20664     toggleSourceEdit : function(sourceEditMode){
20665         
20666         this.sourceEditMode = sourceEditMode === true;
20667         
20668         if(this.sourceEditMode){
20669  
20670             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20671             
20672         }else{
20673             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20674             //this.iframe.className = '';
20675             this.deferFocus();
20676         }
20677         //this.setSize(this.owner.wrap.getSize());
20678         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20679     },
20680
20681     
20682   
20683
20684     /**
20685      * Protected method that will not generally be called directly. If you need/want
20686      * custom HTML cleanup, this is the method you should override.
20687      * @param {String} html The HTML to be cleaned
20688      * return {String} The cleaned HTML
20689      */
20690     cleanHtml : function(html){
20691         html = String(html);
20692         if(html.length > 5){
20693             if(Roo.isSafari){ // strip safari nonsense
20694                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20695             }
20696         }
20697         if(html == '&nbsp;'){
20698             html = '';
20699         }
20700         return html;
20701     },
20702
20703     /**
20704      * HTML Editor -> Textarea
20705      * Protected method that will not generally be called directly. Syncs the contents
20706      * of the editor iframe with the textarea.
20707      */
20708     syncValue : function(){
20709         if(this.initialized){
20710             var bd = (this.doc.body || this.doc.documentElement);
20711             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20712             var html = bd.innerHTML;
20713             if(Roo.isSafari){
20714                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20715                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20716                 if(m && m[1]){
20717                     html = '<div style="'+m[0]+'">' + html + '</div>';
20718                 }
20719             }
20720             html = this.cleanHtml(html);
20721             // fix up the special chars.. normaly like back quotes in word...
20722             // however we do not want to do this with chinese..
20723             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20724                 var cc = b.charCodeAt();
20725                 if (
20726                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20727                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20728                     (cc >= 0xf900 && cc < 0xfb00 )
20729                 ) {
20730                         return b;
20731                 }
20732                 return "&#"+cc+";" 
20733             });
20734             if(this.owner.fireEvent('beforesync', this, html) !== false){
20735                 this.el.dom.value = html;
20736                 this.owner.fireEvent('sync', this, html);
20737             }
20738         }
20739     },
20740
20741     /**
20742      * Protected method that will not generally be called directly. Pushes the value of the textarea
20743      * into the iframe editor.
20744      */
20745     pushValue : function(){
20746         if(this.initialized){
20747             var v = this.el.dom.value.trim();
20748             
20749 //            if(v.length < 1){
20750 //                v = '&#160;';
20751 //            }
20752             
20753             if(this.owner.fireEvent('beforepush', this, v) !== false){
20754                 var d = (this.doc.body || this.doc.documentElement);
20755                 d.innerHTML = v;
20756                 this.cleanUpPaste();
20757                 this.el.dom.value = d.innerHTML;
20758                 this.owner.fireEvent('push', this, v);
20759             }
20760         }
20761     },
20762
20763     // private
20764     deferFocus : function(){
20765         this.focus.defer(10, this);
20766     },
20767
20768     // doc'ed in Field
20769     focus : function(){
20770         if(this.win && !this.sourceEditMode){
20771             this.win.focus();
20772         }else{
20773             this.el.focus();
20774         }
20775     },
20776     
20777     assignDocWin: function()
20778     {
20779         var iframe = this.iframe;
20780         
20781          if(Roo.isIE){
20782             this.doc = iframe.contentWindow.document;
20783             this.win = iframe.contentWindow;
20784         } else {
20785 //            if (!Roo.get(this.frameId)) {
20786 //                return;
20787 //            }
20788 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20789 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20790             
20791             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20792                 return;
20793             }
20794             
20795             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20796             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20797         }
20798     },
20799     
20800     // private
20801     initEditor : function(){
20802         //console.log("INIT EDITOR");
20803         this.assignDocWin();
20804         
20805         
20806         
20807         this.doc.designMode="on";
20808         this.doc.open();
20809         this.doc.write(this.getDocMarkup());
20810         this.doc.close();
20811         
20812         var dbody = (this.doc.body || this.doc.documentElement);
20813         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20814         // this copies styles from the containing element into thsi one..
20815         // not sure why we need all of this..
20816         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20817         
20818         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20819         //ss['background-attachment'] = 'fixed'; // w3c
20820         dbody.bgProperties = 'fixed'; // ie
20821         //Roo.DomHelper.applyStyles(dbody, ss);
20822         Roo.EventManager.on(this.doc, {
20823             //'mousedown': this.onEditorEvent,
20824             'mouseup': this.onEditorEvent,
20825             'dblclick': this.onEditorEvent,
20826             'click': this.onEditorEvent,
20827             'keyup': this.onEditorEvent,
20828             buffer:100,
20829             scope: this
20830         });
20831         if(Roo.isGecko){
20832             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20833         }
20834         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20835             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20836         }
20837         this.initialized = true;
20838
20839         this.owner.fireEvent('initialize', this);
20840         this.pushValue();
20841     },
20842
20843     // private
20844     onDestroy : function(){
20845         
20846         
20847         
20848         if(this.rendered){
20849             
20850             //for (var i =0; i < this.toolbars.length;i++) {
20851             //    // fixme - ask toolbars for heights?
20852             //    this.toolbars[i].onDestroy();
20853            // }
20854             
20855             //this.wrap.dom.innerHTML = '';
20856             //this.wrap.remove();
20857         }
20858     },
20859
20860     // private
20861     onFirstFocus : function(){
20862         
20863         this.assignDocWin();
20864         
20865         
20866         this.activated = true;
20867          
20868     
20869         if(Roo.isGecko){ // prevent silly gecko errors
20870             this.win.focus();
20871             var s = this.win.getSelection();
20872             if(!s.focusNode || s.focusNode.nodeType != 3){
20873                 var r = s.getRangeAt(0);
20874                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20875                 r.collapse(true);
20876                 this.deferFocus();
20877             }
20878             try{
20879                 this.execCmd('useCSS', true);
20880                 this.execCmd('styleWithCSS', false);
20881             }catch(e){}
20882         }
20883         this.owner.fireEvent('activate', this);
20884     },
20885
20886     // private
20887     adjustFont: function(btn){
20888         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20889         //if(Roo.isSafari){ // safari
20890         //    adjust *= 2;
20891        // }
20892         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20893         if(Roo.isSafari){ // safari
20894             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20895             v =  (v < 10) ? 10 : v;
20896             v =  (v > 48) ? 48 : v;
20897             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20898             
20899         }
20900         
20901         
20902         v = Math.max(1, v+adjust);
20903         
20904         this.execCmd('FontSize', v  );
20905     },
20906
20907     onEditorEvent : function(e)
20908     {
20909         this.owner.fireEvent('editorevent', this, e);
20910       //  this.updateToolbar();
20911         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20912     },
20913
20914     insertTag : function(tg)
20915     {
20916         // could be a bit smarter... -> wrap the current selected tRoo..
20917         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20918             
20919             range = this.createRange(this.getSelection());
20920             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20921             wrappingNode.appendChild(range.extractContents());
20922             range.insertNode(wrappingNode);
20923
20924             return;
20925             
20926             
20927             
20928         }
20929         this.execCmd("formatblock",   tg);
20930         
20931     },
20932     
20933     insertText : function(txt)
20934     {
20935         
20936         
20937         var range = this.createRange();
20938         range.deleteContents();
20939                //alert(Sender.getAttribute('label'));
20940                
20941         range.insertNode(this.doc.createTextNode(txt));
20942     } ,
20943     
20944      
20945
20946     /**
20947      * Executes a Midas editor command on the editor document and performs necessary focus and
20948      * toolbar updates. <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     relayCmd : function(cmd, value){
20953         this.win.focus();
20954         this.execCmd(cmd, value);
20955         this.owner.fireEvent('editorevent', this);
20956         //this.updateToolbar();
20957         this.owner.deferFocus();
20958     },
20959
20960     /**
20961      * Executes a Midas editor command directly on the editor document.
20962      * For visual commands, you should use {@link #relayCmd} instead.
20963      * <b>This should only be called after the editor is initialized.</b>
20964      * @param {String} cmd The Midas command
20965      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20966      */
20967     execCmd : function(cmd, value){
20968         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20969         this.syncValue();
20970     },
20971  
20972  
20973    
20974     /**
20975      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20976      * to insert tRoo.
20977      * @param {String} text | dom node.. 
20978      */
20979     insertAtCursor : function(text)
20980     {
20981         
20982         
20983         
20984         if(!this.activated){
20985             return;
20986         }
20987         /*
20988         if(Roo.isIE){
20989             this.win.focus();
20990             var r = this.doc.selection.createRange();
20991             if(r){
20992                 r.collapse(true);
20993                 r.pasteHTML(text);
20994                 this.syncValue();
20995                 this.deferFocus();
20996             
20997             }
20998             return;
20999         }
21000         */
21001         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21002             this.win.focus();
21003             
21004             
21005             // from jquery ui (MIT licenced)
21006             var range, node;
21007             var win = this.win;
21008             
21009             if (win.getSelection && win.getSelection().getRangeAt) {
21010                 range = win.getSelection().getRangeAt(0);
21011                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21012                 range.insertNode(node);
21013             } else if (win.document.selection && win.document.selection.createRange) {
21014                 // no firefox support
21015                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21016                 win.document.selection.createRange().pasteHTML(txt);
21017             } else {
21018                 // no firefox support
21019                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21020                 this.execCmd('InsertHTML', txt);
21021             } 
21022             
21023             this.syncValue();
21024             
21025             this.deferFocus();
21026         }
21027     },
21028  // private
21029     mozKeyPress : function(e){
21030         if(e.ctrlKey){
21031             var c = e.getCharCode(), cmd;
21032           
21033             if(c > 0){
21034                 c = String.fromCharCode(c).toLowerCase();
21035                 switch(c){
21036                     case 'b':
21037                         cmd = 'bold';
21038                         break;
21039                     case 'i':
21040                         cmd = 'italic';
21041                         break;
21042                     
21043                     case 'u':
21044                         cmd = 'underline';
21045                         break;
21046                     
21047                     case 'v':
21048                         this.cleanUpPaste.defer(100, this);
21049                         return;
21050                         
21051                 }
21052                 if(cmd){
21053                     this.win.focus();
21054                     this.execCmd(cmd);
21055                     this.deferFocus();
21056                     e.preventDefault();
21057                 }
21058                 
21059             }
21060         }
21061     },
21062
21063     // private
21064     fixKeys : function(){ // load time branching for fastest keydown performance
21065         if(Roo.isIE){
21066             return function(e){
21067                 var k = e.getKey(), r;
21068                 if(k == e.TAB){
21069                     e.stopEvent();
21070                     r = this.doc.selection.createRange();
21071                     if(r){
21072                         r.collapse(true);
21073                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21074                         this.deferFocus();
21075                     }
21076                     return;
21077                 }
21078                 
21079                 if(k == e.ENTER){
21080                     r = this.doc.selection.createRange();
21081                     if(r){
21082                         var target = r.parentElement();
21083                         if(!target || target.tagName.toLowerCase() != 'li'){
21084                             e.stopEvent();
21085                             r.pasteHTML('<br />');
21086                             r.collapse(false);
21087                             r.select();
21088                         }
21089                     }
21090                 }
21091                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21092                     this.cleanUpPaste.defer(100, this);
21093                     return;
21094                 }
21095                 
21096                 
21097             };
21098         }else if(Roo.isOpera){
21099             return function(e){
21100                 var k = e.getKey();
21101                 if(k == e.TAB){
21102                     e.stopEvent();
21103                     this.win.focus();
21104                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21105                     this.deferFocus();
21106                 }
21107                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21108                     this.cleanUpPaste.defer(100, this);
21109                     return;
21110                 }
21111                 
21112             };
21113         }else if(Roo.isSafari){
21114             return function(e){
21115                 var k = e.getKey();
21116                 
21117                 if(k == e.TAB){
21118                     e.stopEvent();
21119                     this.execCmd('InsertText','\t');
21120                     this.deferFocus();
21121                     return;
21122                 }
21123                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21124                     this.cleanUpPaste.defer(100, this);
21125                     return;
21126                 }
21127                 
21128              };
21129         }
21130     }(),
21131     
21132     getAllAncestors: function()
21133     {
21134         var p = this.getSelectedNode();
21135         var a = [];
21136         if (!p) {
21137             a.push(p); // push blank onto stack..
21138             p = this.getParentElement();
21139         }
21140         
21141         
21142         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21143             a.push(p);
21144             p = p.parentNode;
21145         }
21146         a.push(this.doc.body);
21147         return a;
21148     },
21149     lastSel : false,
21150     lastSelNode : false,
21151     
21152     
21153     getSelection : function() 
21154     {
21155         this.assignDocWin();
21156         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21157     },
21158     
21159     getSelectedNode: function() 
21160     {
21161         // this may only work on Gecko!!!
21162         
21163         // should we cache this!!!!
21164         
21165         
21166         
21167          
21168         var range = this.createRange(this.getSelection()).cloneRange();
21169         
21170         if (Roo.isIE) {
21171             var parent = range.parentElement();
21172             while (true) {
21173                 var testRange = range.duplicate();
21174                 testRange.moveToElementText(parent);
21175                 if (testRange.inRange(range)) {
21176                     break;
21177                 }
21178                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21179                     break;
21180                 }
21181                 parent = parent.parentElement;
21182             }
21183             return parent;
21184         }
21185         
21186         // is ancestor a text element.
21187         var ac =  range.commonAncestorContainer;
21188         if (ac.nodeType == 3) {
21189             ac = ac.parentNode;
21190         }
21191         
21192         var ar = ac.childNodes;
21193          
21194         var nodes = [];
21195         var other_nodes = [];
21196         var has_other_nodes = false;
21197         for (var i=0;i<ar.length;i++) {
21198             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21199                 continue;
21200             }
21201             // fullly contained node.
21202             
21203             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21204                 nodes.push(ar[i]);
21205                 continue;
21206             }
21207             
21208             // probably selected..
21209             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21210                 other_nodes.push(ar[i]);
21211                 continue;
21212             }
21213             // outer..
21214             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21215                 continue;
21216             }
21217             
21218             
21219             has_other_nodes = true;
21220         }
21221         if (!nodes.length && other_nodes.length) {
21222             nodes= other_nodes;
21223         }
21224         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21225             return false;
21226         }
21227         
21228         return nodes[0];
21229     },
21230     createRange: function(sel)
21231     {
21232         // this has strange effects when using with 
21233         // top toolbar - not sure if it's a great idea.
21234         //this.editor.contentWindow.focus();
21235         if (typeof sel != "undefined") {
21236             try {
21237                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21238             } catch(e) {
21239                 return this.doc.createRange();
21240             }
21241         } else {
21242             return this.doc.createRange();
21243         }
21244     },
21245     getParentElement: function()
21246     {
21247         
21248         this.assignDocWin();
21249         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21250         
21251         var range = this.createRange(sel);
21252          
21253         try {
21254             var p = range.commonAncestorContainer;
21255             while (p.nodeType == 3) { // text node
21256                 p = p.parentNode;
21257             }
21258             return p;
21259         } catch (e) {
21260             return null;
21261         }
21262     
21263     },
21264     /***
21265      *
21266      * Range intersection.. the hard stuff...
21267      *  '-1' = before
21268      *  '0' = hits..
21269      *  '1' = after.
21270      *         [ -- selected range --- ]
21271      *   [fail]                        [fail]
21272      *
21273      *    basically..
21274      *      if end is before start or  hits it. fail.
21275      *      if start is after end or hits it fail.
21276      *
21277      *   if either hits (but other is outside. - then it's not 
21278      *   
21279      *    
21280      **/
21281     
21282     
21283     // @see http://www.thismuchiknow.co.uk/?p=64.
21284     rangeIntersectsNode : function(range, node)
21285     {
21286         var nodeRange = node.ownerDocument.createRange();
21287         try {
21288             nodeRange.selectNode(node);
21289         } catch (e) {
21290             nodeRange.selectNodeContents(node);
21291         }
21292     
21293         var rangeStartRange = range.cloneRange();
21294         rangeStartRange.collapse(true);
21295     
21296         var rangeEndRange = range.cloneRange();
21297         rangeEndRange.collapse(false);
21298     
21299         var nodeStartRange = nodeRange.cloneRange();
21300         nodeStartRange.collapse(true);
21301     
21302         var nodeEndRange = nodeRange.cloneRange();
21303         nodeEndRange.collapse(false);
21304     
21305         return rangeStartRange.compareBoundaryPoints(
21306                  Range.START_TO_START, nodeEndRange) == -1 &&
21307                rangeEndRange.compareBoundaryPoints(
21308                  Range.START_TO_START, nodeStartRange) == 1;
21309         
21310          
21311     },
21312     rangeCompareNode : function(range, node)
21313     {
21314         var nodeRange = node.ownerDocument.createRange();
21315         try {
21316             nodeRange.selectNode(node);
21317         } catch (e) {
21318             nodeRange.selectNodeContents(node);
21319         }
21320         
21321         
21322         range.collapse(true);
21323     
21324         nodeRange.collapse(true);
21325      
21326         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21327         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21328          
21329         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21330         
21331         var nodeIsBefore   =  ss == 1;
21332         var nodeIsAfter    = ee == -1;
21333         
21334         if (nodeIsBefore && nodeIsAfter) {
21335             return 0; // outer
21336         }
21337         if (!nodeIsBefore && nodeIsAfter) {
21338             return 1; //right trailed.
21339         }
21340         
21341         if (nodeIsBefore && !nodeIsAfter) {
21342             return 2;  // left trailed.
21343         }
21344         // fully contined.
21345         return 3;
21346     },
21347
21348     // private? - in a new class?
21349     cleanUpPaste :  function()
21350     {
21351         // cleans up the whole document..
21352         Roo.log('cleanuppaste');
21353         
21354         this.cleanUpChildren(this.doc.body);
21355         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21356         if (clean != this.doc.body.innerHTML) {
21357             this.doc.body.innerHTML = clean;
21358         }
21359         
21360     },
21361     
21362     cleanWordChars : function(input) {// change the chars to hex code
21363         var he = Roo.HtmlEditorCore;
21364         
21365         var output = input;
21366         Roo.each(he.swapCodes, function(sw) { 
21367             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21368             
21369             output = output.replace(swapper, sw[1]);
21370         });
21371         
21372         return output;
21373     },
21374     
21375     
21376     cleanUpChildren : function (n)
21377     {
21378         if (!n.childNodes.length) {
21379             return;
21380         }
21381         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21382            this.cleanUpChild(n.childNodes[i]);
21383         }
21384     },
21385     
21386     
21387         
21388     
21389     cleanUpChild : function (node)
21390     {
21391         var ed = this;
21392         //console.log(node);
21393         if (node.nodeName == "#text") {
21394             // clean up silly Windows -- stuff?
21395             return; 
21396         }
21397         if (node.nodeName == "#comment") {
21398             node.parentNode.removeChild(node);
21399             // clean up silly Windows -- stuff?
21400             return; 
21401         }
21402         var lcname = node.tagName.toLowerCase();
21403         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21404         // whitelist of tags..
21405         
21406         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21407             // remove node.
21408             node.parentNode.removeChild(node);
21409             return;
21410             
21411         }
21412         
21413         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21414         
21415         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21416         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21417         
21418         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21419         //    remove_keep_children = true;
21420         //}
21421         
21422         if (remove_keep_children) {
21423             this.cleanUpChildren(node);
21424             // inserts everything just before this node...
21425             while (node.childNodes.length) {
21426                 var cn = node.childNodes[0];
21427                 node.removeChild(cn);
21428                 node.parentNode.insertBefore(cn, node);
21429             }
21430             node.parentNode.removeChild(node);
21431             return;
21432         }
21433         
21434         if (!node.attributes || !node.attributes.length) {
21435             this.cleanUpChildren(node);
21436             return;
21437         }
21438         
21439         function cleanAttr(n,v)
21440         {
21441             
21442             if (v.match(/^\./) || v.match(/^\//)) {
21443                 return;
21444             }
21445             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21446                 return;
21447             }
21448             if (v.match(/^#/)) {
21449                 return;
21450             }
21451 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21452             node.removeAttribute(n);
21453             
21454         }
21455         
21456         var cwhite = this.cwhite;
21457         var cblack = this.cblack;
21458             
21459         function cleanStyle(n,v)
21460         {
21461             if (v.match(/expression/)) { //XSS?? should we even bother..
21462                 node.removeAttribute(n);
21463                 return;
21464             }
21465             
21466             var parts = v.split(/;/);
21467             var clean = [];
21468             
21469             Roo.each(parts, function(p) {
21470                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21471                 if (!p.length) {
21472                     return true;
21473                 }
21474                 var l = p.split(':').shift().replace(/\s+/g,'');
21475                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21476                 
21477                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21478 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21479                     //node.removeAttribute(n);
21480                     return true;
21481                 }
21482                 //Roo.log()
21483                 // only allow 'c whitelisted system attributes'
21484                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21485 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21486                     //node.removeAttribute(n);
21487                     return true;
21488                 }
21489                 
21490                 
21491                  
21492                 
21493                 clean.push(p);
21494                 return true;
21495             });
21496             if (clean.length) { 
21497                 node.setAttribute(n, clean.join(';'));
21498             } else {
21499                 node.removeAttribute(n);
21500             }
21501             
21502         }
21503         
21504         
21505         for (var i = node.attributes.length-1; i > -1 ; i--) {
21506             var a = node.attributes[i];
21507             //console.log(a);
21508             
21509             if (a.name.toLowerCase().substr(0,2)=='on')  {
21510                 node.removeAttribute(a.name);
21511                 continue;
21512             }
21513             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21514                 node.removeAttribute(a.name);
21515                 continue;
21516             }
21517             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21518                 cleanAttr(a.name,a.value); // fixme..
21519                 continue;
21520             }
21521             if (a.name == 'style') {
21522                 cleanStyle(a.name,a.value);
21523                 continue;
21524             }
21525             /// clean up MS crap..
21526             // tecnically this should be a list of valid class'es..
21527             
21528             
21529             if (a.name == 'class') {
21530                 if (a.value.match(/^Mso/)) {
21531                     node.className = '';
21532                 }
21533                 
21534                 if (a.value.match(/body/)) {
21535                     node.className = '';
21536                 }
21537                 continue;
21538             }
21539             
21540             // style cleanup!?
21541             // class cleanup?
21542             
21543         }
21544         
21545         
21546         this.cleanUpChildren(node);
21547         
21548         
21549     },
21550     
21551     /**
21552      * Clean up MS wordisms...
21553      */
21554     cleanWord : function(node)
21555     {
21556         
21557         
21558         if (!node) {
21559             this.cleanWord(this.doc.body);
21560             return;
21561         }
21562         if (node.nodeName == "#text") {
21563             // clean up silly Windows -- stuff?
21564             return; 
21565         }
21566         if (node.nodeName == "#comment") {
21567             node.parentNode.removeChild(node);
21568             // clean up silly Windows -- stuff?
21569             return; 
21570         }
21571         
21572         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21573             node.parentNode.removeChild(node);
21574             return;
21575         }
21576         
21577         // remove - but keep children..
21578         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21579             while (node.childNodes.length) {
21580                 var cn = node.childNodes[0];
21581                 node.removeChild(cn);
21582                 node.parentNode.insertBefore(cn, node);
21583             }
21584             node.parentNode.removeChild(node);
21585             this.iterateChildren(node, this.cleanWord);
21586             return;
21587         }
21588         // clean styles
21589         if (node.className.length) {
21590             
21591             var cn = node.className.split(/\W+/);
21592             var cna = [];
21593             Roo.each(cn, function(cls) {
21594                 if (cls.match(/Mso[a-zA-Z]+/)) {
21595                     return;
21596                 }
21597                 cna.push(cls);
21598             });
21599             node.className = cna.length ? cna.join(' ') : '';
21600             if (!cna.length) {
21601                 node.removeAttribute("class");
21602             }
21603         }
21604         
21605         if (node.hasAttribute("lang")) {
21606             node.removeAttribute("lang");
21607         }
21608         
21609         if (node.hasAttribute("style")) {
21610             
21611             var styles = node.getAttribute("style").split(";");
21612             var nstyle = [];
21613             Roo.each(styles, function(s) {
21614                 if (!s.match(/:/)) {
21615                     return;
21616                 }
21617                 var kv = s.split(":");
21618                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21619                     return;
21620                 }
21621                 // what ever is left... we allow.
21622                 nstyle.push(s);
21623             });
21624             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21625             if (!nstyle.length) {
21626                 node.removeAttribute('style');
21627             }
21628         }
21629         this.iterateChildren(node, this.cleanWord);
21630         
21631         
21632         
21633     },
21634     /**
21635      * iterateChildren of a Node, calling fn each time, using this as the scole..
21636      * @param {DomNode} node node to iterate children of.
21637      * @param {Function} fn method of this class to call on each item.
21638      */
21639     iterateChildren : function(node, fn)
21640     {
21641         if (!node.childNodes.length) {
21642                 return;
21643         }
21644         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21645            fn.call(this, node.childNodes[i])
21646         }
21647     },
21648     
21649     
21650     /**
21651      * cleanTableWidths.
21652      *
21653      * Quite often pasting from word etc.. results in tables with column and widths.
21654      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21655      *
21656      */
21657     cleanTableWidths : function(node)
21658     {
21659          
21660          
21661         if (!node) {
21662             this.cleanTableWidths(this.doc.body);
21663             return;
21664         }
21665         
21666         // ignore list...
21667         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21668             return; 
21669         }
21670         Roo.log(node.tagName);
21671         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21672             this.iterateChildren(node, this.cleanTableWidths);
21673             return;
21674         }
21675         if (node.hasAttribute('width')) {
21676             node.removeAttribute('width');
21677         }
21678         
21679          
21680         if (node.hasAttribute("style")) {
21681             // pretty basic...
21682             
21683             var styles = node.getAttribute("style").split(";");
21684             var nstyle = [];
21685             Roo.each(styles, function(s) {
21686                 if (!s.match(/:/)) {
21687                     return;
21688                 }
21689                 var kv = s.split(":");
21690                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21691                     return;
21692                 }
21693                 // what ever is left... we allow.
21694                 nstyle.push(s);
21695             });
21696             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21697             if (!nstyle.length) {
21698                 node.removeAttribute('style');
21699             }
21700         }
21701         
21702         this.iterateChildren(node, this.cleanTableWidths);
21703         
21704         
21705     },
21706     
21707     
21708     
21709     
21710     domToHTML : function(currentElement, depth, nopadtext) {
21711         
21712         depth = depth || 0;
21713         nopadtext = nopadtext || false;
21714     
21715         if (!currentElement) {
21716             return this.domToHTML(this.doc.body);
21717         }
21718         
21719         //Roo.log(currentElement);
21720         var j;
21721         var allText = false;
21722         var nodeName = currentElement.nodeName;
21723         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21724         
21725         if  (nodeName == '#text') {
21726             
21727             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21728         }
21729         
21730         
21731         var ret = '';
21732         if (nodeName != 'BODY') {
21733              
21734             var i = 0;
21735             // Prints the node tagName, such as <A>, <IMG>, etc
21736             if (tagName) {
21737                 var attr = [];
21738                 for(i = 0; i < currentElement.attributes.length;i++) {
21739                     // quoting?
21740                     var aname = currentElement.attributes.item(i).name;
21741                     if (!currentElement.attributes.item(i).value.length) {
21742                         continue;
21743                     }
21744                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21745                 }
21746                 
21747                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21748             } 
21749             else {
21750                 
21751                 // eack
21752             }
21753         } else {
21754             tagName = false;
21755         }
21756         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21757             return ret;
21758         }
21759         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21760             nopadtext = true;
21761         }
21762         
21763         
21764         // Traverse the tree
21765         i = 0;
21766         var currentElementChild = currentElement.childNodes.item(i);
21767         var allText = true;
21768         var innerHTML  = '';
21769         lastnode = '';
21770         while (currentElementChild) {
21771             // Formatting code (indent the tree so it looks nice on the screen)
21772             var nopad = nopadtext;
21773             if (lastnode == 'SPAN') {
21774                 nopad  = true;
21775             }
21776             // text
21777             if  (currentElementChild.nodeName == '#text') {
21778                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21779                 toadd = nopadtext ? toadd : toadd.trim();
21780                 if (!nopad && toadd.length > 80) {
21781                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21782                 }
21783                 innerHTML  += toadd;
21784                 
21785                 i++;
21786                 currentElementChild = currentElement.childNodes.item(i);
21787                 lastNode = '';
21788                 continue;
21789             }
21790             allText = false;
21791             
21792             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21793                 
21794             // Recursively traverse the tree structure of the child node
21795             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21796             lastnode = currentElementChild.nodeName;
21797             i++;
21798             currentElementChild=currentElement.childNodes.item(i);
21799         }
21800         
21801         ret += innerHTML;
21802         
21803         if (!allText) {
21804                 // The remaining code is mostly for formatting the tree
21805             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21806         }
21807         
21808         
21809         if (tagName) {
21810             ret+= "</"+tagName+">";
21811         }
21812         return ret;
21813         
21814     },
21815         
21816     applyBlacklists : function()
21817     {
21818         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21819         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21820         
21821         this.white = [];
21822         this.black = [];
21823         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21824             if (b.indexOf(tag) > -1) {
21825                 return;
21826             }
21827             this.white.push(tag);
21828             
21829         }, this);
21830         
21831         Roo.each(w, function(tag) {
21832             if (b.indexOf(tag) > -1) {
21833                 return;
21834             }
21835             if (this.white.indexOf(tag) > -1) {
21836                 return;
21837             }
21838             this.white.push(tag);
21839             
21840         }, this);
21841         
21842         
21843         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21844             if (w.indexOf(tag) > -1) {
21845                 return;
21846             }
21847             this.black.push(tag);
21848             
21849         }, this);
21850         
21851         Roo.each(b, function(tag) {
21852             if (w.indexOf(tag) > -1) {
21853                 return;
21854             }
21855             if (this.black.indexOf(tag) > -1) {
21856                 return;
21857             }
21858             this.black.push(tag);
21859             
21860         }, this);
21861         
21862         
21863         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21864         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21865         
21866         this.cwhite = [];
21867         this.cblack = [];
21868         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21869             if (b.indexOf(tag) > -1) {
21870                 return;
21871             }
21872             this.cwhite.push(tag);
21873             
21874         }, this);
21875         
21876         Roo.each(w, function(tag) {
21877             if (b.indexOf(tag) > -1) {
21878                 return;
21879             }
21880             if (this.cwhite.indexOf(tag) > -1) {
21881                 return;
21882             }
21883             this.cwhite.push(tag);
21884             
21885         }, this);
21886         
21887         
21888         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21889             if (w.indexOf(tag) > -1) {
21890                 return;
21891             }
21892             this.cblack.push(tag);
21893             
21894         }, this);
21895         
21896         Roo.each(b, function(tag) {
21897             if (w.indexOf(tag) > -1) {
21898                 return;
21899             }
21900             if (this.cblack.indexOf(tag) > -1) {
21901                 return;
21902             }
21903             this.cblack.push(tag);
21904             
21905         }, this);
21906     },
21907     
21908     setStylesheets : function(stylesheets)
21909     {
21910         if(typeof(stylesheets) == 'string'){
21911             Roo.get(this.iframe.contentDocument.head).createChild({
21912                 tag : 'link',
21913                 rel : 'stylesheet',
21914                 type : 'text/css',
21915                 href : stylesheets
21916             });
21917             
21918             return;
21919         }
21920         var _this = this;
21921      
21922         Roo.each(stylesheets, function(s) {
21923             if(!s.length){
21924                 return;
21925             }
21926             
21927             Roo.get(_this.iframe.contentDocument.head).createChild({
21928                 tag : 'link',
21929                 rel : 'stylesheet',
21930                 type : 'text/css',
21931                 href : s
21932             });
21933         });
21934
21935         
21936     },
21937     
21938     removeStylesheets : function()
21939     {
21940         var _this = this;
21941         
21942         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21943             s.remove();
21944         });
21945     }
21946     
21947     // hide stuff that is not compatible
21948     /**
21949      * @event blur
21950      * @hide
21951      */
21952     /**
21953      * @event change
21954      * @hide
21955      */
21956     /**
21957      * @event focus
21958      * @hide
21959      */
21960     /**
21961      * @event specialkey
21962      * @hide
21963      */
21964     /**
21965      * @cfg {String} fieldClass @hide
21966      */
21967     /**
21968      * @cfg {String} focusClass @hide
21969      */
21970     /**
21971      * @cfg {String} autoCreate @hide
21972      */
21973     /**
21974      * @cfg {String} inputType @hide
21975      */
21976     /**
21977      * @cfg {String} invalidClass @hide
21978      */
21979     /**
21980      * @cfg {String} invalidText @hide
21981      */
21982     /**
21983      * @cfg {String} msgFx @hide
21984      */
21985     /**
21986      * @cfg {String} validateOnBlur @hide
21987      */
21988 });
21989
21990 Roo.HtmlEditorCore.white = [
21991         'area', 'br', 'img', 'input', 'hr', 'wbr',
21992         
21993        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21994        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21995        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21996        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21997        'table',   'ul',         'xmp', 
21998        
21999        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22000       'thead',   'tr', 
22001      
22002       'dir', 'menu', 'ol', 'ul', 'dl',
22003        
22004       'embed',  'object'
22005 ];
22006
22007
22008 Roo.HtmlEditorCore.black = [
22009     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22010         'applet', // 
22011         'base',   'basefont', 'bgsound', 'blink',  'body', 
22012         'frame',  'frameset', 'head',    'html',   'ilayer', 
22013         'iframe', 'layer',  'link',     'meta',    'object',   
22014         'script', 'style' ,'title',  'xml' // clean later..
22015 ];
22016 Roo.HtmlEditorCore.clean = [
22017     'script', 'style', 'title', 'xml'
22018 ];
22019 Roo.HtmlEditorCore.remove = [
22020     'font'
22021 ];
22022 // attributes..
22023
22024 Roo.HtmlEditorCore.ablack = [
22025     'on'
22026 ];
22027     
22028 Roo.HtmlEditorCore.aclean = [ 
22029     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22030 ];
22031
22032 // protocols..
22033 Roo.HtmlEditorCore.pwhite= [
22034         'http',  'https',  'mailto'
22035 ];
22036
22037 // white listed style attributes.
22038 Roo.HtmlEditorCore.cwhite= [
22039       //  'text-align', /// default is to allow most things..
22040       
22041          
22042 //        'font-size'//??
22043 ];
22044
22045 // black listed style attributes.
22046 Roo.HtmlEditorCore.cblack= [
22047       //  'font-size' -- this can be set by the project 
22048 ];
22049
22050
22051 Roo.HtmlEditorCore.swapCodes   =[ 
22052     [    8211, "--" ], 
22053     [    8212, "--" ], 
22054     [    8216,  "'" ],  
22055     [    8217, "'" ],  
22056     [    8220, '"' ],  
22057     [    8221, '"' ],  
22058     [    8226, "*" ],  
22059     [    8230, "..." ]
22060 ]; 
22061
22062     /*
22063  * - LGPL
22064  *
22065  * HtmlEditor
22066  * 
22067  */
22068
22069 /**
22070  * @class Roo.bootstrap.HtmlEditor
22071  * @extends Roo.bootstrap.TextArea
22072  * Bootstrap HtmlEditor class
22073
22074  * @constructor
22075  * Create a new HtmlEditor
22076  * @param {Object} config The config object
22077  */
22078
22079 Roo.bootstrap.HtmlEditor = function(config){
22080     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22081     if (!this.toolbars) {
22082         this.toolbars = [];
22083     }
22084     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22085     this.addEvents({
22086             /**
22087              * @event initialize
22088              * Fires when the editor is fully initialized (including the iframe)
22089              * @param {HtmlEditor} this
22090              */
22091             initialize: true,
22092             /**
22093              * @event activate
22094              * Fires when the editor is first receives the focus. Any insertion must wait
22095              * until after this event.
22096              * @param {HtmlEditor} this
22097              */
22098             activate: true,
22099              /**
22100              * @event beforesync
22101              * Fires before the textarea is updated with content from the editor iframe. Return false
22102              * to cancel the sync.
22103              * @param {HtmlEditor} this
22104              * @param {String} html
22105              */
22106             beforesync: true,
22107              /**
22108              * @event beforepush
22109              * Fires before the iframe editor is updated with content from the textarea. Return false
22110              * to cancel the push.
22111              * @param {HtmlEditor} this
22112              * @param {String} html
22113              */
22114             beforepush: true,
22115              /**
22116              * @event sync
22117              * Fires when the textarea is updated with content from the editor iframe.
22118              * @param {HtmlEditor} this
22119              * @param {String} html
22120              */
22121             sync: true,
22122              /**
22123              * @event push
22124              * Fires when the iframe editor is updated with content from the textarea.
22125              * @param {HtmlEditor} this
22126              * @param {String} html
22127              */
22128             push: true,
22129              /**
22130              * @event editmodechange
22131              * Fires when the editor switches edit modes
22132              * @param {HtmlEditor} this
22133              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22134              */
22135             editmodechange: true,
22136             /**
22137              * @event editorevent
22138              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22139              * @param {HtmlEditor} this
22140              */
22141             editorevent: true,
22142             /**
22143              * @event firstfocus
22144              * Fires when on first focus - needed by toolbars..
22145              * @param {HtmlEditor} this
22146              */
22147             firstfocus: true,
22148             /**
22149              * @event autosave
22150              * Auto save the htmlEditor value as a file into Events
22151              * @param {HtmlEditor} this
22152              */
22153             autosave: true,
22154             /**
22155              * @event savedpreview
22156              * preview the saved version of htmlEditor
22157              * @param {HtmlEditor} this
22158              */
22159             savedpreview: true
22160         });
22161 };
22162
22163
22164 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22165     
22166     
22167       /**
22168      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22169      */
22170     toolbars : false,
22171    
22172      /**
22173      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22174      *                        Roo.resizable.
22175      */
22176     resizable : false,
22177      /**
22178      * @cfg {Number} height (in pixels)
22179      */   
22180     height: 300,
22181    /**
22182      * @cfg {Number} width (in pixels)
22183      */   
22184     width: false,
22185     
22186     /**
22187      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22188      * 
22189      */
22190     stylesheets: false,
22191     
22192     // id of frame..
22193     frameId: false,
22194     
22195     // private properties
22196     validationEvent : false,
22197     deferHeight: true,
22198     initialized : false,
22199     activated : false,
22200     
22201     onFocus : Roo.emptyFn,
22202     iframePad:3,
22203     hideMode:'offsets',
22204     
22205     
22206     tbContainer : false,
22207     
22208     toolbarContainer :function() {
22209         return this.wrap.select('.x-html-editor-tb',true).first();
22210     },
22211
22212     /**
22213      * Protected method that will not generally be called directly. It
22214      * is called when the editor creates its toolbar. Override this method if you need to
22215      * add custom toolbar buttons.
22216      * @param {HtmlEditor} editor
22217      */
22218     createToolbar : function(){
22219         
22220         Roo.log("create toolbars");
22221         
22222         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22223         this.toolbars[0].render(this.toolbarContainer());
22224         
22225         return;
22226         
22227 //        if (!editor.toolbars || !editor.toolbars.length) {
22228 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22229 //        }
22230 //        
22231 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22232 //            editor.toolbars[i] = Roo.factory(
22233 //                    typeof(editor.toolbars[i]) == 'string' ?
22234 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22235 //                Roo.bootstrap.HtmlEditor);
22236 //            editor.toolbars[i].init(editor);
22237 //        }
22238     },
22239
22240      
22241     // private
22242     onRender : function(ct, position)
22243     {
22244        // Roo.log("Call onRender: " + this.xtype);
22245         var _t = this;
22246         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22247       
22248         this.wrap = this.inputEl().wrap({
22249             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22250         });
22251         
22252         this.editorcore.onRender(ct, position);
22253          
22254         if (this.resizable) {
22255             this.resizeEl = new Roo.Resizable(this.wrap, {
22256                 pinned : true,
22257                 wrap: true,
22258                 dynamic : true,
22259                 minHeight : this.height,
22260                 height: this.height,
22261                 handles : this.resizable,
22262                 width: this.width,
22263                 listeners : {
22264                     resize : function(r, w, h) {
22265                         _t.onResize(w,h); // -something
22266                     }
22267                 }
22268             });
22269             
22270         }
22271         this.createToolbar(this);
22272        
22273         
22274         if(!this.width && this.resizable){
22275             this.setSize(this.wrap.getSize());
22276         }
22277         if (this.resizeEl) {
22278             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22279             // should trigger onReize..
22280         }
22281         
22282     },
22283
22284     // private
22285     onResize : function(w, h)
22286     {
22287         Roo.log('resize: ' +w + ',' + h );
22288         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22289         var ew = false;
22290         var eh = false;
22291         
22292         if(this.inputEl() ){
22293             if(typeof w == 'number'){
22294                 var aw = w - this.wrap.getFrameWidth('lr');
22295                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22296                 ew = aw;
22297             }
22298             if(typeof h == 'number'){
22299                  var tbh = -11;  // fixme it needs to tool bar size!
22300                 for (var i =0; i < this.toolbars.length;i++) {
22301                     // fixme - ask toolbars for heights?
22302                     tbh += this.toolbars[i].el.getHeight();
22303                     //if (this.toolbars[i].footer) {
22304                     //    tbh += this.toolbars[i].footer.el.getHeight();
22305                     //}
22306                 }
22307               
22308                 
22309                 
22310                 
22311                 
22312                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22313                 ah -= 5; // knock a few pixes off for look..
22314                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22315                 var eh = ah;
22316             }
22317         }
22318         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22319         this.editorcore.onResize(ew,eh);
22320         
22321     },
22322
22323     /**
22324      * Toggles the editor between standard and source edit mode.
22325      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22326      */
22327     toggleSourceEdit : function(sourceEditMode)
22328     {
22329         this.editorcore.toggleSourceEdit(sourceEditMode);
22330         
22331         if(this.editorcore.sourceEditMode){
22332             Roo.log('editor - showing textarea');
22333             
22334 //            Roo.log('in');
22335 //            Roo.log(this.syncValue());
22336             this.syncValue();
22337             this.inputEl().removeClass(['hide', 'x-hidden']);
22338             this.inputEl().dom.removeAttribute('tabIndex');
22339             this.inputEl().focus();
22340         }else{
22341             Roo.log('editor - hiding textarea');
22342 //            Roo.log('out')
22343 //            Roo.log(this.pushValue()); 
22344             this.pushValue();
22345             
22346             this.inputEl().addClass(['hide', 'x-hidden']);
22347             this.inputEl().dom.setAttribute('tabIndex', -1);
22348             //this.deferFocus();
22349         }
22350          
22351         if(this.resizable){
22352             this.setSize(this.wrap.getSize());
22353         }
22354         
22355         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22356     },
22357  
22358     // private (for BoxComponent)
22359     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22360
22361     // private (for BoxComponent)
22362     getResizeEl : function(){
22363         return this.wrap;
22364     },
22365
22366     // private (for BoxComponent)
22367     getPositionEl : function(){
22368         return this.wrap;
22369     },
22370
22371     // private
22372     initEvents : function(){
22373         this.originalValue = this.getValue();
22374     },
22375
22376 //    /**
22377 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22378 //     * @method
22379 //     */
22380 //    markInvalid : Roo.emptyFn,
22381 //    /**
22382 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22383 //     * @method
22384 //     */
22385 //    clearInvalid : Roo.emptyFn,
22386
22387     setValue : function(v){
22388         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22389         this.editorcore.pushValue();
22390     },
22391
22392      
22393     // private
22394     deferFocus : function(){
22395         this.focus.defer(10, this);
22396     },
22397
22398     // doc'ed in Field
22399     focus : function(){
22400         this.editorcore.focus();
22401         
22402     },
22403       
22404
22405     // private
22406     onDestroy : function(){
22407         
22408         
22409         
22410         if(this.rendered){
22411             
22412             for (var i =0; i < this.toolbars.length;i++) {
22413                 // fixme - ask toolbars for heights?
22414                 this.toolbars[i].onDestroy();
22415             }
22416             
22417             this.wrap.dom.innerHTML = '';
22418             this.wrap.remove();
22419         }
22420     },
22421
22422     // private
22423     onFirstFocus : function(){
22424         //Roo.log("onFirstFocus");
22425         this.editorcore.onFirstFocus();
22426          for (var i =0; i < this.toolbars.length;i++) {
22427             this.toolbars[i].onFirstFocus();
22428         }
22429         
22430     },
22431     
22432     // private
22433     syncValue : function()
22434     {   
22435         this.editorcore.syncValue();
22436     },
22437     
22438     pushValue : function()
22439     {   
22440         this.editorcore.pushValue();
22441     }
22442      
22443     
22444     // hide stuff that is not compatible
22445     /**
22446      * @event blur
22447      * @hide
22448      */
22449     /**
22450      * @event change
22451      * @hide
22452      */
22453     /**
22454      * @event focus
22455      * @hide
22456      */
22457     /**
22458      * @event specialkey
22459      * @hide
22460      */
22461     /**
22462      * @cfg {String} fieldClass @hide
22463      */
22464     /**
22465      * @cfg {String} focusClass @hide
22466      */
22467     /**
22468      * @cfg {String} autoCreate @hide
22469      */
22470     /**
22471      * @cfg {String} inputType @hide
22472      */
22473     /**
22474      * @cfg {String} invalidClass @hide
22475      */
22476     /**
22477      * @cfg {String} invalidText @hide
22478      */
22479     /**
22480      * @cfg {String} msgFx @hide
22481      */
22482     /**
22483      * @cfg {String} validateOnBlur @hide
22484      */
22485 });
22486  
22487     
22488    
22489    
22490    
22491       
22492 Roo.namespace('Roo.bootstrap.htmleditor');
22493 /**
22494  * @class Roo.bootstrap.HtmlEditorToolbar1
22495  * Basic Toolbar
22496  * 
22497  * Usage:
22498  *
22499  new Roo.bootstrap.HtmlEditor({
22500     ....
22501     toolbars : [
22502         new Roo.bootstrap.HtmlEditorToolbar1({
22503             disable : { fonts: 1 , format: 1, ..., ... , ...],
22504             btns : [ .... ]
22505         })
22506     }
22507      
22508  * 
22509  * @cfg {Object} disable List of elements to disable..
22510  * @cfg {Array} btns List of additional buttons.
22511  * 
22512  * 
22513  * NEEDS Extra CSS? 
22514  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22515  */
22516  
22517 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22518 {
22519     
22520     Roo.apply(this, config);
22521     
22522     // default disabled, based on 'good practice'..
22523     this.disable = this.disable || {};
22524     Roo.applyIf(this.disable, {
22525         fontSize : true,
22526         colors : true,
22527         specialElements : true
22528     });
22529     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22530     
22531     this.editor = config.editor;
22532     this.editorcore = config.editor.editorcore;
22533     
22534     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22535     
22536     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22537     // dont call parent... till later.
22538 }
22539 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22540      
22541     bar : true,
22542     
22543     editor : false,
22544     editorcore : false,
22545     
22546     
22547     formats : [
22548         "p" ,  
22549         "h1","h2","h3","h4","h5","h6", 
22550         "pre", "code", 
22551         "abbr", "acronym", "address", "cite", "samp", "var",
22552         'div','span'
22553     ],
22554     
22555     onRender : function(ct, position)
22556     {
22557        // Roo.log("Call onRender: " + this.xtype);
22558         
22559        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22560        Roo.log(this.el);
22561        this.el.dom.style.marginBottom = '0';
22562        var _this = this;
22563        var editorcore = this.editorcore;
22564        var editor= this.editor;
22565        
22566        var children = [];
22567        var btn = function(id,cmd , toggle, handler){
22568        
22569             var  event = toggle ? 'toggle' : 'click';
22570        
22571             var a = {
22572                 size : 'sm',
22573                 xtype: 'Button',
22574                 xns: Roo.bootstrap,
22575                 glyphicon : id,
22576                 cmd : id || cmd,
22577                 enableToggle:toggle !== false,
22578                 //html : 'submit'
22579                 pressed : toggle ? false : null,
22580                 listeners : {}
22581             };
22582             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22583                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22584             };
22585             children.push(a);
22586             return a;
22587        }
22588         
22589         var style = {
22590                 xtype: 'Button',
22591                 size : 'sm',
22592                 xns: Roo.bootstrap,
22593                 glyphicon : 'font',
22594                 //html : 'submit'
22595                 menu : {
22596                     xtype: 'Menu',
22597                     xns: Roo.bootstrap,
22598                     items:  []
22599                 }
22600         };
22601         Roo.each(this.formats, function(f) {
22602             style.menu.items.push({
22603                 xtype :'MenuItem',
22604                 xns: Roo.bootstrap,
22605                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22606                 tagname : f,
22607                 listeners : {
22608                     click : function()
22609                     {
22610                         editorcore.insertTag(this.tagname);
22611                         editor.focus();
22612                     }
22613                 }
22614                 
22615             });
22616         });
22617          children.push(style);   
22618             
22619             
22620         btn('bold',false,true);
22621         btn('italic',false,true);
22622         btn('align-left', 'justifyleft',true);
22623         btn('align-center', 'justifycenter',true);
22624         btn('align-right' , 'justifyright',true);
22625         btn('link', false, false, function(btn) {
22626             //Roo.log("create link?");
22627             var url = prompt(this.createLinkText, this.defaultLinkValue);
22628             if(url && url != 'http:/'+'/'){
22629                 this.editorcore.relayCmd('createlink', url);
22630             }
22631         }),
22632         btn('list','insertunorderedlist',true);
22633         btn('pencil', false,true, function(btn){
22634                 Roo.log(this);
22635                 
22636                 this.toggleSourceEdit(btn.pressed);
22637         });
22638         /*
22639         var cog = {
22640                 xtype: 'Button',
22641                 size : 'sm',
22642                 xns: Roo.bootstrap,
22643                 glyphicon : 'cog',
22644                 //html : 'submit'
22645                 menu : {
22646                     xtype: 'Menu',
22647                     xns: Roo.bootstrap,
22648                     items:  []
22649                 }
22650         };
22651         
22652         cog.menu.items.push({
22653             xtype :'MenuItem',
22654             xns: Roo.bootstrap,
22655             html : Clean styles,
22656             tagname : f,
22657             listeners : {
22658                 click : function()
22659                 {
22660                     editorcore.insertTag(this.tagname);
22661                     editor.focus();
22662                 }
22663             }
22664             
22665         });
22666        */
22667         
22668          
22669        this.xtype = 'NavSimplebar';
22670         
22671         for(var i=0;i< children.length;i++) {
22672             
22673             this.buttons.add(this.addxtypeChild(children[i]));
22674             
22675         }
22676         
22677         editor.on('editorevent', this.updateToolbar, this);
22678     },
22679     onBtnClick : function(id)
22680     {
22681        this.editorcore.relayCmd(id);
22682        this.editorcore.focus();
22683     },
22684     
22685     /**
22686      * Protected method that will not generally be called directly. It triggers
22687      * a toolbar update by reading the markup state of the current selection in the editor.
22688      */
22689     updateToolbar: function(){
22690
22691         if(!this.editorcore.activated){
22692             this.editor.onFirstFocus(); // is this neeed?
22693             return;
22694         }
22695
22696         var btns = this.buttons; 
22697         var doc = this.editorcore.doc;
22698         btns.get('bold').setActive(doc.queryCommandState('bold'));
22699         btns.get('italic').setActive(doc.queryCommandState('italic'));
22700         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22701         
22702         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22703         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22704         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22705         
22706         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22707         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22708          /*
22709         
22710         var ans = this.editorcore.getAllAncestors();
22711         if (this.formatCombo) {
22712             
22713             
22714             var store = this.formatCombo.store;
22715             this.formatCombo.setValue("");
22716             for (var i =0; i < ans.length;i++) {
22717                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22718                     // select it..
22719                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22720                     break;
22721                 }
22722             }
22723         }
22724         
22725         
22726         
22727         // hides menus... - so this cant be on a menu...
22728         Roo.bootstrap.MenuMgr.hideAll();
22729         */
22730         Roo.bootstrap.MenuMgr.hideAll();
22731         //this.editorsyncValue();
22732     },
22733     onFirstFocus: function() {
22734         this.buttons.each(function(item){
22735            item.enable();
22736         });
22737     },
22738     toggleSourceEdit : function(sourceEditMode){
22739         
22740           
22741         if(sourceEditMode){
22742             Roo.log("disabling buttons");
22743            this.buttons.each( function(item){
22744                 if(item.cmd != 'pencil'){
22745                     item.disable();
22746                 }
22747             });
22748           
22749         }else{
22750             Roo.log("enabling buttons");
22751             if(this.editorcore.initialized){
22752                 this.buttons.each( function(item){
22753                     item.enable();
22754                 });
22755             }
22756             
22757         }
22758         Roo.log("calling toggole on editor");
22759         // tell the editor that it's been pressed..
22760         this.editor.toggleSourceEdit(sourceEditMode);
22761        
22762     }
22763 });
22764
22765
22766
22767
22768
22769 /**
22770  * @class Roo.bootstrap.Table.AbstractSelectionModel
22771  * @extends Roo.util.Observable
22772  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22773  * implemented by descendant classes.  This class should not be directly instantiated.
22774  * @constructor
22775  */
22776 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22777     this.locked = false;
22778     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22779 };
22780
22781
22782 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22783     /** @ignore Called by the grid automatically. Do not call directly. */
22784     init : function(grid){
22785         this.grid = grid;
22786         this.initEvents();
22787     },
22788
22789     /**
22790      * Locks the selections.
22791      */
22792     lock : function(){
22793         this.locked = true;
22794     },
22795
22796     /**
22797      * Unlocks the selections.
22798      */
22799     unlock : function(){
22800         this.locked = false;
22801     },
22802
22803     /**
22804      * Returns true if the selections are locked.
22805      * @return {Boolean}
22806      */
22807     isLocked : function(){
22808         return this.locked;
22809     }
22810 });
22811 /**
22812  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22813  * @class Roo.bootstrap.Table.RowSelectionModel
22814  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22815  * It supports multiple selections and keyboard selection/navigation. 
22816  * @constructor
22817  * @param {Object} config
22818  */
22819
22820 Roo.bootstrap.Table.RowSelectionModel = function(config){
22821     Roo.apply(this, config);
22822     this.selections = new Roo.util.MixedCollection(false, function(o){
22823         return o.id;
22824     });
22825
22826     this.last = false;
22827     this.lastActive = false;
22828
22829     this.addEvents({
22830         /**
22831              * @event selectionchange
22832              * Fires when the selection changes
22833              * @param {SelectionModel} this
22834              */
22835             "selectionchange" : true,
22836         /**
22837              * @event afterselectionchange
22838              * Fires after the selection changes (eg. by key press or clicking)
22839              * @param {SelectionModel} this
22840              */
22841             "afterselectionchange" : true,
22842         /**
22843              * @event beforerowselect
22844              * Fires when a row is selected being selected, return false to cancel.
22845              * @param {SelectionModel} this
22846              * @param {Number} rowIndex The selected index
22847              * @param {Boolean} keepExisting False if other selections will be cleared
22848              */
22849             "beforerowselect" : true,
22850         /**
22851              * @event rowselect
22852              * Fires when a row is selected.
22853              * @param {SelectionModel} this
22854              * @param {Number} rowIndex The selected index
22855              * @param {Roo.data.Record} r The record
22856              */
22857             "rowselect" : true,
22858         /**
22859              * @event rowdeselect
22860              * Fires when a row is deselected.
22861              * @param {SelectionModel} this
22862              * @param {Number} rowIndex The selected index
22863              */
22864         "rowdeselect" : true
22865     });
22866     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22867     this.locked = false;
22868  };
22869
22870 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22871     /**
22872      * @cfg {Boolean} singleSelect
22873      * True to allow selection of only one row at a time (defaults to false)
22874      */
22875     singleSelect : false,
22876
22877     // private
22878     initEvents : function()
22879     {
22880
22881         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22882         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22883         //}else{ // allow click to work like normal
22884          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22885         //}
22886         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22887         this.grid.on("rowclick", this.handleMouseDown, this);
22888         
22889         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22890             "up" : function(e){
22891                 if(!e.shiftKey){
22892                     this.selectPrevious(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             "down" : function(e){
22906                 if(!e.shiftKey){
22907                     this.selectNext(e.shiftKey);
22908                 }else if(this.last !== false && this.lastActive !== false){
22909                     var last = this.last;
22910                     this.selectRange(this.last,  this.lastActive+1);
22911                     this.grid.getView().focusRow(this.lastActive);
22912                     if(last !== false){
22913                         this.last = last;
22914                     }
22915                 }else{
22916                     this.selectFirstRow();
22917                 }
22918                 this.fireEvent("afterselectionchange", this);
22919             },
22920             scope: this
22921         });
22922         this.grid.store.on('load', function(){
22923             this.selections.clear();
22924         },this);
22925         /*
22926         var view = this.grid.view;
22927         view.on("refresh", this.onRefresh, this);
22928         view.on("rowupdated", this.onRowUpdated, this);
22929         view.on("rowremoved", this.onRemove, this);
22930         */
22931     },
22932
22933     // private
22934     onRefresh : function()
22935     {
22936         var ds = this.grid.store, i, v = this.grid.view;
22937         var s = this.selections;
22938         s.each(function(r){
22939             if((i = ds.indexOfId(r.id)) != -1){
22940                 v.onRowSelect(i);
22941             }else{
22942                 s.remove(r);
22943             }
22944         });
22945     },
22946
22947     // private
22948     onRemove : function(v, index, r){
22949         this.selections.remove(r);
22950     },
22951
22952     // private
22953     onRowUpdated : function(v, index, r){
22954         if(this.isSelected(r)){
22955             v.onRowSelect(index);
22956         }
22957     },
22958
22959     /**
22960      * Select records.
22961      * @param {Array} records The records to select
22962      * @param {Boolean} keepExisting (optional) True to keep existing selections
22963      */
22964     selectRecords : function(records, keepExisting)
22965     {
22966         if(!keepExisting){
22967             this.clearSelections();
22968         }
22969             var ds = this.grid.store;
22970         for(var i = 0, len = records.length; i < len; i++){
22971             this.selectRow(ds.indexOf(records[i]), true);
22972         }
22973     },
22974
22975     /**
22976      * Gets the number of selected rows.
22977      * @return {Number}
22978      */
22979     getCount : function(){
22980         return this.selections.length;
22981     },
22982
22983     /**
22984      * Selects the first row in the grid.
22985      */
22986     selectFirstRow : function(){
22987         this.selectRow(0);
22988     },
22989
22990     /**
22991      * Select the last row.
22992      * @param {Boolean} keepExisting (optional) True to keep existing selections
22993      */
22994     selectLastRow : function(keepExisting){
22995         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22996         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22997     },
22998
22999     /**
23000      * Selects the row immediately following the last selected row.
23001      * @param {Boolean} keepExisting (optional) True to keep existing selections
23002      */
23003     selectNext : function(keepExisting)
23004     {
23005             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23006             this.selectRow(this.last+1, keepExisting);
23007             this.grid.getView().focusRow(this.last);
23008         }
23009     },
23010
23011     /**
23012      * Selects the row that precedes the last selected row.
23013      * @param {Boolean} keepExisting (optional) True to keep existing selections
23014      */
23015     selectPrevious : function(keepExisting){
23016         if(this.last){
23017             this.selectRow(this.last-1, keepExisting);
23018             this.grid.getView().focusRow(this.last);
23019         }
23020     },
23021
23022     /**
23023      * Returns the selected records
23024      * @return {Array} Array of selected records
23025      */
23026     getSelections : function(){
23027         return [].concat(this.selections.items);
23028     },
23029
23030     /**
23031      * Returns the first selected record.
23032      * @return {Record}
23033      */
23034     getSelected : function(){
23035         return this.selections.itemAt(0);
23036     },
23037
23038
23039     /**
23040      * Clears all selections.
23041      */
23042     clearSelections : function(fast)
23043     {
23044         if(this.locked) {
23045             return;
23046         }
23047         if(fast !== true){
23048                 var ds = this.grid.store;
23049             var s = this.selections;
23050             s.each(function(r){
23051                 this.deselectRow(ds.indexOfId(r.id));
23052             }, this);
23053             s.clear();
23054         }else{
23055             this.selections.clear();
23056         }
23057         this.last = false;
23058     },
23059
23060
23061     /**
23062      * Selects all rows.
23063      */
23064     selectAll : function(){
23065         if(this.locked) {
23066             return;
23067         }
23068         this.selections.clear();
23069         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23070             this.selectRow(i, true);
23071         }
23072     },
23073
23074     /**
23075      * Returns True if there is a selection.
23076      * @return {Boolean}
23077      */
23078     hasSelection : function(){
23079         return this.selections.length > 0;
23080     },
23081
23082     /**
23083      * Returns True if the specified row is selected.
23084      * @param {Number/Record} record The record or index of the record to check
23085      * @return {Boolean}
23086      */
23087     isSelected : function(index){
23088             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23089         return (r && this.selections.key(r.id) ? true : false);
23090     },
23091
23092     /**
23093      * Returns True if the specified record id is selected.
23094      * @param {String} id The id of record to check
23095      * @return {Boolean}
23096      */
23097     isIdSelected : function(id){
23098         return (this.selections.key(id) ? true : false);
23099     },
23100
23101
23102     // private
23103     handleMouseDBClick : function(e, t){
23104         
23105     },
23106     // private
23107     handleMouseDown : function(e, t)
23108     {
23109             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23110         if(this.isLocked() || rowIndex < 0 ){
23111             return;
23112         };
23113         if(e.shiftKey && this.last !== false){
23114             var last = this.last;
23115             this.selectRange(last, rowIndex, e.ctrlKey);
23116             this.last = last; // reset the last
23117             t.focus();
23118     
23119         }else{
23120             var isSelected = this.isSelected(rowIndex);
23121             //Roo.log("select row:" + rowIndex);
23122             if(isSelected){
23123                 this.deselectRow(rowIndex);
23124             } else {
23125                         this.selectRow(rowIndex, true);
23126             }
23127     
23128             /*
23129                 if(e.button !== 0 && isSelected){
23130                 alert('rowIndex 2: ' + rowIndex);
23131                     view.focusRow(rowIndex);
23132                 }else if(e.ctrlKey && isSelected){
23133                     this.deselectRow(rowIndex);
23134                 }else if(!isSelected){
23135                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23136                     view.focusRow(rowIndex);
23137                 }
23138             */
23139         }
23140         this.fireEvent("afterselectionchange", this);
23141     },
23142     // private
23143     handleDragableRowClick :  function(grid, rowIndex, e) 
23144     {
23145         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23146             this.selectRow(rowIndex, false);
23147             grid.view.focusRow(rowIndex);
23148              this.fireEvent("afterselectionchange", this);
23149         }
23150     },
23151     
23152     /**
23153      * Selects multiple rows.
23154      * @param {Array} rows Array of the indexes of the row to select
23155      * @param {Boolean} keepExisting (optional) True to keep existing selections
23156      */
23157     selectRows : function(rows, keepExisting){
23158         if(!keepExisting){
23159             this.clearSelections();
23160         }
23161         for(var i = 0, len = rows.length; i < len; i++){
23162             this.selectRow(rows[i], true);
23163         }
23164     },
23165
23166     /**
23167      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23168      * @param {Number} startRow The index of the first row in the range
23169      * @param {Number} endRow The index of the last row in the range
23170      * @param {Boolean} keepExisting (optional) True to retain existing selections
23171      */
23172     selectRange : function(startRow, endRow, keepExisting){
23173         if(this.locked) {
23174             return;
23175         }
23176         if(!keepExisting){
23177             this.clearSelections();
23178         }
23179         if(startRow <= endRow){
23180             for(var i = startRow; i <= endRow; i++){
23181                 this.selectRow(i, true);
23182             }
23183         }else{
23184             for(var i = startRow; i >= endRow; i--){
23185                 this.selectRow(i, true);
23186             }
23187         }
23188     },
23189
23190     /**
23191      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23192      * @param {Number} startRow The index of the first row in the range
23193      * @param {Number} endRow The index of the last row in the range
23194      */
23195     deselectRange : function(startRow, endRow, preventViewNotify){
23196         if(this.locked) {
23197             return;
23198         }
23199         for(var i = startRow; i <= endRow; i++){
23200             this.deselectRow(i, preventViewNotify);
23201         }
23202     },
23203
23204     /**
23205      * Selects a row.
23206      * @param {Number} row The index of the row to select
23207      * @param {Boolean} keepExisting (optional) True to keep existing selections
23208      */
23209     selectRow : function(index, keepExisting, preventViewNotify)
23210     {
23211             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23212             return;
23213         }
23214         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23215             if(!keepExisting || this.singleSelect){
23216                 this.clearSelections();
23217             }
23218             
23219             var r = this.grid.store.getAt(index);
23220             //console.log('selectRow - record id :' + r.id);
23221             
23222             this.selections.add(r);
23223             this.last = this.lastActive = index;
23224             if(!preventViewNotify){
23225                 var proxy = new Roo.Element(
23226                                 this.grid.getRowDom(index)
23227                 );
23228                 proxy.addClass('bg-info info');
23229             }
23230             this.fireEvent("rowselect", this, index, r);
23231             this.fireEvent("selectionchange", this);
23232         }
23233     },
23234
23235     /**
23236      * Deselects a row.
23237      * @param {Number} row The index of the row to deselect
23238      */
23239     deselectRow : function(index, preventViewNotify)
23240     {
23241         if(this.locked) {
23242             return;
23243         }
23244         if(this.last == index){
23245             this.last = false;
23246         }
23247         if(this.lastActive == index){
23248             this.lastActive = false;
23249         }
23250         
23251         var r = this.grid.store.getAt(index);
23252         if (!r) {
23253             return;
23254         }
23255         
23256         this.selections.remove(r);
23257         //.console.log('deselectRow - record id :' + r.id);
23258         if(!preventViewNotify){
23259         
23260             var proxy = new Roo.Element(
23261                 this.grid.getRowDom(index)
23262             );
23263             proxy.removeClass('bg-info info');
23264         }
23265         this.fireEvent("rowdeselect", this, index);
23266         this.fireEvent("selectionchange", this);
23267     },
23268
23269     // private
23270     restoreLast : function(){
23271         if(this._last){
23272             this.last = this._last;
23273         }
23274     },
23275
23276     // private
23277     acceptsNav : function(row, col, cm){
23278         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23279     },
23280
23281     // private
23282     onEditorKey : function(field, e){
23283         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23284         if(k == e.TAB){
23285             e.stopEvent();
23286             ed.completeEdit();
23287             if(e.shiftKey){
23288                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23289             }else{
23290                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23291             }
23292         }else if(k == e.ENTER && !e.ctrlKey){
23293             e.stopEvent();
23294             ed.completeEdit();
23295             if(e.shiftKey){
23296                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23297             }else{
23298                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23299             }
23300         }else if(k == e.ESC){
23301             ed.cancelEdit();
23302         }
23303         if(newCell){
23304             g.startEditing(newCell[0], newCell[1]);
23305         }
23306     }
23307 });
23308 /*
23309  * Based on:
23310  * Ext JS Library 1.1.1
23311  * Copyright(c) 2006-2007, Ext JS, LLC.
23312  *
23313  * Originally Released Under LGPL - original licence link has changed is not relivant.
23314  *
23315  * Fork - LGPL
23316  * <script type="text/javascript">
23317  */
23318  
23319 /**
23320  * @class Roo.bootstrap.PagingToolbar
23321  * @extends Roo.bootstrap.NavSimplebar
23322  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23323  * @constructor
23324  * Create a new PagingToolbar
23325  * @param {Object} config The config object
23326  * @param {Roo.data.Store} store
23327  */
23328 Roo.bootstrap.PagingToolbar = function(config)
23329 {
23330     // old args format still supported... - xtype is prefered..
23331         // created from xtype...
23332     
23333     this.ds = config.dataSource;
23334     
23335     if (config.store && !this.ds) {
23336         this.store= Roo.factory(config.store, Roo.data);
23337         this.ds = this.store;
23338         this.ds.xmodule = this.xmodule || false;
23339     }
23340     
23341     this.toolbarItems = [];
23342     if (config.items) {
23343         this.toolbarItems = config.items;
23344     }
23345     
23346     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23347     
23348     this.cursor = 0;
23349     
23350     if (this.ds) { 
23351         this.bind(this.ds);
23352     }
23353     
23354     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23355     
23356 };
23357
23358 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23359     /**
23360      * @cfg {Roo.data.Store} dataSource
23361      * The underlying data store providing the paged data
23362      */
23363     /**
23364      * @cfg {String/HTMLElement/Element} container
23365      * container The id or element that will contain the toolbar
23366      */
23367     /**
23368      * @cfg {Boolean} displayInfo
23369      * True to display the displayMsg (defaults to false)
23370      */
23371     /**
23372      * @cfg {Number} pageSize
23373      * The number of records to display per page (defaults to 20)
23374      */
23375     pageSize: 20,
23376     /**
23377      * @cfg {String} displayMsg
23378      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23379      */
23380     displayMsg : 'Displaying {0} - {1} of {2}',
23381     /**
23382      * @cfg {String} emptyMsg
23383      * The message to display when no records are found (defaults to "No data to display")
23384      */
23385     emptyMsg : 'No data to display',
23386     /**
23387      * Customizable piece of the default paging text (defaults to "Page")
23388      * @type String
23389      */
23390     beforePageText : "Page",
23391     /**
23392      * Customizable piece of the default paging text (defaults to "of %0")
23393      * @type String
23394      */
23395     afterPageText : "of {0}",
23396     /**
23397      * Customizable piece of the default paging text (defaults to "First Page")
23398      * @type String
23399      */
23400     firstText : "First Page",
23401     /**
23402      * Customizable piece of the default paging text (defaults to "Previous Page")
23403      * @type String
23404      */
23405     prevText : "Previous Page",
23406     /**
23407      * Customizable piece of the default paging text (defaults to "Next Page")
23408      * @type String
23409      */
23410     nextText : "Next Page",
23411     /**
23412      * Customizable piece of the default paging text (defaults to "Last Page")
23413      * @type String
23414      */
23415     lastText : "Last Page",
23416     /**
23417      * Customizable piece of the default paging text (defaults to "Refresh")
23418      * @type String
23419      */
23420     refreshText : "Refresh",
23421
23422     buttons : false,
23423     // private
23424     onRender : function(ct, position) 
23425     {
23426         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23427         this.navgroup.parentId = this.id;
23428         this.navgroup.onRender(this.el, null);
23429         // add the buttons to the navgroup
23430         
23431         if(this.displayInfo){
23432             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23433             this.displayEl = this.el.select('.x-paging-info', true).first();
23434 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23435 //            this.displayEl = navel.el.select('span',true).first();
23436         }
23437         
23438         var _this = this;
23439         
23440         if(this.buttons){
23441             Roo.each(_this.buttons, function(e){ // this might need to use render????
23442                Roo.factory(e).onRender(_this.el, null);
23443             });
23444         }
23445             
23446         Roo.each(_this.toolbarItems, function(e) {
23447             _this.navgroup.addItem(e);
23448         });
23449         
23450         
23451         this.first = this.navgroup.addItem({
23452             tooltip: this.firstText,
23453             cls: "prev",
23454             icon : 'fa fa-backward',
23455             disabled: true,
23456             preventDefault: true,
23457             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23458         });
23459         
23460         this.prev =  this.navgroup.addItem({
23461             tooltip: this.prevText,
23462             cls: "prev",
23463             icon : 'fa fa-step-backward',
23464             disabled: true,
23465             preventDefault: true,
23466             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23467         });
23468     //this.addSeparator();
23469         
23470         
23471         var field = this.navgroup.addItem( {
23472             tagtype : 'span',
23473             cls : 'x-paging-position',
23474             
23475             html : this.beforePageText  +
23476                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23477                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23478          } ); //?? escaped?
23479         
23480         this.field = field.el.select('input', true).first();
23481         this.field.on("keydown", this.onPagingKeydown, this);
23482         this.field.on("focus", function(){this.dom.select();});
23483     
23484     
23485         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23486         //this.field.setHeight(18);
23487         //this.addSeparator();
23488         this.next = this.navgroup.addItem({
23489             tooltip: this.nextText,
23490             cls: "next",
23491             html : ' <i class="fa fa-step-forward">',
23492             disabled: true,
23493             preventDefault: true,
23494             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23495         });
23496         this.last = this.navgroup.addItem({
23497             tooltip: this.lastText,
23498             icon : 'fa fa-forward',
23499             cls: "next",
23500             disabled: true,
23501             preventDefault: true,
23502             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23503         });
23504     //this.addSeparator();
23505         this.loading = this.navgroup.addItem({
23506             tooltip: this.refreshText,
23507             icon: 'fa fa-refresh',
23508             preventDefault: true,
23509             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23510         });
23511         
23512     },
23513
23514     // private
23515     updateInfo : function(){
23516         if(this.displayEl){
23517             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23518             var msg = count == 0 ?
23519                 this.emptyMsg :
23520                 String.format(
23521                     this.displayMsg,
23522                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23523                 );
23524             this.displayEl.update(msg);
23525         }
23526     },
23527
23528     // private
23529     onLoad : function(ds, r, o){
23530        this.cursor = o.params ? o.params.start : 0;
23531        var d = this.getPageData(),
23532             ap = d.activePage,
23533             ps = d.pages;
23534         
23535        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23536        this.field.dom.value = ap;
23537        this.first.setDisabled(ap == 1);
23538        this.prev.setDisabled(ap == 1);
23539        this.next.setDisabled(ap == ps);
23540        this.last.setDisabled(ap == ps);
23541        this.loading.enable();
23542        this.updateInfo();
23543     },
23544
23545     // private
23546     getPageData : function(){
23547         var total = this.ds.getTotalCount();
23548         return {
23549             total : total,
23550             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23551             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23552         };
23553     },
23554
23555     // private
23556     onLoadError : function(){
23557         this.loading.enable();
23558     },
23559
23560     // private
23561     onPagingKeydown : function(e){
23562         var k = e.getKey();
23563         var d = this.getPageData();
23564         if(k == e.RETURN){
23565             var v = this.field.dom.value, pageNum;
23566             if(!v || isNaN(pageNum = parseInt(v, 10))){
23567                 this.field.dom.value = d.activePage;
23568                 return;
23569             }
23570             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23571             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23572             e.stopEvent();
23573         }
23574         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))
23575         {
23576           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23577           this.field.dom.value = pageNum;
23578           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23579           e.stopEvent();
23580         }
23581         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23582         {
23583           var v = this.field.dom.value, pageNum; 
23584           var increment = (e.shiftKey) ? 10 : 1;
23585           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23586                 increment *= -1;
23587           }
23588           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23589             this.field.dom.value = d.activePage;
23590             return;
23591           }
23592           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23593           {
23594             this.field.dom.value = parseInt(v, 10) + increment;
23595             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23596             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23597           }
23598           e.stopEvent();
23599         }
23600     },
23601
23602     // private
23603     beforeLoad : function(){
23604         if(this.loading){
23605             this.loading.disable();
23606         }
23607     },
23608
23609     // private
23610     onClick : function(which){
23611         
23612         var ds = this.ds;
23613         if (!ds) {
23614             return;
23615         }
23616         
23617         switch(which){
23618             case "first":
23619                 ds.load({params:{start: 0, limit: this.pageSize}});
23620             break;
23621             case "prev":
23622                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23623             break;
23624             case "next":
23625                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23626             break;
23627             case "last":
23628                 var total = ds.getTotalCount();
23629                 var extra = total % this.pageSize;
23630                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23631                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23632             break;
23633             case "refresh":
23634                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23635             break;
23636         }
23637     },
23638
23639     /**
23640      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23641      * @param {Roo.data.Store} store The data store to unbind
23642      */
23643     unbind : function(ds){
23644         ds.un("beforeload", this.beforeLoad, this);
23645         ds.un("load", this.onLoad, this);
23646         ds.un("loadexception", this.onLoadError, this);
23647         ds.un("remove", this.updateInfo, this);
23648         ds.un("add", this.updateInfo, this);
23649         this.ds = undefined;
23650     },
23651
23652     /**
23653      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23654      * @param {Roo.data.Store} store The data store to bind
23655      */
23656     bind : function(ds){
23657         ds.on("beforeload", this.beforeLoad, this);
23658         ds.on("load", this.onLoad, this);
23659         ds.on("loadexception", this.onLoadError, this);
23660         ds.on("remove", this.updateInfo, this);
23661         ds.on("add", this.updateInfo, this);
23662         this.ds = ds;
23663     }
23664 });/*
23665  * - LGPL
23666  *
23667  * element
23668  * 
23669  */
23670
23671 /**
23672  * @class Roo.bootstrap.MessageBar
23673  * @extends Roo.bootstrap.Component
23674  * Bootstrap MessageBar class
23675  * @cfg {String} html contents of the MessageBar
23676  * @cfg {String} weight (info | success | warning | danger) default info
23677  * @cfg {String} beforeClass insert the bar before the given class
23678  * @cfg {Boolean} closable (true | false) default false
23679  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23680  * 
23681  * @constructor
23682  * Create a new Element
23683  * @param {Object} config The config object
23684  */
23685
23686 Roo.bootstrap.MessageBar = function(config){
23687     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23688 };
23689
23690 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23691     
23692     html: '',
23693     weight: 'info',
23694     closable: false,
23695     fixed: false,
23696     beforeClass: 'bootstrap-sticky-wrap',
23697     
23698     getAutoCreate : function(){
23699         
23700         var cfg = {
23701             tag: 'div',
23702             cls: 'alert alert-dismissable alert-' + this.weight,
23703             cn: [
23704                 {
23705                     tag: 'span',
23706                     cls: 'message',
23707                     html: this.html || ''
23708                 }
23709             ]
23710         };
23711         
23712         if(this.fixed){
23713             cfg.cls += ' alert-messages-fixed';
23714         }
23715         
23716         if(this.closable){
23717             cfg.cn.push({
23718                 tag: 'button',
23719                 cls: 'close',
23720                 html: 'x'
23721             });
23722         }
23723         
23724         return cfg;
23725     },
23726     
23727     onRender : function(ct, position)
23728     {
23729         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23730         
23731         if(!this.el){
23732             var cfg = Roo.apply({},  this.getAutoCreate());
23733             cfg.id = Roo.id();
23734             
23735             if (this.cls) {
23736                 cfg.cls += ' ' + this.cls;
23737             }
23738             if (this.style) {
23739                 cfg.style = this.style;
23740             }
23741             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23742             
23743             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23744         }
23745         
23746         this.el.select('>button.close').on('click', this.hide, this);
23747         
23748     },
23749     
23750     show : function()
23751     {
23752         if (!this.rendered) {
23753             this.render();
23754         }
23755         
23756         this.el.show();
23757         
23758         this.fireEvent('show', this);
23759         
23760     },
23761     
23762     hide : function()
23763     {
23764         if (!this.rendered) {
23765             this.render();
23766         }
23767         
23768         this.el.hide();
23769         
23770         this.fireEvent('hide', this);
23771     },
23772     
23773     update : function()
23774     {
23775 //        var e = this.el.dom.firstChild;
23776 //        
23777 //        if(this.closable){
23778 //            e = e.nextSibling;
23779 //        }
23780 //        
23781 //        e.data = this.html || '';
23782
23783         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23784     }
23785    
23786 });
23787
23788  
23789
23790      /*
23791  * - LGPL
23792  *
23793  * Graph
23794  * 
23795  */
23796
23797
23798 /**
23799  * @class Roo.bootstrap.Graph
23800  * @extends Roo.bootstrap.Component
23801  * Bootstrap Graph class
23802 > Prameters
23803  -sm {number} sm 4
23804  -md {number} md 5
23805  @cfg {String} graphtype  bar | vbar | pie
23806  @cfg {number} g_x coodinator | centre x (pie)
23807  @cfg {number} g_y coodinator | centre y (pie)
23808  @cfg {number} g_r radius (pie)
23809  @cfg {number} g_height height of the chart (respected by all elements in the set)
23810  @cfg {number} g_width width of the chart (respected by all elements in the set)
23811  @cfg {Object} title The title of the chart
23812     
23813  -{Array}  values
23814  -opts (object) options for the chart 
23815      o {
23816      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23817      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23818      o vgutter (number)
23819      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.
23820      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23821      o to
23822      o stretch (boolean)
23823      o }
23824  -opts (object) options for the pie
23825      o{
23826      o cut
23827      o startAngle (number)
23828      o endAngle (number)
23829      } 
23830  *
23831  * @constructor
23832  * Create a new Input
23833  * @param {Object} config The config object
23834  */
23835
23836 Roo.bootstrap.Graph = function(config){
23837     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23838     
23839     this.addEvents({
23840         // img events
23841         /**
23842          * @event click
23843          * The img click event for the img.
23844          * @param {Roo.EventObject} e
23845          */
23846         "click" : true
23847     });
23848 };
23849
23850 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23851     
23852     sm: 4,
23853     md: 5,
23854     graphtype: 'bar',
23855     g_height: 250,
23856     g_width: 400,
23857     g_x: 50,
23858     g_y: 50,
23859     g_r: 30,
23860     opts:{
23861         //g_colors: this.colors,
23862         g_type: 'soft',
23863         g_gutter: '20%'
23864
23865     },
23866     title : false,
23867
23868     getAutoCreate : function(){
23869         
23870         var cfg = {
23871             tag: 'div',
23872             html : null
23873         };
23874         
23875         
23876         return  cfg;
23877     },
23878
23879     onRender : function(ct,position){
23880         
23881         
23882         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23883         
23884         if (typeof(Raphael) == 'undefined') {
23885             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23886             return;
23887         }
23888         
23889         this.raphael = Raphael(this.el.dom);
23890         
23891                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23892                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23893                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23894                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23895                 /*
23896                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23897                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23898                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23899                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23900                 
23901                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23902                 r.barchart(330, 10, 300, 220, data1);
23903                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23904                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23905                 */
23906                 
23907                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23908                 // r.barchart(30, 30, 560, 250,  xdata, {
23909                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23910                 //     axis : "0 0 1 1",
23911                 //     axisxlabels :  xdata
23912                 //     //yvalues : cols,
23913                    
23914                 // });
23915 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23916 //        
23917 //        this.load(null,xdata,{
23918 //                axis : "0 0 1 1",
23919 //                axisxlabels :  xdata
23920 //                });
23921
23922     },
23923
23924     load : function(graphtype,xdata,opts)
23925     {
23926         this.raphael.clear();
23927         if(!graphtype) {
23928             graphtype = this.graphtype;
23929         }
23930         if(!opts){
23931             opts = this.opts;
23932         }
23933         var r = this.raphael,
23934             fin = function () {
23935                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23936             },
23937             fout = function () {
23938                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23939             },
23940             pfin = function() {
23941                 this.sector.stop();
23942                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23943
23944                 if (this.label) {
23945                     this.label[0].stop();
23946                     this.label[0].attr({ r: 7.5 });
23947                     this.label[1].attr({ "font-weight": 800 });
23948                 }
23949             },
23950             pfout = function() {
23951                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23952
23953                 if (this.label) {
23954                     this.label[0].animate({ r: 5 }, 500, "bounce");
23955                     this.label[1].attr({ "font-weight": 400 });
23956                 }
23957             };
23958
23959         switch(graphtype){
23960             case 'bar':
23961                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23962                 break;
23963             case 'hbar':
23964                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23965                 break;
23966             case 'pie':
23967 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23968 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23969 //            
23970                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23971                 
23972                 break;
23973
23974         }
23975         
23976         if(this.title){
23977             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23978         }
23979         
23980     },
23981     
23982     setTitle: function(o)
23983     {
23984         this.title = o;
23985     },
23986     
23987     initEvents: function() {
23988         
23989         if(!this.href){
23990             this.el.on('click', this.onClick, this);
23991         }
23992     },
23993     
23994     onClick : function(e)
23995     {
23996         Roo.log('img onclick');
23997         this.fireEvent('click', this, e);
23998     }
23999    
24000 });
24001
24002  
24003 /*
24004  * - LGPL
24005  *
24006  * numberBox
24007  * 
24008  */
24009 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24010
24011 /**
24012  * @class Roo.bootstrap.dash.NumberBox
24013  * @extends Roo.bootstrap.Component
24014  * Bootstrap NumberBox class
24015  * @cfg {String} headline Box headline
24016  * @cfg {String} content Box content
24017  * @cfg {String} icon Box icon
24018  * @cfg {String} footer Footer text
24019  * @cfg {String} fhref Footer href
24020  * 
24021  * @constructor
24022  * Create a new NumberBox
24023  * @param {Object} config The config object
24024  */
24025
24026
24027 Roo.bootstrap.dash.NumberBox = function(config){
24028     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24029     
24030 };
24031
24032 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24033     
24034     headline : '',
24035     content : '',
24036     icon : '',
24037     footer : '',
24038     fhref : '',
24039     ficon : '',
24040     
24041     getAutoCreate : function(){
24042         
24043         var cfg = {
24044             tag : 'div',
24045             cls : 'small-box ',
24046             cn : [
24047                 {
24048                     tag : 'div',
24049                     cls : 'inner',
24050                     cn :[
24051                         {
24052                             tag : 'h3',
24053                             cls : 'roo-headline',
24054                             html : this.headline
24055                         },
24056                         {
24057                             tag : 'p',
24058                             cls : 'roo-content',
24059                             html : this.content
24060                         }
24061                     ]
24062                 }
24063             ]
24064         };
24065         
24066         if(this.icon){
24067             cfg.cn.push({
24068                 tag : 'div',
24069                 cls : 'icon',
24070                 cn :[
24071                     {
24072                         tag : 'i',
24073                         cls : 'ion ' + this.icon
24074                     }
24075                 ]
24076             });
24077         }
24078         
24079         if(this.footer){
24080             var footer = {
24081                 tag : 'a',
24082                 cls : 'small-box-footer',
24083                 href : this.fhref || '#',
24084                 html : this.footer
24085             };
24086             
24087             cfg.cn.push(footer);
24088             
24089         }
24090         
24091         return  cfg;
24092     },
24093
24094     onRender : function(ct,position){
24095         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24096
24097
24098        
24099                 
24100     },
24101
24102     setHeadline: function (value)
24103     {
24104         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24105     },
24106     
24107     setFooter: function (value, href)
24108     {
24109         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24110         
24111         if(href){
24112             this.el.select('a.small-box-footer',true).first().attr('href', href);
24113         }
24114         
24115     },
24116
24117     setContent: function (value)
24118     {
24119         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24120     },
24121
24122     initEvents: function() 
24123     {   
24124         
24125     }
24126     
24127 });
24128
24129  
24130 /*
24131  * - LGPL
24132  *
24133  * TabBox
24134  * 
24135  */
24136 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24137
24138 /**
24139  * @class Roo.bootstrap.dash.TabBox
24140  * @extends Roo.bootstrap.Component
24141  * Bootstrap TabBox class
24142  * @cfg {String} title Title of the TabBox
24143  * @cfg {String} icon Icon of the TabBox
24144  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24145  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24146  * 
24147  * @constructor
24148  * Create a new TabBox
24149  * @param {Object} config The config object
24150  */
24151
24152
24153 Roo.bootstrap.dash.TabBox = function(config){
24154     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24155     this.addEvents({
24156         // raw events
24157         /**
24158          * @event addpane
24159          * When a pane is added
24160          * @param {Roo.bootstrap.dash.TabPane} pane
24161          */
24162         "addpane" : true,
24163         /**
24164          * @event activatepane
24165          * When a pane is activated
24166          * @param {Roo.bootstrap.dash.TabPane} pane
24167          */
24168         "activatepane" : true
24169         
24170          
24171     });
24172     
24173     this.panes = [];
24174 };
24175
24176 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24177
24178     title : '',
24179     icon : false,
24180     showtabs : true,
24181     tabScrollable : false,
24182     
24183     getChildContainer : function()
24184     {
24185         return this.el.select('.tab-content', true).first();
24186     },
24187     
24188     getAutoCreate : function(){
24189         
24190         var header = {
24191             tag: 'li',
24192             cls: 'pull-left header',
24193             html: this.title,
24194             cn : []
24195         };
24196         
24197         if(this.icon){
24198             header.cn.push({
24199                 tag: 'i',
24200                 cls: 'fa ' + this.icon
24201             });
24202         }
24203         
24204         var h = {
24205             tag: 'ul',
24206             cls: 'nav nav-tabs pull-right',
24207             cn: [
24208                 header
24209             ]
24210         };
24211         
24212         if(this.tabScrollable){
24213             h = {
24214                 tag: 'div',
24215                 cls: 'tab-header',
24216                 cn: [
24217                     {
24218                         tag: 'ul',
24219                         cls: 'nav nav-tabs pull-right',
24220                         cn: [
24221                             header
24222                         ]
24223                     }
24224                 ]
24225             };
24226         }
24227         
24228         var cfg = {
24229             tag: 'div',
24230             cls: 'nav-tabs-custom',
24231             cn: [
24232                 h,
24233                 {
24234                     tag: 'div',
24235                     cls: 'tab-content no-padding',
24236                     cn: []
24237                 }
24238             ]
24239         };
24240
24241         return  cfg;
24242     },
24243     initEvents : function()
24244     {
24245         //Roo.log('add add pane handler');
24246         this.on('addpane', this.onAddPane, this);
24247     },
24248      /**
24249      * Updates the box title
24250      * @param {String} html to set the title to.
24251      */
24252     setTitle : function(value)
24253     {
24254         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24255     },
24256     onAddPane : function(pane)
24257     {
24258         this.panes.push(pane);
24259         //Roo.log('addpane');
24260         //Roo.log(pane);
24261         // tabs are rendere left to right..
24262         if(!this.showtabs){
24263             return;
24264         }
24265         
24266         var ctr = this.el.select('.nav-tabs', true).first();
24267          
24268          
24269         var existing = ctr.select('.nav-tab',true);
24270         var qty = existing.getCount();;
24271         
24272         
24273         var tab = ctr.createChild({
24274             tag : 'li',
24275             cls : 'nav-tab' + (qty ? '' : ' active'),
24276             cn : [
24277                 {
24278                     tag : 'a',
24279                     href:'#',
24280                     html : pane.title
24281                 }
24282             ]
24283         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24284         pane.tab = tab;
24285         
24286         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24287         if (!qty) {
24288             pane.el.addClass('active');
24289         }
24290         
24291                 
24292     },
24293     onTabClick : function(ev,un,ob,pane)
24294     {
24295         //Roo.log('tab - prev default');
24296         ev.preventDefault();
24297         
24298         
24299         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24300         pane.tab.addClass('active');
24301         //Roo.log(pane.title);
24302         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24303         // technically we should have a deactivate event.. but maybe add later.
24304         // and it should not de-activate the selected tab...
24305         this.fireEvent('activatepane', pane);
24306         pane.el.addClass('active');
24307         pane.fireEvent('activate');
24308         
24309         
24310     },
24311     
24312     getActivePane : function()
24313     {
24314         var r = false;
24315         Roo.each(this.panes, function(p) {
24316             if(p.el.hasClass('active')){
24317                 r = p;
24318                 return false;
24319             }
24320             
24321             return;
24322         });
24323         
24324         return r;
24325     }
24326     
24327     
24328 });
24329
24330  
24331 /*
24332  * - LGPL
24333  *
24334  * Tab pane
24335  * 
24336  */
24337 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24338 /**
24339  * @class Roo.bootstrap.TabPane
24340  * @extends Roo.bootstrap.Component
24341  * Bootstrap TabPane class
24342  * @cfg {Boolean} active (false | true) Default false
24343  * @cfg {String} title title of panel
24344
24345  * 
24346  * @constructor
24347  * Create a new TabPane
24348  * @param {Object} config The config object
24349  */
24350
24351 Roo.bootstrap.dash.TabPane = function(config){
24352     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24353     
24354     this.addEvents({
24355         // raw events
24356         /**
24357          * @event activate
24358          * When a pane is activated
24359          * @param {Roo.bootstrap.dash.TabPane} pane
24360          */
24361         "activate" : true
24362          
24363     });
24364 };
24365
24366 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24367     
24368     active : false,
24369     title : '',
24370     
24371     // the tabBox that this is attached to.
24372     tab : false,
24373      
24374     getAutoCreate : function() 
24375     {
24376         var cfg = {
24377             tag: 'div',
24378             cls: 'tab-pane'
24379         };
24380         
24381         if(this.active){
24382             cfg.cls += ' active';
24383         }
24384         
24385         return cfg;
24386     },
24387     initEvents  : function()
24388     {
24389         //Roo.log('trigger add pane handler');
24390         this.parent().fireEvent('addpane', this)
24391     },
24392     
24393      /**
24394      * Updates the tab title 
24395      * @param {String} html to set the title to.
24396      */
24397     setTitle: function(str)
24398     {
24399         if (!this.tab) {
24400             return;
24401         }
24402         this.title = str;
24403         this.tab.select('a', true).first().dom.innerHTML = str;
24404         
24405     }
24406     
24407     
24408     
24409 });
24410
24411  
24412
24413
24414  /*
24415  * - LGPL
24416  *
24417  * menu
24418  * 
24419  */
24420 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24421
24422 /**
24423  * @class Roo.bootstrap.menu.Menu
24424  * @extends Roo.bootstrap.Component
24425  * Bootstrap Menu class - container for Menu
24426  * @cfg {String} html Text of the menu
24427  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24428  * @cfg {String} icon Font awesome icon
24429  * @cfg {String} pos Menu align to (top | bottom) default bottom
24430  * 
24431  * 
24432  * @constructor
24433  * Create a new Menu
24434  * @param {Object} config The config object
24435  */
24436
24437
24438 Roo.bootstrap.menu.Menu = function(config){
24439     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24440     
24441     this.addEvents({
24442         /**
24443          * @event beforeshow
24444          * Fires before this menu is displayed
24445          * @param {Roo.bootstrap.menu.Menu} this
24446          */
24447         beforeshow : true,
24448         /**
24449          * @event beforehide
24450          * Fires before this menu is hidden
24451          * @param {Roo.bootstrap.menu.Menu} this
24452          */
24453         beforehide : true,
24454         /**
24455          * @event show
24456          * Fires after this menu is displayed
24457          * @param {Roo.bootstrap.menu.Menu} this
24458          */
24459         show : true,
24460         /**
24461          * @event hide
24462          * Fires after this menu is hidden
24463          * @param {Roo.bootstrap.menu.Menu} this
24464          */
24465         hide : true,
24466         /**
24467          * @event click
24468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24469          * @param {Roo.bootstrap.menu.Menu} this
24470          * @param {Roo.EventObject} e
24471          */
24472         click : true
24473     });
24474     
24475 };
24476
24477 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24478     
24479     submenu : false,
24480     html : '',
24481     weight : 'default',
24482     icon : false,
24483     pos : 'bottom',
24484     
24485     
24486     getChildContainer : function() {
24487         if(this.isSubMenu){
24488             return this.el;
24489         }
24490         
24491         return this.el.select('ul.dropdown-menu', true).first();  
24492     },
24493     
24494     getAutoCreate : function()
24495     {
24496         var text = [
24497             {
24498                 tag : 'span',
24499                 cls : 'roo-menu-text',
24500                 html : this.html
24501             }
24502         ];
24503         
24504         if(this.icon){
24505             text.unshift({
24506                 tag : 'i',
24507                 cls : 'fa ' + this.icon
24508             })
24509         }
24510         
24511         
24512         var cfg = {
24513             tag : 'div',
24514             cls : 'btn-group',
24515             cn : [
24516                 {
24517                     tag : 'button',
24518                     cls : 'dropdown-button btn btn-' + this.weight,
24519                     cn : text
24520                 },
24521                 {
24522                     tag : 'button',
24523                     cls : 'dropdown-toggle btn btn-' + this.weight,
24524                     cn : [
24525                         {
24526                             tag : 'span',
24527                             cls : 'caret'
24528                         }
24529                     ]
24530                 },
24531                 {
24532                     tag : 'ul',
24533                     cls : 'dropdown-menu'
24534                 }
24535             ]
24536             
24537         };
24538         
24539         if(this.pos == 'top'){
24540             cfg.cls += ' dropup';
24541         }
24542         
24543         if(this.isSubMenu){
24544             cfg = {
24545                 tag : 'ul',
24546                 cls : 'dropdown-menu'
24547             }
24548         }
24549         
24550         return cfg;
24551     },
24552     
24553     onRender : function(ct, position)
24554     {
24555         this.isSubMenu = ct.hasClass('dropdown-submenu');
24556         
24557         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24558     },
24559     
24560     initEvents : function() 
24561     {
24562         if(this.isSubMenu){
24563             return;
24564         }
24565         
24566         this.hidden = true;
24567         
24568         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24569         this.triggerEl.on('click', this.onTriggerPress, this);
24570         
24571         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24572         this.buttonEl.on('click', this.onClick, this);
24573         
24574     },
24575     
24576     list : function()
24577     {
24578         if(this.isSubMenu){
24579             return this.el;
24580         }
24581         
24582         return this.el.select('ul.dropdown-menu', true).first();
24583     },
24584     
24585     onClick : function(e)
24586     {
24587         this.fireEvent("click", this, e);
24588     },
24589     
24590     onTriggerPress  : function(e)
24591     {   
24592         if (this.isVisible()) {
24593             this.hide();
24594         } else {
24595             this.show();
24596         }
24597     },
24598     
24599     isVisible : function(){
24600         return !this.hidden;
24601     },
24602     
24603     show : function()
24604     {
24605         this.fireEvent("beforeshow", this);
24606         
24607         this.hidden = false;
24608         this.el.addClass('open');
24609         
24610         Roo.get(document).on("mouseup", this.onMouseUp, this);
24611         
24612         this.fireEvent("show", this);
24613         
24614         
24615     },
24616     
24617     hide : function()
24618     {
24619         this.fireEvent("beforehide", this);
24620         
24621         this.hidden = true;
24622         this.el.removeClass('open');
24623         
24624         Roo.get(document).un("mouseup", this.onMouseUp);
24625         
24626         this.fireEvent("hide", this);
24627     },
24628     
24629     onMouseUp : function()
24630     {
24631         this.hide();
24632     }
24633     
24634 });
24635
24636  
24637  /*
24638  * - LGPL
24639  *
24640  * menu item
24641  * 
24642  */
24643 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24644
24645 /**
24646  * @class Roo.bootstrap.menu.Item
24647  * @extends Roo.bootstrap.Component
24648  * Bootstrap MenuItem class
24649  * @cfg {Boolean} submenu (true | false) default false
24650  * @cfg {String} html text of the item
24651  * @cfg {String} href the link
24652  * @cfg {Boolean} disable (true | false) default false
24653  * @cfg {Boolean} preventDefault (true | false) default true
24654  * @cfg {String} icon Font awesome icon
24655  * @cfg {String} pos Submenu align to (left | right) default right 
24656  * 
24657  * 
24658  * @constructor
24659  * Create a new Item
24660  * @param {Object} config The config object
24661  */
24662
24663
24664 Roo.bootstrap.menu.Item = function(config){
24665     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24666     this.addEvents({
24667         /**
24668          * @event mouseover
24669          * Fires when the mouse is hovering over this menu
24670          * @param {Roo.bootstrap.menu.Item} this
24671          * @param {Roo.EventObject} e
24672          */
24673         mouseover : true,
24674         /**
24675          * @event mouseout
24676          * Fires when the mouse exits this menu
24677          * @param {Roo.bootstrap.menu.Item} this
24678          * @param {Roo.EventObject} e
24679          */
24680         mouseout : true,
24681         // raw events
24682         /**
24683          * @event click
24684          * The raw click event for the entire grid.
24685          * @param {Roo.EventObject} e
24686          */
24687         click : true
24688     });
24689 };
24690
24691 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24692     
24693     submenu : false,
24694     href : '',
24695     html : '',
24696     preventDefault: true,
24697     disable : false,
24698     icon : false,
24699     pos : 'right',
24700     
24701     getAutoCreate : function()
24702     {
24703         var text = [
24704             {
24705                 tag : 'span',
24706                 cls : 'roo-menu-item-text',
24707                 html : this.html
24708             }
24709         ];
24710         
24711         if(this.icon){
24712             text.unshift({
24713                 tag : 'i',
24714                 cls : 'fa ' + this.icon
24715             })
24716         }
24717         
24718         var cfg = {
24719             tag : 'li',
24720             cn : [
24721                 {
24722                     tag : 'a',
24723                     href : this.href || '#',
24724                     cn : text
24725                 }
24726             ]
24727         };
24728         
24729         if(this.disable){
24730             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24731         }
24732         
24733         if(this.submenu){
24734             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24735             
24736             if(this.pos == 'left'){
24737                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24738             }
24739         }
24740         
24741         return cfg;
24742     },
24743     
24744     initEvents : function() 
24745     {
24746         this.el.on('mouseover', this.onMouseOver, this);
24747         this.el.on('mouseout', this.onMouseOut, this);
24748         
24749         this.el.select('a', true).first().on('click', this.onClick, this);
24750         
24751     },
24752     
24753     onClick : function(e)
24754     {
24755         if(this.preventDefault){
24756             e.preventDefault();
24757         }
24758         
24759         this.fireEvent("click", this, e);
24760     },
24761     
24762     onMouseOver : function(e)
24763     {
24764         if(this.submenu && this.pos == 'left'){
24765             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24766         }
24767         
24768         this.fireEvent("mouseover", this, e);
24769     },
24770     
24771     onMouseOut : function(e)
24772     {
24773         this.fireEvent("mouseout", this, e);
24774     }
24775 });
24776
24777  
24778
24779  /*
24780  * - LGPL
24781  *
24782  * menu separator
24783  * 
24784  */
24785 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24786
24787 /**
24788  * @class Roo.bootstrap.menu.Separator
24789  * @extends Roo.bootstrap.Component
24790  * Bootstrap Separator class
24791  * 
24792  * @constructor
24793  * Create a new Separator
24794  * @param {Object} config The config object
24795  */
24796
24797
24798 Roo.bootstrap.menu.Separator = function(config){
24799     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24800 };
24801
24802 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24803     
24804     getAutoCreate : function(){
24805         var cfg = {
24806             tag : 'li',
24807             cls: 'divider'
24808         };
24809         
24810         return cfg;
24811     }
24812    
24813 });
24814
24815  
24816
24817  /*
24818  * - LGPL
24819  *
24820  * Tooltip
24821  * 
24822  */
24823
24824 /**
24825  * @class Roo.bootstrap.Tooltip
24826  * Bootstrap Tooltip class
24827  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24828  * to determine which dom element triggers the tooltip.
24829  * 
24830  * It needs to add support for additional attributes like tooltip-position
24831  * 
24832  * @constructor
24833  * Create a new Toolti
24834  * @param {Object} config The config object
24835  */
24836
24837 Roo.bootstrap.Tooltip = function(config){
24838     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24839 };
24840
24841 Roo.apply(Roo.bootstrap.Tooltip, {
24842     /**
24843      * @function init initialize tooltip monitoring.
24844      * @static
24845      */
24846     currentEl : false,
24847     currentTip : false,
24848     currentRegion : false,
24849     
24850     //  init : delay?
24851     
24852     init : function()
24853     {
24854         Roo.get(document).on('mouseover', this.enter ,this);
24855         Roo.get(document).on('mouseout', this.leave, this);
24856          
24857         
24858         this.currentTip = new Roo.bootstrap.Tooltip();
24859     },
24860     
24861     enter : function(ev)
24862     {
24863         var dom = ev.getTarget();
24864         
24865         //Roo.log(['enter',dom]);
24866         var el = Roo.fly(dom);
24867         if (this.currentEl) {
24868             //Roo.log(dom);
24869             //Roo.log(this.currentEl);
24870             //Roo.log(this.currentEl.contains(dom));
24871             if (this.currentEl == el) {
24872                 return;
24873             }
24874             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24875                 return;
24876             }
24877
24878         }
24879         
24880         if (this.currentTip.el) {
24881             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24882         }    
24883         //Roo.log(ev);
24884         
24885         if(!el || el.dom == document){
24886             return;
24887         }
24888         
24889         var bindEl = el;
24890         
24891         // you can not look for children, as if el is the body.. then everythign is the child..
24892         if (!el.attr('tooltip')) { //
24893             if (!el.select("[tooltip]").elements.length) {
24894                 return;
24895             }
24896             // is the mouse over this child...?
24897             bindEl = el.select("[tooltip]").first();
24898             var xy = ev.getXY();
24899             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24900                 //Roo.log("not in region.");
24901                 return;
24902             }
24903             //Roo.log("child element over..");
24904             
24905         }
24906         this.currentEl = bindEl;
24907         this.currentTip.bind(bindEl);
24908         this.currentRegion = Roo.lib.Region.getRegion(dom);
24909         this.currentTip.enter();
24910         
24911     },
24912     leave : function(ev)
24913     {
24914         var dom = ev.getTarget();
24915         //Roo.log(['leave',dom]);
24916         if (!this.currentEl) {
24917             return;
24918         }
24919         
24920         
24921         if (dom != this.currentEl.dom) {
24922             return;
24923         }
24924         var xy = ev.getXY();
24925         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24926             return;
24927         }
24928         // only activate leave if mouse cursor is outside... bounding box..
24929         
24930         
24931         
24932         
24933         if (this.currentTip) {
24934             this.currentTip.leave();
24935         }
24936         //Roo.log('clear currentEl');
24937         this.currentEl = false;
24938         
24939         
24940     },
24941     alignment : {
24942         'left' : ['r-l', [-2,0], 'right'],
24943         'right' : ['l-r', [2,0], 'left'],
24944         'bottom' : ['t-b', [0,2], 'top'],
24945         'top' : [ 'b-t', [0,-2], 'bottom']
24946     }
24947     
24948 });
24949
24950
24951 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24952     
24953     
24954     bindEl : false,
24955     
24956     delay : null, // can be { show : 300 , hide: 500}
24957     
24958     timeout : null,
24959     
24960     hoverState : null, //???
24961     
24962     placement : 'bottom', 
24963     
24964     getAutoCreate : function(){
24965     
24966         var cfg = {
24967            cls : 'tooltip',
24968            role : 'tooltip',
24969            cn : [
24970                 {
24971                     cls : 'tooltip-arrow'
24972                 },
24973                 {
24974                     cls : 'tooltip-inner'
24975                 }
24976            ]
24977         };
24978         
24979         return cfg;
24980     },
24981     bind : function(el)
24982     {
24983         this.bindEl = el;
24984     },
24985       
24986     
24987     enter : function () {
24988        
24989         if (this.timeout != null) {
24990             clearTimeout(this.timeout);
24991         }
24992         
24993         this.hoverState = 'in';
24994          //Roo.log("enter - show");
24995         if (!this.delay || !this.delay.show) {
24996             this.show();
24997             return;
24998         }
24999         var _t = this;
25000         this.timeout = setTimeout(function () {
25001             if (_t.hoverState == 'in') {
25002                 _t.show();
25003             }
25004         }, this.delay.show);
25005     },
25006     leave : function()
25007     {
25008         clearTimeout(this.timeout);
25009     
25010         this.hoverState = 'out';
25011          if (!this.delay || !this.delay.hide) {
25012             this.hide();
25013             return;
25014         }
25015        
25016         var _t = this;
25017         this.timeout = setTimeout(function () {
25018             //Roo.log("leave - timeout");
25019             
25020             if (_t.hoverState == 'out') {
25021                 _t.hide();
25022                 Roo.bootstrap.Tooltip.currentEl = false;
25023             }
25024         }, delay);
25025     },
25026     
25027     show : function ()
25028     {
25029         if (!this.el) {
25030             this.render(document.body);
25031         }
25032         // set content.
25033         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25034         
25035         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25036         
25037         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25038         
25039         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25040         
25041         var placement = typeof this.placement == 'function' ?
25042             this.placement.call(this, this.el, on_el) :
25043             this.placement;
25044             
25045         var autoToken = /\s?auto?\s?/i;
25046         var autoPlace = autoToken.test(placement);
25047         if (autoPlace) {
25048             placement = placement.replace(autoToken, '') || 'top';
25049         }
25050         
25051         //this.el.detach()
25052         //this.el.setXY([0,0]);
25053         this.el.show();
25054         //this.el.dom.style.display='block';
25055         
25056         //this.el.appendTo(on_el);
25057         
25058         var p = this.getPosition();
25059         var box = this.el.getBox();
25060         
25061         if (autoPlace) {
25062             // fixme..
25063         }
25064         
25065         var align = Roo.bootstrap.Tooltip.alignment[placement];
25066         
25067         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25068         
25069         if(placement == 'top' || placement == 'bottom'){
25070             if(xy[0] < 0){
25071                 placement = 'right';
25072             }
25073             
25074             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25075                 placement = 'left';
25076             }
25077             
25078             var scroll = Roo.select('body', true).first().getScroll();
25079             
25080             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25081                 placement = 'top';
25082             }
25083             
25084         }
25085         
25086         align = Roo.bootstrap.Tooltip.alignment[placement];
25087         
25088         this.el.alignTo(this.bindEl, align[0],align[1]);
25089         //var arrow = this.el.select('.arrow',true).first();
25090         //arrow.set(align[2], 
25091         
25092         this.el.addClass(placement);
25093         
25094         this.el.addClass('in fade');
25095         
25096         this.hoverState = null;
25097         
25098         if (this.el.hasClass('fade')) {
25099             // fade it?
25100         }
25101         
25102     },
25103     hide : function()
25104     {
25105          
25106         if (!this.el) {
25107             return;
25108         }
25109         //this.el.setXY([0,0]);
25110         this.el.removeClass('in');
25111         //this.el.hide();
25112         
25113     }
25114     
25115 });
25116  
25117
25118  /*
25119  * - LGPL
25120  *
25121  * Location Picker
25122  * 
25123  */
25124
25125 /**
25126  * @class Roo.bootstrap.LocationPicker
25127  * @extends Roo.bootstrap.Component
25128  * Bootstrap LocationPicker class
25129  * @cfg {Number} latitude Position when init default 0
25130  * @cfg {Number} longitude Position when init default 0
25131  * @cfg {Number} zoom default 15
25132  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25133  * @cfg {Boolean} mapTypeControl default false
25134  * @cfg {Boolean} disableDoubleClickZoom default false
25135  * @cfg {Boolean} scrollwheel default true
25136  * @cfg {Boolean} streetViewControl default false
25137  * @cfg {Number} radius default 0
25138  * @cfg {String} locationName
25139  * @cfg {Boolean} draggable default true
25140  * @cfg {Boolean} enableAutocomplete default false
25141  * @cfg {Boolean} enableReverseGeocode default true
25142  * @cfg {String} markerTitle
25143  * 
25144  * @constructor
25145  * Create a new LocationPicker
25146  * @param {Object} config The config object
25147  */
25148
25149
25150 Roo.bootstrap.LocationPicker = function(config){
25151     
25152     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25153     
25154     this.addEvents({
25155         /**
25156          * @event initial
25157          * Fires when the picker initialized.
25158          * @param {Roo.bootstrap.LocationPicker} this
25159          * @param {Google Location} location
25160          */
25161         initial : true,
25162         /**
25163          * @event positionchanged
25164          * Fires when the picker position changed.
25165          * @param {Roo.bootstrap.LocationPicker} this
25166          * @param {Google Location} location
25167          */
25168         positionchanged : true,
25169         /**
25170          * @event resize
25171          * Fires when the map resize.
25172          * @param {Roo.bootstrap.LocationPicker} this
25173          */
25174         resize : true,
25175         /**
25176          * @event show
25177          * Fires when the map show.
25178          * @param {Roo.bootstrap.LocationPicker} this
25179          */
25180         show : true,
25181         /**
25182          * @event hide
25183          * Fires when the map hide.
25184          * @param {Roo.bootstrap.LocationPicker} this
25185          */
25186         hide : true,
25187         /**
25188          * @event mapClick
25189          * Fires when click the map.
25190          * @param {Roo.bootstrap.LocationPicker} this
25191          * @param {Map event} e
25192          */
25193         mapClick : true,
25194         /**
25195          * @event mapRightClick
25196          * Fires when right click the map.
25197          * @param {Roo.bootstrap.LocationPicker} this
25198          * @param {Map event} e
25199          */
25200         mapRightClick : true,
25201         /**
25202          * @event markerClick
25203          * Fires when click the marker.
25204          * @param {Roo.bootstrap.LocationPicker} this
25205          * @param {Map event} e
25206          */
25207         markerClick : true,
25208         /**
25209          * @event markerRightClick
25210          * Fires when right click the marker.
25211          * @param {Roo.bootstrap.LocationPicker} this
25212          * @param {Map event} e
25213          */
25214         markerRightClick : true,
25215         /**
25216          * @event OverlayViewDraw
25217          * Fires when OverlayView Draw
25218          * @param {Roo.bootstrap.LocationPicker} this
25219          */
25220         OverlayViewDraw : true,
25221         /**
25222          * @event OverlayViewOnAdd
25223          * Fires when OverlayView Draw
25224          * @param {Roo.bootstrap.LocationPicker} this
25225          */
25226         OverlayViewOnAdd : true,
25227         /**
25228          * @event OverlayViewOnRemove
25229          * Fires when OverlayView Draw
25230          * @param {Roo.bootstrap.LocationPicker} this
25231          */
25232         OverlayViewOnRemove : true,
25233         /**
25234          * @event OverlayViewShow
25235          * Fires when OverlayView Draw
25236          * @param {Roo.bootstrap.LocationPicker} this
25237          * @param {Pixel} cpx
25238          */
25239         OverlayViewShow : true,
25240         /**
25241          * @event OverlayViewHide
25242          * Fires when OverlayView Draw
25243          * @param {Roo.bootstrap.LocationPicker} this
25244          */
25245         OverlayViewHide : true,
25246         /**
25247          * @event loadexception
25248          * Fires when load google lib failed.
25249          * @param {Roo.bootstrap.LocationPicker} this
25250          */
25251         loadexception : true
25252     });
25253         
25254 };
25255
25256 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25257     
25258     gMapContext: false,
25259     
25260     latitude: 0,
25261     longitude: 0,
25262     zoom: 15,
25263     mapTypeId: false,
25264     mapTypeControl: false,
25265     disableDoubleClickZoom: false,
25266     scrollwheel: true,
25267     streetViewControl: false,
25268     radius: 0,
25269     locationName: '',
25270     draggable: true,
25271     enableAutocomplete: false,
25272     enableReverseGeocode: true,
25273     markerTitle: '',
25274     
25275     getAutoCreate: function()
25276     {
25277
25278         var cfg = {
25279             tag: 'div',
25280             cls: 'roo-location-picker'
25281         };
25282         
25283         return cfg
25284     },
25285     
25286     initEvents: function(ct, position)
25287     {       
25288         if(!this.el.getWidth() || this.isApplied()){
25289             return;
25290         }
25291         
25292         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25293         
25294         this.initial();
25295     },
25296     
25297     initial: function()
25298     {
25299         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25300             this.fireEvent('loadexception', this);
25301             return;
25302         }
25303         
25304         if(!this.mapTypeId){
25305             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25306         }
25307         
25308         this.gMapContext = this.GMapContext();
25309         
25310         this.initOverlayView();
25311         
25312         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25313         
25314         var _this = this;
25315                 
25316         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25317             _this.setPosition(_this.gMapContext.marker.position);
25318         });
25319         
25320         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25321             _this.fireEvent('mapClick', this, event);
25322             
25323         });
25324
25325         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25326             _this.fireEvent('mapRightClick', this, event);
25327             
25328         });
25329         
25330         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25331             _this.fireEvent('markerClick', this, event);
25332             
25333         });
25334
25335         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25336             _this.fireEvent('markerRightClick', this, event);
25337             
25338         });
25339         
25340         this.setPosition(this.gMapContext.location);
25341         
25342         this.fireEvent('initial', this, this.gMapContext.location);
25343     },
25344     
25345     initOverlayView: function()
25346     {
25347         var _this = this;
25348         
25349         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25350             
25351             draw: function()
25352             {
25353                 _this.fireEvent('OverlayViewDraw', _this);
25354             },
25355             
25356             onAdd: function()
25357             {
25358                 _this.fireEvent('OverlayViewOnAdd', _this);
25359             },
25360             
25361             onRemove: function()
25362             {
25363                 _this.fireEvent('OverlayViewOnRemove', _this);
25364             },
25365             
25366             show: function(cpx)
25367             {
25368                 _this.fireEvent('OverlayViewShow', _this, cpx);
25369             },
25370             
25371             hide: function()
25372             {
25373                 _this.fireEvent('OverlayViewHide', _this);
25374             }
25375             
25376         });
25377     },
25378     
25379     fromLatLngToContainerPixel: function(event)
25380     {
25381         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25382     },
25383     
25384     isApplied: function() 
25385     {
25386         return this.getGmapContext() == false ? false : true;
25387     },
25388     
25389     getGmapContext: function() 
25390     {
25391         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25392     },
25393     
25394     GMapContext: function() 
25395     {
25396         var position = new google.maps.LatLng(this.latitude, this.longitude);
25397         
25398         var _map = new google.maps.Map(this.el.dom, {
25399             center: position,
25400             zoom: this.zoom,
25401             mapTypeId: this.mapTypeId,
25402             mapTypeControl: this.mapTypeControl,
25403             disableDoubleClickZoom: this.disableDoubleClickZoom,
25404             scrollwheel: this.scrollwheel,
25405             streetViewControl: this.streetViewControl,
25406             locationName: this.locationName,
25407             draggable: this.draggable,
25408             enableAutocomplete: this.enableAutocomplete,
25409             enableReverseGeocode: this.enableReverseGeocode
25410         });
25411         
25412         var _marker = new google.maps.Marker({
25413             position: position,
25414             map: _map,
25415             title: this.markerTitle,
25416             draggable: this.draggable
25417         });
25418         
25419         return {
25420             map: _map,
25421             marker: _marker,
25422             circle: null,
25423             location: position,
25424             radius: this.radius,
25425             locationName: this.locationName,
25426             addressComponents: {
25427                 formatted_address: null,
25428                 addressLine1: null,
25429                 addressLine2: null,
25430                 streetName: null,
25431                 streetNumber: null,
25432                 city: null,
25433                 district: null,
25434                 state: null,
25435                 stateOrProvince: null
25436             },
25437             settings: this,
25438             domContainer: this.el.dom,
25439             geodecoder: new google.maps.Geocoder()
25440         };
25441     },
25442     
25443     drawCircle: function(center, radius, options) 
25444     {
25445         if (this.gMapContext.circle != null) {
25446             this.gMapContext.circle.setMap(null);
25447         }
25448         if (radius > 0) {
25449             radius *= 1;
25450             options = Roo.apply({}, options, {
25451                 strokeColor: "#0000FF",
25452                 strokeOpacity: .35,
25453                 strokeWeight: 2,
25454                 fillColor: "#0000FF",
25455                 fillOpacity: .2
25456             });
25457             
25458             options.map = this.gMapContext.map;
25459             options.radius = radius;
25460             options.center = center;
25461             this.gMapContext.circle = new google.maps.Circle(options);
25462             return this.gMapContext.circle;
25463         }
25464         
25465         return null;
25466     },
25467     
25468     setPosition: function(location) 
25469     {
25470         this.gMapContext.location = location;
25471         this.gMapContext.marker.setPosition(location);
25472         this.gMapContext.map.panTo(location);
25473         this.drawCircle(location, this.gMapContext.radius, {});
25474         
25475         var _this = this;
25476         
25477         if (this.gMapContext.settings.enableReverseGeocode) {
25478             this.gMapContext.geodecoder.geocode({
25479                 latLng: this.gMapContext.location
25480             }, function(results, status) {
25481                 
25482                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25483                     _this.gMapContext.locationName = results[0].formatted_address;
25484                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25485                     
25486                     _this.fireEvent('positionchanged', this, location);
25487                 }
25488             });
25489             
25490             return;
25491         }
25492         
25493         this.fireEvent('positionchanged', this, location);
25494     },
25495     
25496     resize: function()
25497     {
25498         google.maps.event.trigger(this.gMapContext.map, "resize");
25499         
25500         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25501         
25502         this.fireEvent('resize', this);
25503     },
25504     
25505     setPositionByLatLng: function(latitude, longitude)
25506     {
25507         this.setPosition(new google.maps.LatLng(latitude, longitude));
25508     },
25509     
25510     getCurrentPosition: function() 
25511     {
25512         return {
25513             latitude: this.gMapContext.location.lat(),
25514             longitude: this.gMapContext.location.lng()
25515         };
25516     },
25517     
25518     getAddressName: function() 
25519     {
25520         return this.gMapContext.locationName;
25521     },
25522     
25523     getAddressComponents: function() 
25524     {
25525         return this.gMapContext.addressComponents;
25526     },
25527     
25528     address_component_from_google_geocode: function(address_components) 
25529     {
25530         var result = {};
25531         
25532         for (var i = 0; i < address_components.length; i++) {
25533             var component = address_components[i];
25534             if (component.types.indexOf("postal_code") >= 0) {
25535                 result.postalCode = component.short_name;
25536             } else if (component.types.indexOf("street_number") >= 0) {
25537                 result.streetNumber = component.short_name;
25538             } else if (component.types.indexOf("route") >= 0) {
25539                 result.streetName = component.short_name;
25540             } else if (component.types.indexOf("neighborhood") >= 0) {
25541                 result.city = component.short_name;
25542             } else if (component.types.indexOf("locality") >= 0) {
25543                 result.city = component.short_name;
25544             } else if (component.types.indexOf("sublocality") >= 0) {
25545                 result.district = component.short_name;
25546             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25547                 result.stateOrProvince = component.short_name;
25548             } else if (component.types.indexOf("country") >= 0) {
25549                 result.country = component.short_name;
25550             }
25551         }
25552         
25553         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25554         result.addressLine2 = "";
25555         return result;
25556     },
25557     
25558     setZoomLevel: function(zoom)
25559     {
25560         this.gMapContext.map.setZoom(zoom);
25561     },
25562     
25563     show: function()
25564     {
25565         if(!this.el){
25566             return;
25567         }
25568         
25569         this.el.show();
25570         
25571         this.resize();
25572         
25573         this.fireEvent('show', this);
25574     },
25575     
25576     hide: function()
25577     {
25578         if(!this.el){
25579             return;
25580         }
25581         
25582         this.el.hide();
25583         
25584         this.fireEvent('hide', this);
25585     }
25586     
25587 });
25588
25589 Roo.apply(Roo.bootstrap.LocationPicker, {
25590     
25591     OverlayView : function(map, options)
25592     {
25593         options = options || {};
25594         
25595         this.setMap(map);
25596     }
25597     
25598     
25599 });/*
25600  * - LGPL
25601  *
25602  * Alert
25603  * 
25604  */
25605
25606 /**
25607  * @class Roo.bootstrap.Alert
25608  * @extends Roo.bootstrap.Component
25609  * Bootstrap Alert class
25610  * @cfg {String} title The title of alert
25611  * @cfg {String} html The content of alert
25612  * @cfg {String} weight (  success | info | warning | danger )
25613  * @cfg {String} faicon font-awesomeicon
25614  * 
25615  * @constructor
25616  * Create a new alert
25617  * @param {Object} config The config object
25618  */
25619
25620
25621 Roo.bootstrap.Alert = function(config){
25622     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25623     
25624 };
25625
25626 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25627     
25628     title: '',
25629     html: '',
25630     weight: false,
25631     faicon: false,
25632     
25633     getAutoCreate : function()
25634     {
25635         
25636         var cfg = {
25637             tag : 'div',
25638             cls : 'alert',
25639             cn : [
25640                 {
25641                     tag : 'i',
25642                     cls : 'roo-alert-icon'
25643                     
25644                 },
25645                 {
25646                     tag : 'b',
25647                     cls : 'roo-alert-title',
25648                     html : this.title
25649                 },
25650                 {
25651                     tag : 'span',
25652                     cls : 'roo-alert-text',
25653                     html : this.html
25654                 }
25655             ]
25656         };
25657         
25658         if(this.faicon){
25659             cfg.cn[0].cls += ' fa ' + this.faicon;
25660         }
25661         
25662         if(this.weight){
25663             cfg.cls += ' alert-' + this.weight;
25664         }
25665         
25666         return cfg;
25667     },
25668     
25669     initEvents: function() 
25670     {
25671         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25672     },
25673     
25674     setTitle : function(str)
25675     {
25676         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25677     },
25678     
25679     setText : function(str)
25680     {
25681         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25682     },
25683     
25684     setWeight : function(weight)
25685     {
25686         if(this.weight){
25687             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25688         }
25689         
25690         this.weight = weight;
25691         
25692         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25693     },
25694     
25695     setIcon : function(icon)
25696     {
25697         if(this.faicon){
25698             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25699         }
25700         
25701         this.faicon = icon;
25702         
25703         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25704     },
25705     
25706     hide: function() 
25707     {
25708         this.el.hide();   
25709     },
25710     
25711     show: function() 
25712     {  
25713         this.el.show();   
25714     }
25715     
25716 });
25717
25718  
25719 /*
25720 * Licence: LGPL
25721 */
25722
25723 /**
25724  * @class Roo.bootstrap.UploadCropbox
25725  * @extends Roo.bootstrap.Component
25726  * Bootstrap UploadCropbox class
25727  * @cfg {String} emptyText show when image has been loaded
25728  * @cfg {String} rotateNotify show when image too small to rotate
25729  * @cfg {Number} errorTimeout default 3000
25730  * @cfg {Number} minWidth default 300
25731  * @cfg {Number} minHeight default 300
25732  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25733  * @cfg {Boolean} isDocument (true|false) default false
25734  * @cfg {String} url action url
25735  * @cfg {String} paramName default 'imageUpload'
25736  * @cfg {String} method default POST
25737  * @cfg {Boolean} loadMask (true|false) default true
25738  * @cfg {Boolean} loadingText default 'Loading...'
25739  * 
25740  * @constructor
25741  * Create a new UploadCropbox
25742  * @param {Object} config The config object
25743  */
25744
25745 Roo.bootstrap.UploadCropbox = function(config){
25746     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25747     
25748     this.addEvents({
25749         /**
25750          * @event beforeselectfile
25751          * Fire before select file
25752          * @param {Roo.bootstrap.UploadCropbox} this
25753          */
25754         "beforeselectfile" : true,
25755         /**
25756          * @event initial
25757          * Fire after initEvent
25758          * @param {Roo.bootstrap.UploadCropbox} this
25759          */
25760         "initial" : true,
25761         /**
25762          * @event crop
25763          * Fire after initEvent
25764          * @param {Roo.bootstrap.UploadCropbox} this
25765          * @param {String} data
25766          */
25767         "crop" : true,
25768         /**
25769          * @event prepare
25770          * Fire when preparing the file data
25771          * @param {Roo.bootstrap.UploadCropbox} this
25772          * @param {Object} file
25773          */
25774         "prepare" : true,
25775         /**
25776          * @event exception
25777          * Fire when get exception
25778          * @param {Roo.bootstrap.UploadCropbox} this
25779          * @param {XMLHttpRequest} xhr
25780          */
25781         "exception" : true,
25782         /**
25783          * @event beforeloadcanvas
25784          * Fire before load the canvas
25785          * @param {Roo.bootstrap.UploadCropbox} this
25786          * @param {String} src
25787          */
25788         "beforeloadcanvas" : true,
25789         /**
25790          * @event trash
25791          * Fire when trash image
25792          * @param {Roo.bootstrap.UploadCropbox} this
25793          */
25794         "trash" : true,
25795         /**
25796          * @event download
25797          * Fire when download the image
25798          * @param {Roo.bootstrap.UploadCropbox} this
25799          */
25800         "download" : true,
25801         /**
25802          * @event footerbuttonclick
25803          * Fire when footerbuttonclick
25804          * @param {Roo.bootstrap.UploadCropbox} this
25805          * @param {String} type
25806          */
25807         "footerbuttonclick" : true,
25808         /**
25809          * @event resize
25810          * Fire when resize
25811          * @param {Roo.bootstrap.UploadCropbox} this
25812          */
25813         "resize" : true,
25814         /**
25815          * @event rotate
25816          * Fire when rotate the image
25817          * @param {Roo.bootstrap.UploadCropbox} this
25818          * @param {String} pos
25819          */
25820         "rotate" : true,
25821         /**
25822          * @event inspect
25823          * Fire when inspect the file
25824          * @param {Roo.bootstrap.UploadCropbox} this
25825          * @param {Object} file
25826          */
25827         "inspect" : true,
25828         /**
25829          * @event upload
25830          * Fire when xhr upload the file
25831          * @param {Roo.bootstrap.UploadCropbox} this
25832          * @param {Object} data
25833          */
25834         "upload" : true,
25835         /**
25836          * @event arrange
25837          * Fire when arrange the file data
25838          * @param {Roo.bootstrap.UploadCropbox} this
25839          * @param {Object} formData
25840          */
25841         "arrange" : true
25842     });
25843     
25844     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25845 };
25846
25847 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25848     
25849     emptyText : 'Click to upload image',
25850     rotateNotify : 'Image is too small to rotate',
25851     errorTimeout : 3000,
25852     scale : 0,
25853     baseScale : 1,
25854     rotate : 0,
25855     dragable : false,
25856     pinching : false,
25857     mouseX : 0,
25858     mouseY : 0,
25859     cropData : false,
25860     minWidth : 300,
25861     minHeight : 300,
25862     file : false,
25863     exif : {},
25864     baseRotate : 1,
25865     cropType : 'image/jpeg',
25866     buttons : false,
25867     canvasLoaded : false,
25868     isDocument : false,
25869     method : 'POST',
25870     paramName : 'imageUpload',
25871     loadMask : true,
25872     loadingText : 'Loading...',
25873     maskEl : false,
25874     
25875     getAutoCreate : function()
25876     {
25877         var cfg = {
25878             tag : 'div',
25879             cls : 'roo-upload-cropbox',
25880             cn : [
25881                 {
25882                     tag : 'input',
25883                     cls : 'roo-upload-cropbox-selector',
25884                     type : 'file'
25885                 },
25886                 {
25887                     tag : 'div',
25888                     cls : 'roo-upload-cropbox-body',
25889                     style : 'cursor:pointer',
25890                     cn : [
25891                         {
25892                             tag : 'div',
25893                             cls : 'roo-upload-cropbox-preview'
25894                         },
25895                         {
25896                             tag : 'div',
25897                             cls : 'roo-upload-cropbox-thumb'
25898                         },
25899                         {
25900                             tag : 'div',
25901                             cls : 'roo-upload-cropbox-empty-notify',
25902                             html : this.emptyText
25903                         },
25904                         {
25905                             tag : 'div',
25906                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25907                             html : this.rotateNotify
25908                         }
25909                     ]
25910                 },
25911                 {
25912                     tag : 'div',
25913                     cls : 'roo-upload-cropbox-footer',
25914                     cn : {
25915                         tag : 'div',
25916                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25917                         cn : []
25918                     }
25919                 }
25920             ]
25921         };
25922         
25923         return cfg;
25924     },
25925     
25926     onRender : function(ct, position)
25927     {
25928         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25929         
25930         if (this.buttons.length) {
25931             
25932             Roo.each(this.buttons, function(bb) {
25933                 
25934                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25935                 
25936                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25937                 
25938             }, this);
25939         }
25940         
25941         if(this.loadMask){
25942             this.maskEl = this.el;
25943         }
25944     },
25945     
25946     initEvents : function()
25947     {
25948         this.urlAPI = (window.createObjectURL && window) || 
25949                                 (window.URL && URL.revokeObjectURL && URL) || 
25950                                 (window.webkitURL && webkitURL);
25951                         
25952         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25953         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25954         
25955         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25956         this.selectorEl.hide();
25957         
25958         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25959         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25960         
25961         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25962         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25963         this.thumbEl.hide();
25964         
25965         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25966         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25967         
25968         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25969         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25970         this.errorEl.hide();
25971         
25972         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25973         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25974         this.footerEl.hide();
25975         
25976         this.setThumbBoxSize();
25977         
25978         this.bind();
25979         
25980         this.resize();
25981         
25982         this.fireEvent('initial', this);
25983     },
25984
25985     bind : function()
25986     {
25987         var _this = this;
25988         
25989         window.addEventListener("resize", function() { _this.resize(); } );
25990         
25991         this.bodyEl.on('click', this.beforeSelectFile, this);
25992         
25993         if(Roo.isTouch){
25994             this.bodyEl.on('touchstart', this.onTouchStart, this);
25995             this.bodyEl.on('touchmove', this.onTouchMove, this);
25996             this.bodyEl.on('touchend', this.onTouchEnd, this);
25997         }
25998         
25999         if(!Roo.isTouch){
26000             this.bodyEl.on('mousedown', this.onMouseDown, this);
26001             this.bodyEl.on('mousemove', this.onMouseMove, this);
26002             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26003             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26004             Roo.get(document).on('mouseup', this.onMouseUp, this);
26005         }
26006         
26007         this.selectorEl.on('change', this.onFileSelected, this);
26008     },
26009     
26010     reset : function()
26011     {    
26012         this.scale = 0;
26013         this.baseScale = 1;
26014         this.rotate = 0;
26015         this.baseRotate = 1;
26016         this.dragable = false;
26017         this.pinching = false;
26018         this.mouseX = 0;
26019         this.mouseY = 0;
26020         this.cropData = false;
26021         this.notifyEl.dom.innerHTML = this.emptyText;
26022         
26023         this.selectorEl.dom.value = '';
26024         
26025     },
26026     
26027     resize : function()
26028     {
26029         if(this.fireEvent('resize', this) != false){
26030             this.setThumbBoxPosition();
26031             this.setCanvasPosition();
26032         }
26033     },
26034     
26035     onFooterButtonClick : function(e, el, o, type)
26036     {
26037         switch (type) {
26038             case 'rotate-left' :
26039                 this.onRotateLeft(e);
26040                 break;
26041             case 'rotate-right' :
26042                 this.onRotateRight(e);
26043                 break;
26044             case 'picture' :
26045                 this.beforeSelectFile(e);
26046                 break;
26047             case 'trash' :
26048                 this.trash(e);
26049                 break;
26050             case 'crop' :
26051                 this.crop(e);
26052                 break;
26053             case 'download' :
26054                 this.download(e);
26055                 break;
26056             default :
26057                 break;
26058         }
26059         
26060         this.fireEvent('footerbuttonclick', this, type);
26061     },
26062     
26063     beforeSelectFile : function(e)
26064     {
26065         e.preventDefault();
26066         
26067         if(this.fireEvent('beforeselectfile', this) != false){
26068             this.selectorEl.dom.click();
26069         }
26070     },
26071     
26072     onFileSelected : function(e)
26073     {
26074         e.preventDefault();
26075         
26076         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26077             return;
26078         }
26079         
26080         var file = this.selectorEl.dom.files[0];
26081         
26082         if(this.fireEvent('inspect', this, file) != false){
26083             this.prepare(file);
26084         }
26085         
26086     },
26087     
26088     trash : function(e)
26089     {
26090         this.fireEvent('trash', this);
26091     },
26092     
26093     download : function(e)
26094     {
26095         this.fireEvent('download', this);
26096     },
26097     
26098     loadCanvas : function(src)
26099     {   
26100         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26101             
26102             this.reset();
26103             
26104             this.imageEl = document.createElement('img');
26105             
26106             var _this = this;
26107             
26108             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26109             
26110             this.imageEl.src = src;
26111         }
26112     },
26113     
26114     onLoadCanvas : function()
26115     {   
26116         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26117         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26118         
26119         this.bodyEl.un('click', this.beforeSelectFile, this);
26120         
26121         this.notifyEl.hide();
26122         this.thumbEl.show();
26123         this.footerEl.show();
26124         
26125         this.baseRotateLevel();
26126         
26127         if(this.isDocument){
26128             this.setThumbBoxSize();
26129         }
26130         
26131         this.setThumbBoxPosition();
26132         
26133         this.baseScaleLevel();
26134         
26135         this.draw();
26136         
26137         this.resize();
26138         
26139         this.canvasLoaded = true;
26140         
26141         if(this.loadMask){
26142             this.maskEl.unmask();
26143         }
26144         
26145     },
26146     
26147     setCanvasPosition : function()
26148     {   
26149         if(!this.canvasEl){
26150             return;
26151         }
26152         
26153         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26154         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26155         
26156         this.previewEl.setLeft(pw);
26157         this.previewEl.setTop(ph);
26158         
26159     },
26160     
26161     onMouseDown : function(e)
26162     {   
26163         e.stopEvent();
26164         
26165         this.dragable = true;
26166         this.pinching = false;
26167         
26168         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26169             this.dragable = false;
26170             return;
26171         }
26172         
26173         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26174         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26175         
26176     },
26177     
26178     onMouseMove : function(e)
26179     {   
26180         e.stopEvent();
26181         
26182         if(!this.canvasLoaded){
26183             return;
26184         }
26185         
26186         if (!this.dragable){
26187             return;
26188         }
26189         
26190         var minX = Math.ceil(this.thumbEl.getLeft(true));
26191         var minY = Math.ceil(this.thumbEl.getTop(true));
26192         
26193         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26194         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26195         
26196         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26197         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26198         
26199         x = x - this.mouseX;
26200         y = y - this.mouseY;
26201         
26202         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26203         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26204         
26205         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26206         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26207         
26208         this.previewEl.setLeft(bgX);
26209         this.previewEl.setTop(bgY);
26210         
26211         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26212         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26213     },
26214     
26215     onMouseUp : function(e)
26216     {   
26217         e.stopEvent();
26218         
26219         this.dragable = false;
26220     },
26221     
26222     onMouseWheel : function(e)
26223     {   
26224         e.stopEvent();
26225         
26226         this.startScale = this.scale;
26227         
26228         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26229         
26230         if(!this.zoomable()){
26231             this.scale = this.startScale;
26232             return;
26233         }
26234         
26235         this.draw();
26236         
26237         return;
26238     },
26239     
26240     zoomable : function()
26241     {
26242         var minScale = this.thumbEl.getWidth() / this.minWidth;
26243         
26244         if(this.minWidth < this.minHeight){
26245             minScale = this.thumbEl.getHeight() / this.minHeight;
26246         }
26247         
26248         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26249         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26250         
26251         if(
26252                 this.isDocument &&
26253                 (this.rotate == 0 || this.rotate == 180) && 
26254                 (
26255                     width > this.imageEl.OriginWidth || 
26256                     height > this.imageEl.OriginHeight ||
26257                     (width < this.minWidth && height < this.minHeight)
26258                 )
26259         ){
26260             return false;
26261         }
26262         
26263         if(
26264                 this.isDocument &&
26265                 (this.rotate == 90 || this.rotate == 270) && 
26266                 (
26267                     width > this.imageEl.OriginWidth || 
26268                     height > this.imageEl.OriginHeight ||
26269                     (width < this.minHeight && height < this.minWidth)
26270                 )
26271         ){
26272             return false;
26273         }
26274         
26275         if(
26276                 !this.isDocument &&
26277                 (this.rotate == 0 || this.rotate == 180) && 
26278                 (
26279                     width < this.minWidth || 
26280                     width > this.imageEl.OriginWidth || 
26281                     height < this.minHeight || 
26282                     height > this.imageEl.OriginHeight
26283                 )
26284         ){
26285             return false;
26286         }
26287         
26288         if(
26289                 !this.isDocument &&
26290                 (this.rotate == 90 || this.rotate == 270) && 
26291                 (
26292                     width < this.minHeight || 
26293                     width > this.imageEl.OriginWidth || 
26294                     height < this.minWidth || 
26295                     height > this.imageEl.OriginHeight
26296                 )
26297         ){
26298             return false;
26299         }
26300         
26301         return true;
26302         
26303     },
26304     
26305     onRotateLeft : function(e)
26306     {   
26307         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26308             
26309             var minScale = this.thumbEl.getWidth() / this.minWidth;
26310             
26311             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26312             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26313             
26314             this.startScale = this.scale;
26315             
26316             while (this.getScaleLevel() < minScale){
26317             
26318                 this.scale = this.scale + 1;
26319                 
26320                 if(!this.zoomable()){
26321                     break;
26322                 }
26323                 
26324                 if(
26325                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26326                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26327                 ){
26328                     continue;
26329                 }
26330                 
26331                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26332
26333                 this.draw();
26334                 
26335                 return;
26336             }
26337             
26338             this.scale = this.startScale;
26339             
26340             this.onRotateFail();
26341             
26342             return false;
26343         }
26344         
26345         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26346
26347         if(this.isDocument){
26348             this.setThumbBoxSize();
26349             this.setThumbBoxPosition();
26350             this.setCanvasPosition();
26351         }
26352         
26353         this.draw();
26354         
26355         this.fireEvent('rotate', this, 'left');
26356         
26357     },
26358     
26359     onRotateRight : function(e)
26360     {
26361         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26362             
26363             var minScale = this.thumbEl.getWidth() / this.minWidth;
26364         
26365             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26366             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26367             
26368             this.startScale = this.scale;
26369             
26370             while (this.getScaleLevel() < minScale){
26371             
26372                 this.scale = this.scale + 1;
26373                 
26374                 if(!this.zoomable()){
26375                     break;
26376                 }
26377                 
26378                 if(
26379                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26380                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26381                 ){
26382                     continue;
26383                 }
26384                 
26385                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26386
26387                 this.draw();
26388                 
26389                 return;
26390             }
26391             
26392             this.scale = this.startScale;
26393             
26394             this.onRotateFail();
26395             
26396             return false;
26397         }
26398         
26399         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26400
26401         if(this.isDocument){
26402             this.setThumbBoxSize();
26403             this.setThumbBoxPosition();
26404             this.setCanvasPosition();
26405         }
26406         
26407         this.draw();
26408         
26409         this.fireEvent('rotate', this, 'right');
26410     },
26411     
26412     onRotateFail : function()
26413     {
26414         this.errorEl.show(true);
26415         
26416         var _this = this;
26417         
26418         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26419     },
26420     
26421     draw : function()
26422     {
26423         this.previewEl.dom.innerHTML = '';
26424         
26425         var canvasEl = document.createElement("canvas");
26426         
26427         var contextEl = canvasEl.getContext("2d");
26428         
26429         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26430         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26431         var center = this.imageEl.OriginWidth / 2;
26432         
26433         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26434             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26435             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26436             center = this.imageEl.OriginHeight / 2;
26437         }
26438         
26439         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26440         
26441         contextEl.translate(center, center);
26442         contextEl.rotate(this.rotate * Math.PI / 180);
26443
26444         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26445         
26446         this.canvasEl = document.createElement("canvas");
26447         
26448         this.contextEl = this.canvasEl.getContext("2d");
26449         
26450         switch (this.rotate) {
26451             case 0 :
26452                 
26453                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26454                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26455                 
26456                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26457                 
26458                 break;
26459             case 90 : 
26460                 
26461                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26462                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26463                 
26464                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26465                     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);
26466                     break;
26467                 }
26468                 
26469                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26470                 
26471                 break;
26472             case 180 :
26473                 
26474                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26475                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26476                 
26477                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26478                     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);
26479                     break;
26480                 }
26481                 
26482                 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);
26483                 
26484                 break;
26485             case 270 :
26486                 
26487                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26488                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26489         
26490                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26491                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26492                     break;
26493                 }
26494                 
26495                 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);
26496                 
26497                 break;
26498             default : 
26499                 break;
26500         }
26501         
26502         this.previewEl.appendChild(this.canvasEl);
26503         
26504         this.setCanvasPosition();
26505     },
26506     
26507     crop : function()
26508     {
26509         if(!this.canvasLoaded){
26510             return;
26511         }
26512         
26513         var imageCanvas = document.createElement("canvas");
26514         
26515         var imageContext = imageCanvas.getContext("2d");
26516         
26517         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26518         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26519         
26520         var center = imageCanvas.width / 2;
26521         
26522         imageContext.translate(center, center);
26523         
26524         imageContext.rotate(this.rotate * Math.PI / 180);
26525         
26526         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26527         
26528         var canvas = document.createElement("canvas");
26529         
26530         var context = canvas.getContext("2d");
26531                 
26532         canvas.width = this.minWidth;
26533         canvas.height = this.minHeight;
26534
26535         switch (this.rotate) {
26536             case 0 :
26537                 
26538                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26539                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26540                 
26541                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26542                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26543                 
26544                 var targetWidth = this.minWidth - 2 * x;
26545                 var targetHeight = this.minHeight - 2 * y;
26546                 
26547                 var scale = 1;
26548                 
26549                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26550                     scale = targetWidth / width;
26551                 }
26552                 
26553                 if(x > 0 && y == 0){
26554                     scale = targetHeight / height;
26555                 }
26556                 
26557                 if(x > 0 && y > 0){
26558                     scale = targetWidth / width;
26559                     
26560                     if(width < height){
26561                         scale = targetHeight / height;
26562                     }
26563                 }
26564                 
26565                 context.scale(scale, scale);
26566                 
26567                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26568                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26569
26570                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26571                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26572
26573                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26574                 
26575                 break;
26576             case 90 : 
26577                 
26578                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26579                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26580                 
26581                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26582                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26583                 
26584                 var targetWidth = this.minWidth - 2 * x;
26585                 var targetHeight = this.minHeight - 2 * y;
26586                 
26587                 var scale = 1;
26588                 
26589                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26590                     scale = targetWidth / width;
26591                 }
26592                 
26593                 if(x > 0 && y == 0){
26594                     scale = targetHeight / height;
26595                 }
26596                 
26597                 if(x > 0 && y > 0){
26598                     scale = targetWidth / width;
26599                     
26600                     if(width < height){
26601                         scale = targetHeight / height;
26602                     }
26603                 }
26604                 
26605                 context.scale(scale, scale);
26606                 
26607                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26608                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26609
26610                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26611                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26612                 
26613                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26614                 
26615                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26616                 
26617                 break;
26618             case 180 :
26619                 
26620                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26621                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26622                 
26623                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26624                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26625                 
26626                 var targetWidth = this.minWidth - 2 * x;
26627                 var targetHeight = this.minHeight - 2 * y;
26628                 
26629                 var scale = 1;
26630                 
26631                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26632                     scale = targetWidth / width;
26633                 }
26634                 
26635                 if(x > 0 && y == 0){
26636                     scale = targetHeight / height;
26637                 }
26638                 
26639                 if(x > 0 && y > 0){
26640                     scale = targetWidth / width;
26641                     
26642                     if(width < height){
26643                         scale = targetHeight / height;
26644                     }
26645                 }
26646                 
26647                 context.scale(scale, scale);
26648                 
26649                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26650                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26651
26652                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26653                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26654
26655                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26656                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26657                 
26658                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26659                 
26660                 break;
26661             case 270 :
26662                 
26663                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26664                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26665                 
26666                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26667                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26668                 
26669                 var targetWidth = this.minWidth - 2 * x;
26670                 var targetHeight = this.minHeight - 2 * y;
26671                 
26672                 var scale = 1;
26673                 
26674                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26675                     scale = targetWidth / width;
26676                 }
26677                 
26678                 if(x > 0 && y == 0){
26679                     scale = targetHeight / height;
26680                 }
26681                 
26682                 if(x > 0 && y > 0){
26683                     scale = targetWidth / width;
26684                     
26685                     if(width < height){
26686                         scale = targetHeight / height;
26687                     }
26688                 }
26689                 
26690                 context.scale(scale, scale);
26691                 
26692                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26693                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26694
26695                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26696                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26697                 
26698                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26699                 
26700                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26701                 
26702                 break;
26703             default : 
26704                 break;
26705         }
26706         
26707         this.cropData = canvas.toDataURL(this.cropType);
26708         
26709         if(this.fireEvent('crop', this, this.cropData) !== false){
26710             this.process(this.file, this.cropData);
26711         }
26712         
26713         return;
26714         
26715     },
26716     
26717     setThumbBoxSize : function()
26718     {
26719         var width, height;
26720         
26721         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26722             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26723             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26724             
26725             this.minWidth = width;
26726             this.minHeight = height;
26727             
26728             if(this.rotate == 90 || this.rotate == 270){
26729                 this.minWidth = height;
26730                 this.minHeight = width;
26731             }
26732         }
26733         
26734         height = 300;
26735         width = Math.ceil(this.minWidth * height / this.minHeight);
26736         
26737         if(this.minWidth > this.minHeight){
26738             width = 300;
26739             height = Math.ceil(this.minHeight * width / this.minWidth);
26740         }
26741         
26742         this.thumbEl.setStyle({
26743             width : width + 'px',
26744             height : height + 'px'
26745         });
26746
26747         return;
26748             
26749     },
26750     
26751     setThumbBoxPosition : function()
26752     {
26753         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26754         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26755         
26756         this.thumbEl.setLeft(x);
26757         this.thumbEl.setTop(y);
26758         
26759     },
26760     
26761     baseRotateLevel : function()
26762     {
26763         this.baseRotate = 1;
26764         
26765         if(
26766                 typeof(this.exif) != 'undefined' &&
26767                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26768                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26769         ){
26770             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26771         }
26772         
26773         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26774         
26775     },
26776     
26777     baseScaleLevel : function()
26778     {
26779         var width, height;
26780         
26781         if(this.isDocument){
26782             
26783             if(this.baseRotate == 6 || this.baseRotate == 8){
26784             
26785                 height = this.thumbEl.getHeight();
26786                 this.baseScale = height / this.imageEl.OriginWidth;
26787
26788                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26789                     width = this.thumbEl.getWidth();
26790                     this.baseScale = width / this.imageEl.OriginHeight;
26791                 }
26792
26793                 return;
26794             }
26795
26796             height = this.thumbEl.getHeight();
26797             this.baseScale = height / this.imageEl.OriginHeight;
26798
26799             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26800                 width = this.thumbEl.getWidth();
26801                 this.baseScale = width / this.imageEl.OriginWidth;
26802             }
26803
26804             return;
26805         }
26806         
26807         if(this.baseRotate == 6 || this.baseRotate == 8){
26808             
26809             width = this.thumbEl.getHeight();
26810             this.baseScale = width / this.imageEl.OriginHeight;
26811             
26812             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26813                 height = this.thumbEl.getWidth();
26814                 this.baseScale = height / this.imageEl.OriginHeight;
26815             }
26816             
26817             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26818                 height = this.thumbEl.getWidth();
26819                 this.baseScale = height / this.imageEl.OriginHeight;
26820                 
26821                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26822                     width = this.thumbEl.getHeight();
26823                     this.baseScale = width / this.imageEl.OriginWidth;
26824                 }
26825             }
26826             
26827             return;
26828         }
26829         
26830         width = this.thumbEl.getWidth();
26831         this.baseScale = width / this.imageEl.OriginWidth;
26832         
26833         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26834             height = this.thumbEl.getHeight();
26835             this.baseScale = height / this.imageEl.OriginHeight;
26836         }
26837         
26838         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26839             
26840             height = this.thumbEl.getHeight();
26841             this.baseScale = height / this.imageEl.OriginHeight;
26842             
26843             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26844                 width = this.thumbEl.getWidth();
26845                 this.baseScale = width / this.imageEl.OriginWidth;
26846             }
26847             
26848         }
26849         
26850         return;
26851     },
26852     
26853     getScaleLevel : function()
26854     {
26855         return this.baseScale * Math.pow(1.1, this.scale);
26856     },
26857     
26858     onTouchStart : function(e)
26859     {
26860         if(!this.canvasLoaded){
26861             this.beforeSelectFile(e);
26862             return;
26863         }
26864         
26865         var touches = e.browserEvent.touches;
26866         
26867         if(!touches){
26868             return;
26869         }
26870         
26871         if(touches.length == 1){
26872             this.onMouseDown(e);
26873             return;
26874         }
26875         
26876         if(touches.length != 2){
26877             return;
26878         }
26879         
26880         var coords = [];
26881         
26882         for(var i = 0, finger; finger = touches[i]; i++){
26883             coords.push(finger.pageX, finger.pageY);
26884         }
26885         
26886         var x = Math.pow(coords[0] - coords[2], 2);
26887         var y = Math.pow(coords[1] - coords[3], 2);
26888         
26889         this.startDistance = Math.sqrt(x + y);
26890         
26891         this.startScale = this.scale;
26892         
26893         this.pinching = true;
26894         this.dragable = false;
26895         
26896     },
26897     
26898     onTouchMove : function(e)
26899     {
26900         if(!this.pinching && !this.dragable){
26901             return;
26902         }
26903         
26904         var touches = e.browserEvent.touches;
26905         
26906         if(!touches){
26907             return;
26908         }
26909         
26910         if(this.dragable){
26911             this.onMouseMove(e);
26912             return;
26913         }
26914         
26915         var coords = [];
26916         
26917         for(var i = 0, finger; finger = touches[i]; i++){
26918             coords.push(finger.pageX, finger.pageY);
26919         }
26920         
26921         var x = Math.pow(coords[0] - coords[2], 2);
26922         var y = Math.pow(coords[1] - coords[3], 2);
26923         
26924         this.endDistance = Math.sqrt(x + y);
26925         
26926         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26927         
26928         if(!this.zoomable()){
26929             this.scale = this.startScale;
26930             return;
26931         }
26932         
26933         this.draw();
26934         
26935     },
26936     
26937     onTouchEnd : function(e)
26938     {
26939         this.pinching = false;
26940         this.dragable = false;
26941         
26942     },
26943     
26944     process : function(file, crop)
26945     {
26946         if(this.loadMask){
26947             this.maskEl.mask(this.loadingText);
26948         }
26949         
26950         this.xhr = new XMLHttpRequest();
26951         
26952         file.xhr = this.xhr;
26953
26954         this.xhr.open(this.method, this.url, true);
26955         
26956         var headers = {
26957             "Accept": "application/json",
26958             "Cache-Control": "no-cache",
26959             "X-Requested-With": "XMLHttpRequest"
26960         };
26961         
26962         for (var headerName in headers) {
26963             var headerValue = headers[headerName];
26964             if (headerValue) {
26965                 this.xhr.setRequestHeader(headerName, headerValue);
26966             }
26967         }
26968         
26969         var _this = this;
26970         
26971         this.xhr.onload = function()
26972         {
26973             _this.xhrOnLoad(_this.xhr);
26974         }
26975         
26976         this.xhr.onerror = function()
26977         {
26978             _this.xhrOnError(_this.xhr);
26979         }
26980         
26981         var formData = new FormData();
26982
26983         formData.append('returnHTML', 'NO');
26984         
26985         if(crop){
26986             formData.append('crop', crop);
26987         }
26988         
26989         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26990             formData.append(this.paramName, file, file.name);
26991         }
26992         
26993         if(typeof(file.filename) != 'undefined'){
26994             formData.append('filename', file.filename);
26995         }
26996         
26997         if(typeof(file.mimetype) != 'undefined'){
26998             formData.append('mimetype', file.mimetype);
26999         }
27000         
27001         if(this.fireEvent('arrange', this, formData) != false){
27002             this.xhr.send(formData);
27003         };
27004     },
27005     
27006     xhrOnLoad : function(xhr)
27007     {
27008         if(this.loadMask){
27009             this.maskEl.unmask();
27010         }
27011         
27012         if (xhr.readyState !== 4) {
27013             this.fireEvent('exception', this, xhr);
27014             return;
27015         }
27016
27017         var response = Roo.decode(xhr.responseText);
27018         
27019         if(!response.success){
27020             this.fireEvent('exception', this, xhr);
27021             return;
27022         }
27023         
27024         var response = Roo.decode(xhr.responseText);
27025         
27026         this.fireEvent('upload', this, response);
27027         
27028     },
27029     
27030     xhrOnError : function()
27031     {
27032         if(this.loadMask){
27033             this.maskEl.unmask();
27034         }
27035         
27036         Roo.log('xhr on error');
27037         
27038         var response = Roo.decode(xhr.responseText);
27039           
27040         Roo.log(response);
27041         
27042     },
27043     
27044     prepare : function(file)
27045     {   
27046         if(this.loadMask){
27047             this.maskEl.mask(this.loadingText);
27048         }
27049         
27050         this.file = false;
27051         this.exif = {};
27052         
27053         if(typeof(file) === 'string'){
27054             this.loadCanvas(file);
27055             return;
27056         }
27057         
27058         if(!file || !this.urlAPI){
27059             return;
27060         }
27061         
27062         this.file = file;
27063         this.cropType = file.type;
27064         
27065         var _this = this;
27066         
27067         if(this.fireEvent('prepare', this, this.file) != false){
27068             
27069             var reader = new FileReader();
27070             
27071             reader.onload = function (e) {
27072                 if (e.target.error) {
27073                     Roo.log(e.target.error);
27074                     return;
27075                 }
27076                 
27077                 var buffer = e.target.result,
27078                     dataView = new DataView(buffer),
27079                     offset = 2,
27080                     maxOffset = dataView.byteLength - 4,
27081                     markerBytes,
27082                     markerLength;
27083                 
27084                 if (dataView.getUint16(0) === 0xffd8) {
27085                     while (offset < maxOffset) {
27086                         markerBytes = dataView.getUint16(offset);
27087                         
27088                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27089                             markerLength = dataView.getUint16(offset + 2) + 2;
27090                             if (offset + markerLength > dataView.byteLength) {
27091                                 Roo.log('Invalid meta data: Invalid segment size.');
27092                                 break;
27093                             }
27094                             
27095                             if(markerBytes == 0xffe1){
27096                                 _this.parseExifData(
27097                                     dataView,
27098                                     offset,
27099                                     markerLength
27100                                 );
27101                             }
27102                             
27103                             offset += markerLength;
27104                             
27105                             continue;
27106                         }
27107                         
27108                         break;
27109                     }
27110                     
27111                 }
27112                 
27113                 var url = _this.urlAPI.createObjectURL(_this.file);
27114                 
27115                 _this.loadCanvas(url);
27116                 
27117                 return;
27118             }
27119             
27120             reader.readAsArrayBuffer(this.file);
27121             
27122         }
27123         
27124     },
27125     
27126     parseExifData : function(dataView, offset, length)
27127     {
27128         var tiffOffset = offset + 10,
27129             littleEndian,
27130             dirOffset;
27131     
27132         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27133             // No Exif data, might be XMP data instead
27134             return;
27135         }
27136         
27137         // Check for the ASCII code for "Exif" (0x45786966):
27138         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27139             // No Exif data, might be XMP data instead
27140             return;
27141         }
27142         if (tiffOffset + 8 > dataView.byteLength) {
27143             Roo.log('Invalid Exif data: Invalid segment size.');
27144             return;
27145         }
27146         // Check for the two null bytes:
27147         if (dataView.getUint16(offset + 8) !== 0x0000) {
27148             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27149             return;
27150         }
27151         // Check the byte alignment:
27152         switch (dataView.getUint16(tiffOffset)) {
27153         case 0x4949:
27154             littleEndian = true;
27155             break;
27156         case 0x4D4D:
27157             littleEndian = false;
27158             break;
27159         default:
27160             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27161             return;
27162         }
27163         // Check for the TIFF tag marker (0x002A):
27164         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27165             Roo.log('Invalid Exif data: Missing TIFF marker.');
27166             return;
27167         }
27168         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27169         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27170         
27171         this.parseExifTags(
27172             dataView,
27173             tiffOffset,
27174             tiffOffset + dirOffset,
27175             littleEndian
27176         );
27177     },
27178     
27179     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27180     {
27181         var tagsNumber,
27182             dirEndOffset,
27183             i;
27184         if (dirOffset + 6 > dataView.byteLength) {
27185             Roo.log('Invalid Exif data: Invalid directory offset.');
27186             return;
27187         }
27188         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27189         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27190         if (dirEndOffset + 4 > dataView.byteLength) {
27191             Roo.log('Invalid Exif data: Invalid directory size.');
27192             return;
27193         }
27194         for (i = 0; i < tagsNumber; i += 1) {
27195             this.parseExifTag(
27196                 dataView,
27197                 tiffOffset,
27198                 dirOffset + 2 + 12 * i, // tag offset
27199                 littleEndian
27200             );
27201         }
27202         // Return the offset to the next directory:
27203         return dataView.getUint32(dirEndOffset, littleEndian);
27204     },
27205     
27206     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27207     {
27208         var tag = dataView.getUint16(offset, littleEndian);
27209         
27210         this.exif[tag] = this.getExifValue(
27211             dataView,
27212             tiffOffset,
27213             offset,
27214             dataView.getUint16(offset + 2, littleEndian), // tag type
27215             dataView.getUint32(offset + 4, littleEndian), // tag length
27216             littleEndian
27217         );
27218     },
27219     
27220     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27221     {
27222         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27223             tagSize,
27224             dataOffset,
27225             values,
27226             i,
27227             str,
27228             c;
27229     
27230         if (!tagType) {
27231             Roo.log('Invalid Exif data: Invalid tag type.');
27232             return;
27233         }
27234         
27235         tagSize = tagType.size * length;
27236         // Determine if the value is contained in the dataOffset bytes,
27237         // or if the value at the dataOffset is a pointer to the actual data:
27238         dataOffset = tagSize > 4 ?
27239                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27240         if (dataOffset + tagSize > dataView.byteLength) {
27241             Roo.log('Invalid Exif data: Invalid data offset.');
27242             return;
27243         }
27244         if (length === 1) {
27245             return tagType.getValue(dataView, dataOffset, littleEndian);
27246         }
27247         values = [];
27248         for (i = 0; i < length; i += 1) {
27249             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27250         }
27251         
27252         if (tagType.ascii) {
27253             str = '';
27254             // Concatenate the chars:
27255             for (i = 0; i < values.length; i += 1) {
27256                 c = values[i];
27257                 // Ignore the terminating NULL byte(s):
27258                 if (c === '\u0000') {
27259                     break;
27260                 }
27261                 str += c;
27262             }
27263             return str;
27264         }
27265         return values;
27266     }
27267     
27268 });
27269
27270 Roo.apply(Roo.bootstrap.UploadCropbox, {
27271     tags : {
27272         'Orientation': 0x0112
27273     },
27274     
27275     Orientation: {
27276             1: 0, //'top-left',
27277 //            2: 'top-right',
27278             3: 180, //'bottom-right',
27279 //            4: 'bottom-left',
27280 //            5: 'left-top',
27281             6: 90, //'right-top',
27282 //            7: 'right-bottom',
27283             8: 270 //'left-bottom'
27284     },
27285     
27286     exifTagTypes : {
27287         // byte, 8-bit unsigned int:
27288         1: {
27289             getValue: function (dataView, dataOffset) {
27290                 return dataView.getUint8(dataOffset);
27291             },
27292             size: 1
27293         },
27294         // ascii, 8-bit byte:
27295         2: {
27296             getValue: function (dataView, dataOffset) {
27297                 return String.fromCharCode(dataView.getUint8(dataOffset));
27298             },
27299             size: 1,
27300             ascii: true
27301         },
27302         // short, 16 bit int:
27303         3: {
27304             getValue: function (dataView, dataOffset, littleEndian) {
27305                 return dataView.getUint16(dataOffset, littleEndian);
27306             },
27307             size: 2
27308         },
27309         // long, 32 bit int:
27310         4: {
27311             getValue: function (dataView, dataOffset, littleEndian) {
27312                 return dataView.getUint32(dataOffset, littleEndian);
27313             },
27314             size: 4
27315         },
27316         // rational = two long values, first is numerator, second is denominator:
27317         5: {
27318             getValue: function (dataView, dataOffset, littleEndian) {
27319                 return dataView.getUint32(dataOffset, littleEndian) /
27320                     dataView.getUint32(dataOffset + 4, littleEndian);
27321             },
27322             size: 8
27323         },
27324         // slong, 32 bit signed int:
27325         9: {
27326             getValue: function (dataView, dataOffset, littleEndian) {
27327                 return dataView.getInt32(dataOffset, littleEndian);
27328             },
27329             size: 4
27330         },
27331         // srational, two slongs, first is numerator, second is denominator:
27332         10: {
27333             getValue: function (dataView, dataOffset, littleEndian) {
27334                 return dataView.getInt32(dataOffset, littleEndian) /
27335                     dataView.getInt32(dataOffset + 4, littleEndian);
27336             },
27337             size: 8
27338         }
27339     },
27340     
27341     footer : {
27342         STANDARD : [
27343             {
27344                 tag : 'div',
27345                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27346                 action : 'rotate-left',
27347                 cn : [
27348                     {
27349                         tag : 'button',
27350                         cls : 'btn btn-default',
27351                         html : '<i class="fa fa-undo"></i>'
27352                     }
27353                 ]
27354             },
27355             {
27356                 tag : 'div',
27357                 cls : 'btn-group roo-upload-cropbox-picture',
27358                 action : 'picture',
27359                 cn : [
27360                     {
27361                         tag : 'button',
27362                         cls : 'btn btn-default',
27363                         html : '<i class="fa fa-picture-o"></i>'
27364                     }
27365                 ]
27366             },
27367             {
27368                 tag : 'div',
27369                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27370                 action : 'rotate-right',
27371                 cn : [
27372                     {
27373                         tag : 'button',
27374                         cls : 'btn btn-default',
27375                         html : '<i class="fa fa-repeat"></i>'
27376                     }
27377                 ]
27378             }
27379         ],
27380         DOCUMENT : [
27381             {
27382                 tag : 'div',
27383                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27384                 action : 'rotate-left',
27385                 cn : [
27386                     {
27387                         tag : 'button',
27388                         cls : 'btn btn-default',
27389                         html : '<i class="fa fa-undo"></i>'
27390                     }
27391                 ]
27392             },
27393             {
27394                 tag : 'div',
27395                 cls : 'btn-group roo-upload-cropbox-download',
27396                 action : 'download',
27397                 cn : [
27398                     {
27399                         tag : 'button',
27400                         cls : 'btn btn-default',
27401                         html : '<i class="fa fa-download"></i>'
27402                     }
27403                 ]
27404             },
27405             {
27406                 tag : 'div',
27407                 cls : 'btn-group roo-upload-cropbox-crop',
27408                 action : 'crop',
27409                 cn : [
27410                     {
27411                         tag : 'button',
27412                         cls : 'btn btn-default',
27413                         html : '<i class="fa fa-crop"></i>'
27414                     }
27415                 ]
27416             },
27417             {
27418                 tag : 'div',
27419                 cls : 'btn-group roo-upload-cropbox-trash',
27420                 action : 'trash',
27421                 cn : [
27422                     {
27423                         tag : 'button',
27424                         cls : 'btn btn-default',
27425                         html : '<i class="fa fa-trash"></i>'
27426                     }
27427                 ]
27428             },
27429             {
27430                 tag : 'div',
27431                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27432                 action : 'rotate-right',
27433                 cn : [
27434                     {
27435                         tag : 'button',
27436                         cls : 'btn btn-default',
27437                         html : '<i class="fa fa-repeat"></i>'
27438                     }
27439                 ]
27440             }
27441         ],
27442         ROTATOR : [
27443             {
27444                 tag : 'div',
27445                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27446                 action : 'rotate-left',
27447                 cn : [
27448                     {
27449                         tag : 'button',
27450                         cls : 'btn btn-default',
27451                         html : '<i class="fa fa-undo"></i>'
27452                     }
27453                 ]
27454             },
27455             {
27456                 tag : 'div',
27457                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27458                 action : 'rotate-right',
27459                 cn : [
27460                     {
27461                         tag : 'button',
27462                         cls : 'btn btn-default',
27463                         html : '<i class="fa fa-repeat"></i>'
27464                     }
27465                 ]
27466             }
27467         ]
27468     }
27469 });
27470
27471 /*
27472 * Licence: LGPL
27473 */
27474
27475 /**
27476  * @class Roo.bootstrap.DocumentManager
27477  * @extends Roo.bootstrap.Component
27478  * Bootstrap DocumentManager class
27479  * @cfg {String} paramName default 'imageUpload'
27480  * @cfg {String} toolTipName default 'filename'
27481  * @cfg {String} method default POST
27482  * @cfg {String} url action url
27483  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27484  * @cfg {Boolean} multiple multiple upload default true
27485  * @cfg {Number} thumbSize default 300
27486  * @cfg {String} fieldLabel
27487  * @cfg {Number} labelWidth default 4
27488  * @cfg {String} labelAlign (left|top) default left
27489  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27490  * 
27491  * @constructor
27492  * Create a new DocumentManager
27493  * @param {Object} config The config object
27494  */
27495
27496 Roo.bootstrap.DocumentManager = function(config){
27497     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27498     
27499     this.files = [];
27500     this.delegates = [];
27501     
27502     this.addEvents({
27503         /**
27504          * @event initial
27505          * Fire when initial the DocumentManager
27506          * @param {Roo.bootstrap.DocumentManager} this
27507          */
27508         "initial" : true,
27509         /**
27510          * @event inspect
27511          * inspect selected file
27512          * @param {Roo.bootstrap.DocumentManager} this
27513          * @param {File} file
27514          */
27515         "inspect" : true,
27516         /**
27517          * @event exception
27518          * Fire when xhr load exception
27519          * @param {Roo.bootstrap.DocumentManager} this
27520          * @param {XMLHttpRequest} xhr
27521          */
27522         "exception" : true,
27523         /**
27524          * @event afterupload
27525          * Fire when xhr load exception
27526          * @param {Roo.bootstrap.DocumentManager} this
27527          * @param {XMLHttpRequest} xhr
27528          */
27529         "afterupload" : true,
27530         /**
27531          * @event prepare
27532          * prepare the form data
27533          * @param {Roo.bootstrap.DocumentManager} this
27534          * @param {Object} formData
27535          */
27536         "prepare" : true,
27537         /**
27538          * @event remove
27539          * Fire when remove the file
27540          * @param {Roo.bootstrap.DocumentManager} this
27541          * @param {Object} file
27542          */
27543         "remove" : true,
27544         /**
27545          * @event refresh
27546          * Fire after refresh the file
27547          * @param {Roo.bootstrap.DocumentManager} this
27548          */
27549         "refresh" : true,
27550         /**
27551          * @event click
27552          * Fire after click the image
27553          * @param {Roo.bootstrap.DocumentManager} this
27554          * @param {Object} file
27555          */
27556         "click" : true,
27557         /**
27558          * @event edit
27559          * Fire when upload a image and editable set to true
27560          * @param {Roo.bootstrap.DocumentManager} this
27561          * @param {Object} file
27562          */
27563         "edit" : true,
27564         /**
27565          * @event beforeselectfile
27566          * Fire before select file
27567          * @param {Roo.bootstrap.DocumentManager} this
27568          */
27569         "beforeselectfile" : true,
27570         /**
27571          * @event process
27572          * Fire before process file
27573          * @param {Roo.bootstrap.DocumentManager} this
27574          * @param {Object} file
27575          */
27576         "process" : true
27577         
27578     });
27579 };
27580
27581 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27582     
27583     boxes : 0,
27584     inputName : '',
27585     thumbSize : 300,
27586     multiple : true,
27587     files : false,
27588     method : 'POST',
27589     url : '',
27590     paramName : 'imageUpload',
27591     toolTipName : 'filename',
27592     fieldLabel : '',
27593     labelWidth : 4,
27594     labelAlign : 'left',
27595     editable : true,
27596     delegates : false,
27597     xhr : false, 
27598     
27599     getAutoCreate : function()
27600     {   
27601         var managerWidget = {
27602             tag : 'div',
27603             cls : 'roo-document-manager',
27604             cn : [
27605                 {
27606                     tag : 'input',
27607                     cls : 'roo-document-manager-selector',
27608                     type : 'file'
27609                 },
27610                 {
27611                     tag : 'div',
27612                     cls : 'roo-document-manager-uploader',
27613                     cn : [
27614                         {
27615                             tag : 'div',
27616                             cls : 'roo-document-manager-upload-btn',
27617                             html : '<i class="fa fa-plus"></i>'
27618                         }
27619                     ]
27620                     
27621                 }
27622             ]
27623         };
27624         
27625         var content = [
27626             {
27627                 tag : 'div',
27628                 cls : 'column col-md-12',
27629                 cn : managerWidget
27630             }
27631         ];
27632         
27633         if(this.fieldLabel.length){
27634             
27635             content = [
27636                 {
27637                     tag : 'div',
27638                     cls : 'column col-md-12',
27639                     html : this.fieldLabel
27640                 },
27641                 {
27642                     tag : 'div',
27643                     cls : 'column col-md-12',
27644                     cn : managerWidget
27645                 }
27646             ];
27647
27648             if(this.labelAlign == 'left'){
27649                 content = [
27650                     {
27651                         tag : 'div',
27652                         cls : 'column col-md-' + this.labelWidth,
27653                         html : this.fieldLabel
27654                     },
27655                     {
27656                         tag : 'div',
27657                         cls : 'column col-md-' + (12 - this.labelWidth),
27658                         cn : managerWidget
27659                     }
27660                 ];
27661                 
27662             }
27663         }
27664         
27665         var cfg = {
27666             tag : 'div',
27667             cls : 'row clearfix',
27668             cn : content
27669         };
27670         
27671         return cfg;
27672         
27673     },
27674     
27675     initEvents : function()
27676     {
27677         this.managerEl = this.el.select('.roo-document-manager', true).first();
27678         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27679         
27680         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27681         this.selectorEl.hide();
27682         
27683         if(this.multiple){
27684             this.selectorEl.attr('multiple', 'multiple');
27685         }
27686         
27687         this.selectorEl.on('change', this.onFileSelected, this);
27688         
27689         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27690         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27691         
27692         this.uploader.on('click', this.onUploaderClick, this);
27693         
27694         this.renderProgressDialog();
27695         
27696         var _this = this;
27697         
27698         window.addEventListener("resize", function() { _this.refresh(); } );
27699         
27700         this.fireEvent('initial', this);
27701     },
27702     
27703     renderProgressDialog : function()
27704     {
27705         var _this = this;
27706         
27707         this.progressDialog = new Roo.bootstrap.Modal({
27708             cls : 'roo-document-manager-progress-dialog',
27709             allow_close : false,
27710             title : '',
27711             buttons : [
27712                 {
27713                     name  :'cancel',
27714                     weight : 'danger',
27715                     html : 'Cancel'
27716                 }
27717             ], 
27718             listeners : { 
27719                 btnclick : function() {
27720                     _this.uploadCancel();
27721                     this.hide();
27722                 }
27723             }
27724         });
27725          
27726         this.progressDialog.render(Roo.get(document.body));
27727          
27728         this.progress = new Roo.bootstrap.Progress({
27729             cls : 'roo-document-manager-progress',
27730             active : true,
27731             striped : true
27732         });
27733         
27734         this.progress.render(this.progressDialog.getChildContainer());
27735         
27736         this.progressBar = new Roo.bootstrap.ProgressBar({
27737             cls : 'roo-document-manager-progress-bar',
27738             aria_valuenow : 0,
27739             aria_valuemin : 0,
27740             aria_valuemax : 12,
27741             panel : 'success'
27742         });
27743         
27744         this.progressBar.render(this.progress.getChildContainer());
27745     },
27746     
27747     onUploaderClick : function(e)
27748     {
27749         e.preventDefault();
27750      
27751         if(this.fireEvent('beforeselectfile', this) != false){
27752             this.selectorEl.dom.click();
27753         }
27754         
27755     },
27756     
27757     onFileSelected : function(e)
27758     {
27759         e.preventDefault();
27760         
27761         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27762             return;
27763         }
27764         
27765         Roo.each(this.selectorEl.dom.files, function(file){
27766             if(this.fireEvent('inspect', this, file) != false){
27767                 this.files.push(file);
27768             }
27769         }, this);
27770         
27771         this.queue();
27772         
27773     },
27774     
27775     queue : function()
27776     {
27777         this.selectorEl.dom.value = '';
27778         
27779         if(!this.files.length){
27780             return;
27781         }
27782         
27783         if(this.boxes > 0 && this.files.length > this.boxes){
27784             this.files = this.files.slice(0, this.boxes);
27785         }
27786         
27787         this.uploader.show();
27788         
27789         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27790             this.uploader.hide();
27791         }
27792         
27793         var _this = this;
27794         
27795         var files = [];
27796         
27797         var docs = [];
27798         
27799         Roo.each(this.files, function(file){
27800             
27801             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27802                 var f = this.renderPreview(file);
27803                 files.push(f);
27804                 return;
27805             }
27806             
27807             if(file.type.indexOf('image') != -1){
27808                 this.delegates.push(
27809                     (function(){
27810                         _this.process(file);
27811                     }).createDelegate(this)
27812                 );
27813         
27814                 return;
27815             }
27816             
27817             docs.push(
27818                 (function(){
27819                     _this.process(file);
27820                 }).createDelegate(this)
27821             );
27822             
27823         }, this);
27824         
27825         this.files = files;
27826         
27827         this.delegates = this.delegates.concat(docs);
27828         
27829         if(!this.delegates.length){
27830             this.refresh();
27831             return;
27832         }
27833         
27834         this.progressBar.aria_valuemax = this.delegates.length;
27835         
27836         this.arrange();
27837         
27838         return;
27839     },
27840     
27841     arrange : function()
27842     {
27843         if(!this.delegates.length){
27844             this.progressDialog.hide();
27845             this.refresh();
27846             return;
27847         }
27848         
27849         var delegate = this.delegates.shift();
27850         
27851         this.progressDialog.show();
27852         
27853         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27854         
27855         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27856         
27857         delegate();
27858     },
27859     
27860     refresh : function()
27861     {
27862         this.uploader.show();
27863         
27864         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27865             this.uploader.hide();
27866         }
27867         
27868         Roo.isTouch ? this.closable(false) : this.closable(true);
27869         
27870         this.fireEvent('refresh', this);
27871     },
27872     
27873     onRemove : function(e, el, o)
27874     {
27875         e.preventDefault();
27876         
27877         this.fireEvent('remove', this, o);
27878         
27879     },
27880     
27881     remove : function(o)
27882     {
27883         var files = [];
27884         
27885         Roo.each(this.files, function(file){
27886             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27887                 files.push(file);
27888                 return;
27889             }
27890
27891             o.target.remove();
27892
27893         }, this);
27894         
27895         this.files = files;
27896         
27897         this.refresh();
27898     },
27899     
27900     clear : function()
27901     {
27902         Roo.each(this.files, function(file){
27903             if(!file.target){
27904                 return;
27905             }
27906             
27907             file.target.remove();
27908
27909         }, this);
27910         
27911         this.files = [];
27912         
27913         this.refresh();
27914     },
27915     
27916     onClick : function(e, el, o)
27917     {
27918         e.preventDefault();
27919         
27920         this.fireEvent('click', this, o);
27921         
27922     },
27923     
27924     closable : function(closable)
27925     {
27926         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27927             
27928             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27929             
27930             if(closable){
27931                 el.show();
27932                 return;
27933             }
27934             
27935             el.hide();
27936             
27937         }, this);
27938     },
27939     
27940     xhrOnLoad : function(xhr)
27941     {
27942         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27943             el.remove();
27944         }, this);
27945         
27946         if (xhr.readyState !== 4) {
27947             this.arrange();
27948             this.fireEvent('exception', this, xhr);
27949             return;
27950         }
27951
27952         var response = Roo.decode(xhr.responseText);
27953         
27954         if(!response.success){
27955             this.arrange();
27956             this.fireEvent('exception', this, xhr);
27957             return;
27958         }
27959         
27960         var file = this.renderPreview(response.data);
27961         
27962         this.files.push(file);
27963         
27964         this.arrange();
27965         
27966         this.fireEvent('afterupload', this, xhr);
27967         
27968     },
27969     
27970     xhrOnError : function(xhr)
27971     {
27972         Roo.log('xhr on error');
27973         
27974         var response = Roo.decode(xhr.responseText);
27975           
27976         Roo.log(response);
27977         
27978         this.arrange();
27979     },
27980     
27981     process : function(file)
27982     {
27983         if(this.fireEvent('process', this, file) !== false){
27984             if(this.editable && file.type.indexOf('image') != -1){
27985                 this.fireEvent('edit', this, file);
27986                 return;
27987             }
27988
27989             this.uploadStart(file, false);
27990
27991             return;
27992         }
27993         
27994     },
27995     
27996     uploadStart : function(file, crop)
27997     {
27998         this.xhr = new XMLHttpRequest();
27999         
28000         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28001             this.arrange();
28002             return;
28003         }
28004         
28005         file.xhr = this.xhr;
28006             
28007         this.managerEl.createChild({
28008             tag : 'div',
28009             cls : 'roo-document-manager-loading',
28010             cn : [
28011                 {
28012                     tag : 'div',
28013                     tooltip : file.name,
28014                     cls : 'roo-document-manager-thumb',
28015                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28016                 }
28017             ]
28018
28019         });
28020
28021         this.xhr.open(this.method, this.url, true);
28022         
28023         var headers = {
28024             "Accept": "application/json",
28025             "Cache-Control": "no-cache",
28026             "X-Requested-With": "XMLHttpRequest"
28027         };
28028         
28029         for (var headerName in headers) {
28030             var headerValue = headers[headerName];
28031             if (headerValue) {
28032                 this.xhr.setRequestHeader(headerName, headerValue);
28033             }
28034         }
28035         
28036         var _this = this;
28037         
28038         this.xhr.onload = function()
28039         {
28040             _this.xhrOnLoad(_this.xhr);
28041         }
28042         
28043         this.xhr.onerror = function()
28044         {
28045             _this.xhrOnError(_this.xhr);
28046         }
28047         
28048         var formData = new FormData();
28049
28050         formData.append('returnHTML', 'NO');
28051         
28052         if(crop){
28053             formData.append('crop', crop);
28054         }
28055         
28056         formData.append(this.paramName, file, file.name);
28057         
28058         var options = {
28059             file : file, 
28060             manually : false
28061         };
28062         
28063         if(this.fireEvent('prepare', this, formData, options) != false){
28064             
28065             if(options.manually){
28066                 return;
28067             }
28068             
28069             this.xhr.send(formData);
28070             return;
28071         };
28072         
28073         this.uploadCancel();
28074     },
28075     
28076     uploadCancel : function()
28077     {
28078         if (this.xhr) {
28079             this.xhr.abort();
28080         }
28081         
28082         this.delegates = [];
28083         
28084         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28085             el.remove();
28086         }, this);
28087         
28088         this.arrange();
28089     },
28090     
28091     renderPreview : function(file)
28092     {
28093         if(typeof(file.target) != 'undefined' && file.target){
28094             return file;
28095         }
28096         
28097         var previewEl = this.managerEl.createChild({
28098             tag : 'div',
28099             cls : 'roo-document-manager-preview',
28100             cn : [
28101                 {
28102                     tag : 'div',
28103                     tooltip : file[this.toolTipName],
28104                     cls : 'roo-document-manager-thumb',
28105                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28106                 },
28107                 {
28108                     tag : 'button',
28109                     cls : 'close',
28110                     html : '<i class="fa fa-times-circle"></i>'
28111                 }
28112             ]
28113         });
28114
28115         var close = previewEl.select('button.close', true).first();
28116
28117         close.on('click', this.onRemove, this, file);
28118
28119         file.target = previewEl;
28120
28121         var image = previewEl.select('img', true).first();
28122         
28123         var _this = this;
28124         
28125         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28126         
28127         image.on('click', this.onClick, this, file);
28128         
28129         return file;
28130         
28131     },
28132     
28133     onPreviewLoad : function(file, image)
28134     {
28135         if(typeof(file.target) == 'undefined' || !file.target){
28136             return;
28137         }
28138         
28139         var width = image.dom.naturalWidth || image.dom.width;
28140         var height = image.dom.naturalHeight || image.dom.height;
28141         
28142         if(width > height){
28143             file.target.addClass('wide');
28144             return;
28145         }
28146         
28147         file.target.addClass('tall');
28148         return;
28149         
28150     },
28151     
28152     uploadFromSource : function(file, crop)
28153     {
28154         this.xhr = new XMLHttpRequest();
28155         
28156         this.managerEl.createChild({
28157             tag : 'div',
28158             cls : 'roo-document-manager-loading',
28159             cn : [
28160                 {
28161                     tag : 'div',
28162                     tooltip : file.name,
28163                     cls : 'roo-document-manager-thumb',
28164                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28165                 }
28166             ]
28167
28168         });
28169
28170         this.xhr.open(this.method, this.url, true);
28171         
28172         var headers = {
28173             "Accept": "application/json",
28174             "Cache-Control": "no-cache",
28175             "X-Requested-With": "XMLHttpRequest"
28176         };
28177         
28178         for (var headerName in headers) {
28179             var headerValue = headers[headerName];
28180             if (headerValue) {
28181                 this.xhr.setRequestHeader(headerName, headerValue);
28182             }
28183         }
28184         
28185         var _this = this;
28186         
28187         this.xhr.onload = function()
28188         {
28189             _this.xhrOnLoad(_this.xhr);
28190         }
28191         
28192         this.xhr.onerror = function()
28193         {
28194             _this.xhrOnError(_this.xhr);
28195         }
28196         
28197         var formData = new FormData();
28198
28199         formData.append('returnHTML', 'NO');
28200         
28201         formData.append('crop', crop);
28202         
28203         if(typeof(file.filename) != 'undefined'){
28204             formData.append('filename', file.filename);
28205         }
28206         
28207         if(typeof(file.mimetype) != 'undefined'){
28208             formData.append('mimetype', file.mimetype);
28209         }
28210         
28211         if(this.fireEvent('prepare', this, formData) != false){
28212             this.xhr.send(formData);
28213         };
28214     }
28215 });
28216
28217 /*
28218 * Licence: LGPL
28219 */
28220
28221 /**
28222  * @class Roo.bootstrap.DocumentViewer
28223  * @extends Roo.bootstrap.Component
28224  * Bootstrap DocumentViewer class
28225  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28226  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28227  * 
28228  * @constructor
28229  * Create a new DocumentViewer
28230  * @param {Object} config The config object
28231  */
28232
28233 Roo.bootstrap.DocumentViewer = function(config){
28234     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28235     
28236     this.addEvents({
28237         /**
28238          * @event initial
28239          * Fire after initEvent
28240          * @param {Roo.bootstrap.DocumentViewer} this
28241          */
28242         "initial" : true,
28243         /**
28244          * @event click
28245          * Fire after click
28246          * @param {Roo.bootstrap.DocumentViewer} this
28247          */
28248         "click" : true,
28249         /**
28250          * @event download
28251          * Fire after download button
28252          * @param {Roo.bootstrap.DocumentViewer} this
28253          */
28254         "download" : true,
28255         /**
28256          * @event trash
28257          * Fire after trash button
28258          * @param {Roo.bootstrap.DocumentViewer} this
28259          */
28260         "trash" : true
28261         
28262     });
28263 };
28264
28265 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28266     
28267     showDownload : true,
28268     
28269     showTrash : true,
28270     
28271     getAutoCreate : function()
28272     {
28273         var cfg = {
28274             tag : 'div',
28275             cls : 'roo-document-viewer',
28276             cn : [
28277                 {
28278                     tag : 'div',
28279                     cls : 'roo-document-viewer-body',
28280                     cn : [
28281                         {
28282                             tag : 'div',
28283                             cls : 'roo-document-viewer-thumb',
28284                             cn : [
28285                                 {
28286                                     tag : 'img',
28287                                     cls : 'roo-document-viewer-image'
28288                                 }
28289                             ]
28290                         }
28291                     ]
28292                 },
28293                 {
28294                     tag : 'div',
28295                     cls : 'roo-document-viewer-footer',
28296                     cn : {
28297                         tag : 'div',
28298                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28299                         cn : [
28300                             {
28301                                 tag : 'div',
28302                                 cls : 'btn-group roo-document-viewer-download',
28303                                 cn : [
28304                                     {
28305                                         tag : 'button',
28306                                         cls : 'btn btn-default',
28307                                         html : '<i class="fa fa-download"></i>'
28308                                     }
28309                                 ]
28310                             },
28311                             {
28312                                 tag : 'div',
28313                                 cls : 'btn-group roo-document-viewer-trash',
28314                                 cn : [
28315                                     {
28316                                         tag : 'button',
28317                                         cls : 'btn btn-default',
28318                                         html : '<i class="fa fa-trash"></i>'
28319                                     }
28320                                 ]
28321                             }
28322                         ]
28323                     }
28324                 }
28325             ]
28326         };
28327         
28328         return cfg;
28329     },
28330     
28331     initEvents : function()
28332     {
28333         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28334         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28335         
28336         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28337         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28338         
28339         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28340         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28341         
28342         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28343         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28344         
28345         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28346         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28347         
28348         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28349         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28350         
28351         this.bodyEl.on('click', this.onClick, this);
28352         this.downloadBtn.on('click', this.onDownload, this);
28353         this.trashBtn.on('click', this.onTrash, this);
28354         
28355         this.downloadBtn.hide();
28356         this.trashBtn.hide();
28357         
28358         if(this.showDownload){
28359             this.downloadBtn.show();
28360         }
28361         
28362         if(this.showTrash){
28363             this.trashBtn.show();
28364         }
28365         
28366         if(!this.showDownload && !this.showTrash) {
28367             this.footerEl.hide();
28368         }
28369         
28370     },
28371     
28372     initial : function()
28373     {
28374         this.fireEvent('initial', this);
28375         
28376     },
28377     
28378     onClick : function(e)
28379     {
28380         e.preventDefault();
28381         
28382         this.fireEvent('click', this);
28383     },
28384     
28385     onDownload : function(e)
28386     {
28387         e.preventDefault();
28388         
28389         this.fireEvent('download', this);
28390     },
28391     
28392     onTrash : function(e)
28393     {
28394         e.preventDefault();
28395         
28396         this.fireEvent('trash', this);
28397     }
28398     
28399 });
28400 /*
28401  * - LGPL
28402  *
28403  * nav progress bar
28404  * 
28405  */
28406
28407 /**
28408  * @class Roo.bootstrap.NavProgressBar
28409  * @extends Roo.bootstrap.Component
28410  * Bootstrap NavProgressBar class
28411  * 
28412  * @constructor
28413  * Create a new nav progress bar
28414  * @param {Object} config The config object
28415  */
28416
28417 Roo.bootstrap.NavProgressBar = function(config){
28418     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28419
28420     this.bullets = this.bullets || [];
28421    
28422 //    Roo.bootstrap.NavProgressBar.register(this);
28423      this.addEvents({
28424         /**
28425              * @event changed
28426              * Fires when the active item changes
28427              * @param {Roo.bootstrap.NavProgressBar} this
28428              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28429              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28430          */
28431         'changed': true
28432      });
28433     
28434 };
28435
28436 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28437     
28438     bullets : [],
28439     barItems : [],
28440     
28441     getAutoCreate : function()
28442     {
28443         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28444         
28445         cfg = {
28446             tag : 'div',
28447             cls : 'roo-navigation-bar-group',
28448             cn : [
28449                 {
28450                     tag : 'div',
28451                     cls : 'roo-navigation-top-bar'
28452                 },
28453                 {
28454                     tag : 'div',
28455                     cls : 'roo-navigation-bullets-bar',
28456                     cn : [
28457                         {
28458                             tag : 'ul',
28459                             cls : 'roo-navigation-bar'
28460                         }
28461                     ]
28462                 },
28463                 
28464                 {
28465                     tag : 'div',
28466                     cls : 'roo-navigation-bottom-bar'
28467                 }
28468             ]
28469             
28470         };
28471         
28472         return cfg;
28473         
28474     },
28475     
28476     initEvents: function() 
28477     {
28478         
28479     },
28480     
28481     onRender : function(ct, position) 
28482     {
28483         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28484         
28485         if(this.bullets.length){
28486             Roo.each(this.bullets, function(b){
28487                this.addItem(b);
28488             }, this);
28489         }
28490         
28491         this.format();
28492         
28493     },
28494     
28495     addItem : function(cfg)
28496     {
28497         var item = new Roo.bootstrap.NavProgressItem(cfg);
28498         
28499         item.parentId = this.id;
28500         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28501         
28502         if(cfg.html){
28503             var top = new Roo.bootstrap.Element({
28504                 tag : 'div',
28505                 cls : 'roo-navigation-bar-text'
28506             });
28507             
28508             var bottom = new Roo.bootstrap.Element({
28509                 tag : 'div',
28510                 cls : 'roo-navigation-bar-text'
28511             });
28512             
28513             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28514             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28515             
28516             var topText = new Roo.bootstrap.Element({
28517                 tag : 'span',
28518                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28519             });
28520             
28521             var bottomText = new Roo.bootstrap.Element({
28522                 tag : 'span',
28523                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28524             });
28525             
28526             topText.onRender(top.el, null);
28527             bottomText.onRender(bottom.el, null);
28528             
28529             item.topEl = top;
28530             item.bottomEl = bottom;
28531         }
28532         
28533         this.barItems.push(item);
28534         
28535         return item;
28536     },
28537     
28538     getActive : function()
28539     {
28540         var active = false;
28541         
28542         Roo.each(this.barItems, function(v){
28543             
28544             if (!v.isActive()) {
28545                 return;
28546             }
28547             
28548             active = v;
28549             return false;
28550             
28551         });
28552         
28553         return active;
28554     },
28555     
28556     setActiveItem : function(item)
28557     {
28558         var prev = false;
28559         
28560         Roo.each(this.barItems, function(v){
28561             if (v.rid == item.rid) {
28562                 return ;
28563             }
28564             
28565             if (v.isActive()) {
28566                 v.setActive(false);
28567                 prev = v;
28568             }
28569         });
28570
28571         item.setActive(true);
28572         
28573         this.fireEvent('changed', this, item, prev);
28574     },
28575     
28576     getBarItem: function(rid)
28577     {
28578         var ret = false;
28579         
28580         Roo.each(this.barItems, function(e) {
28581             if (e.rid != rid) {
28582                 return;
28583             }
28584             
28585             ret =  e;
28586             return false;
28587         });
28588         
28589         return ret;
28590     },
28591     
28592     indexOfItem : function(item)
28593     {
28594         var index = false;
28595         
28596         Roo.each(this.barItems, function(v, i){
28597             
28598             if (v.rid != item.rid) {
28599                 return;
28600             }
28601             
28602             index = i;
28603             return false
28604         });
28605         
28606         return index;
28607     },
28608     
28609     setActiveNext : function()
28610     {
28611         var i = this.indexOfItem(this.getActive());
28612         
28613         if (i > this.barItems.length) {
28614             return;
28615         }
28616         
28617         this.setActiveItem(this.barItems[i+1]);
28618     },
28619     
28620     setActivePrev : function()
28621     {
28622         var i = this.indexOfItem(this.getActive());
28623         
28624         if (i  < 1) {
28625             return;
28626         }
28627         
28628         this.setActiveItem(this.barItems[i-1]);
28629     },
28630     
28631     format : function()
28632     {
28633         if(!this.barItems.length){
28634             return;
28635         }
28636      
28637         var width = 100 / this.barItems.length;
28638         
28639         Roo.each(this.barItems, function(i){
28640             i.el.setStyle('width', width + '%');
28641             i.topEl.el.setStyle('width', width + '%');
28642             i.bottomEl.el.setStyle('width', width + '%');
28643         }, this);
28644         
28645     }
28646     
28647 });
28648 /*
28649  * - LGPL
28650  *
28651  * Nav Progress Item
28652  * 
28653  */
28654
28655 /**
28656  * @class Roo.bootstrap.NavProgressItem
28657  * @extends Roo.bootstrap.Component
28658  * Bootstrap NavProgressItem class
28659  * @cfg {String} rid the reference id
28660  * @cfg {Boolean} active (true|false) Is item active default false
28661  * @cfg {Boolean} disabled (true|false) Is item active default false
28662  * @cfg {String} html
28663  * @cfg {String} position (top|bottom) text position default bottom
28664  * @cfg {String} icon show icon instead of number
28665  * 
28666  * @constructor
28667  * Create a new NavProgressItem
28668  * @param {Object} config The config object
28669  */
28670 Roo.bootstrap.NavProgressItem = function(config){
28671     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28672     this.addEvents({
28673         // raw events
28674         /**
28675          * @event click
28676          * The raw click event for the entire grid.
28677          * @param {Roo.bootstrap.NavProgressItem} this
28678          * @param {Roo.EventObject} e
28679          */
28680         "click" : true
28681     });
28682    
28683 };
28684
28685 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28686     
28687     rid : '',
28688     active : false,
28689     disabled : false,
28690     html : '',
28691     position : 'bottom',
28692     icon : false,
28693     
28694     getAutoCreate : function()
28695     {
28696         var iconCls = 'roo-navigation-bar-item-icon';
28697         
28698         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28699         
28700         var cfg = {
28701             tag: 'li',
28702             cls: 'roo-navigation-bar-item',
28703             cn : [
28704                 {
28705                     tag : 'i',
28706                     cls : iconCls
28707                 }
28708             ]
28709         };
28710         
28711         if(this.active){
28712             cfg.cls += ' active';
28713         }
28714         if(this.disabled){
28715             cfg.cls += ' disabled';
28716         }
28717         
28718         return cfg;
28719     },
28720     
28721     disable : function()
28722     {
28723         this.setDisabled(true);
28724     },
28725     
28726     enable : function()
28727     {
28728         this.setDisabled(false);
28729     },
28730     
28731     initEvents: function() 
28732     {
28733         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28734         
28735         this.iconEl.on('click', this.onClick, this);
28736     },
28737     
28738     onClick : function(e)
28739     {
28740         e.preventDefault();
28741         
28742         if(this.disabled){
28743             return;
28744         }
28745         
28746         if(this.fireEvent('click', this, e) === false){
28747             return;
28748         };
28749         
28750         this.parent().setActiveItem(this);
28751     },
28752     
28753     isActive: function () 
28754     {
28755         return this.active;
28756     },
28757     
28758     setActive : function(state)
28759     {
28760         if(this.active == state){
28761             return;
28762         }
28763         
28764         this.active = state;
28765         
28766         if (state) {
28767             this.el.addClass('active');
28768             return;
28769         }
28770         
28771         this.el.removeClass('active');
28772         
28773         return;
28774     },
28775     
28776     setDisabled : function(state)
28777     {
28778         if(this.disabled == state){
28779             return;
28780         }
28781         
28782         this.disabled = state;
28783         
28784         if (state) {
28785             this.el.addClass('disabled');
28786             return;
28787         }
28788         
28789         this.el.removeClass('disabled');
28790     },
28791     
28792     tooltipEl : function()
28793     {
28794         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28795     }
28796 });
28797  
28798
28799  /*
28800  * - LGPL
28801  *
28802  * FieldLabel
28803  * 
28804  */
28805
28806 /**
28807  * @class Roo.bootstrap.FieldLabel
28808  * @extends Roo.bootstrap.Component
28809  * Bootstrap FieldLabel class
28810  * @cfg {String} html contents of the element
28811  * @cfg {String} tag tag of the element default label
28812  * @cfg {String} cls class of the element
28813  * @cfg {String} target label target 
28814  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28815  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28816  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28817  * @cfg {String} iconTooltip default "This field is required"
28818  * 
28819  * @constructor
28820  * Create a new FieldLabel
28821  * @param {Object} config The config object
28822  */
28823
28824 Roo.bootstrap.FieldLabel = function(config){
28825     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28826     
28827     this.addEvents({
28828             /**
28829              * @event invalid
28830              * Fires after the field has been marked as invalid.
28831              * @param {Roo.form.FieldLabel} this
28832              * @param {String} msg The validation message
28833              */
28834             invalid : true,
28835             /**
28836              * @event valid
28837              * Fires after the field has been validated with no errors.
28838              * @param {Roo.form.FieldLabel} this
28839              */
28840             valid : true
28841         });
28842 };
28843
28844 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28845     
28846     tag: 'label',
28847     cls: '',
28848     html: '',
28849     target: '',
28850     allowBlank : true,
28851     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28852     validClass : 'text-success fa fa-lg fa-check',
28853     iconTooltip : 'This field is required',
28854     
28855     getAutoCreate : function(){
28856         
28857         var cfg = {
28858             tag : this.tag,
28859             cls : 'roo-bootstrap-field-label ' + this.cls,
28860             for : this.target,
28861             cn : [
28862                 {
28863                     tag : 'i',
28864                     cls : '',
28865                     tooltip : this.iconTooltip
28866                 },
28867                 {
28868                     tag : 'span',
28869                     html : this.html
28870                 }
28871             ] 
28872         };
28873         
28874         return cfg;
28875     },
28876     
28877     initEvents: function() 
28878     {
28879         Roo.bootstrap.Element.superclass.initEvents.call(this);
28880         
28881         this.iconEl = this.el.select('i', true).first();
28882         
28883         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28884         
28885         Roo.bootstrap.FieldLabel.register(this);
28886     },
28887     
28888     /**
28889      * Mark this field as valid
28890      */
28891     markValid : function()
28892     {
28893         this.iconEl.show();
28894         
28895         this.iconEl.removeClass(this.invalidClass);
28896         
28897         this.iconEl.addClass(this.validClass);
28898         
28899         this.fireEvent('valid', this);
28900     },
28901     
28902     /**
28903      * Mark this field as invalid
28904      * @param {String} msg The validation message
28905      */
28906     markInvalid : function(msg)
28907     {
28908         this.iconEl.show();
28909         
28910         this.iconEl.removeClass(this.validClass);
28911         
28912         this.iconEl.addClass(this.invalidClass);
28913         
28914         this.fireEvent('invalid', this, msg);
28915     }
28916     
28917    
28918 });
28919
28920 Roo.apply(Roo.bootstrap.FieldLabel, {
28921     
28922     groups: {},
28923     
28924      /**
28925     * register a FieldLabel Group
28926     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28927     */
28928     register : function(label)
28929     {
28930         if(this.groups.hasOwnProperty(label.target)){
28931             return;
28932         }
28933      
28934         this.groups[label.target] = label;
28935         
28936     },
28937     /**
28938     * fetch a FieldLabel Group based on the target
28939     * @param {string} target
28940     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28941     */
28942     get: function(target) {
28943         if (typeof(this.groups[target]) == 'undefined') {
28944             return false;
28945         }
28946         
28947         return this.groups[target] ;
28948     }
28949 });
28950
28951  
28952
28953  /*
28954  * - LGPL
28955  *
28956  * page DateSplitField.
28957  * 
28958  */
28959
28960
28961 /**
28962  * @class Roo.bootstrap.DateSplitField
28963  * @extends Roo.bootstrap.Component
28964  * Bootstrap DateSplitField class
28965  * @cfg {string} fieldLabel - the label associated
28966  * @cfg {Number} labelWidth set the width of label (0-12)
28967  * @cfg {String} labelAlign (top|left)
28968  * @cfg {Boolean} dayAllowBlank (true|false) default false
28969  * @cfg {Boolean} monthAllowBlank (true|false) default false
28970  * @cfg {Boolean} yearAllowBlank (true|false) default false
28971  * @cfg {string} dayPlaceholder 
28972  * @cfg {string} monthPlaceholder
28973  * @cfg {string} yearPlaceholder
28974  * @cfg {string} dayFormat default 'd'
28975  * @cfg {string} monthFormat default 'm'
28976  * @cfg {string} yearFormat default 'Y'
28977
28978  *     
28979  * @constructor
28980  * Create a new DateSplitField
28981  * @param {Object} config The config object
28982  */
28983
28984 Roo.bootstrap.DateSplitField = function(config){
28985     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28986     
28987     this.addEvents({
28988         // raw events
28989          /**
28990          * @event years
28991          * getting the data of years
28992          * @param {Roo.bootstrap.DateSplitField} this
28993          * @param {Object} years
28994          */
28995         "years" : true,
28996         /**
28997          * @event days
28998          * getting the data of days
28999          * @param {Roo.bootstrap.DateSplitField} this
29000          * @param {Object} days
29001          */
29002         "days" : true,
29003         /**
29004          * @event invalid
29005          * Fires after the field has been marked as invalid.
29006          * @param {Roo.form.Field} this
29007          * @param {String} msg The validation message
29008          */
29009         invalid : true,
29010        /**
29011          * @event valid
29012          * Fires after the field has been validated with no errors.
29013          * @param {Roo.form.Field} this
29014          */
29015         valid : true
29016     });
29017 };
29018
29019 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29020     
29021     fieldLabel : '',
29022     labelAlign : 'top',
29023     labelWidth : 3,
29024     dayAllowBlank : false,
29025     monthAllowBlank : false,
29026     yearAllowBlank : false,
29027     dayPlaceholder : '',
29028     monthPlaceholder : '',
29029     yearPlaceholder : '',
29030     dayFormat : 'd',
29031     monthFormat : 'm',
29032     yearFormat : 'Y',
29033     isFormField : true,
29034     
29035     getAutoCreate : function()
29036     {
29037         var cfg = {
29038             tag : 'div',
29039             cls : 'row roo-date-split-field-group',
29040             cn : [
29041                 {
29042                     tag : 'input',
29043                     type : 'hidden',
29044                     cls : 'form-hidden-field roo-date-split-field-group-value',
29045                     name : this.name
29046                 }
29047             ]
29048         };
29049         
29050         if(this.fieldLabel){
29051             cfg.cn.push({
29052                 tag : 'div',
29053                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29054                 cn : [
29055                     {
29056                         tag : 'label',
29057                         html : this.fieldLabel
29058                     }
29059                 ]
29060             });
29061         }
29062         
29063         Roo.each(['day', 'month', 'year'], function(t){
29064             cfg.cn.push({
29065                 tag : 'div',
29066                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29067             });
29068         }, this);
29069         
29070         return cfg;
29071     },
29072     
29073     inputEl: function ()
29074     {
29075         return this.el.select('.roo-date-split-field-group-value', true).first();
29076     },
29077     
29078     onRender : function(ct, position) 
29079     {
29080         var _this = this;
29081         
29082         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29083         
29084         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29085         
29086         this.dayField = new Roo.bootstrap.ComboBox({
29087             allowBlank : this.dayAllowBlank,
29088             alwaysQuery : true,
29089             displayField : 'value',
29090             editable : false,
29091             fieldLabel : '',
29092             forceSelection : true,
29093             mode : 'local',
29094             placeholder : this.dayPlaceholder,
29095             selectOnFocus : true,
29096             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29097             triggerAction : 'all',
29098             typeAhead : true,
29099             valueField : 'value',
29100             store : new Roo.data.SimpleStore({
29101                 data : (function() {    
29102                     var days = [];
29103                     _this.fireEvent('days', _this, days);
29104                     return days;
29105                 })(),
29106                 fields : [ 'value' ]
29107             }),
29108             listeners : {
29109                 select : function (_self, record, index)
29110                 {
29111                     _this.setValue(_this.getValue());
29112                 }
29113             }
29114         });
29115
29116         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29117         
29118         this.monthField = new Roo.bootstrap.MonthField({
29119             after : '<i class=\"fa fa-calendar\"></i>',
29120             allowBlank : this.monthAllowBlank,
29121             placeholder : this.monthPlaceholder,
29122             readOnly : true,
29123             listeners : {
29124                 render : function (_self)
29125                 {
29126                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29127                         e.preventDefault();
29128                         _self.focus();
29129                     });
29130                 },
29131                 select : function (_self, oldvalue, newvalue)
29132                 {
29133                     _this.setValue(_this.getValue());
29134                 }
29135             }
29136         });
29137         
29138         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29139         
29140         this.yearField = new Roo.bootstrap.ComboBox({
29141             allowBlank : this.yearAllowBlank,
29142             alwaysQuery : true,
29143             displayField : 'value',
29144             editable : false,
29145             fieldLabel : '',
29146             forceSelection : true,
29147             mode : 'local',
29148             placeholder : this.yearPlaceholder,
29149             selectOnFocus : true,
29150             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29151             triggerAction : 'all',
29152             typeAhead : true,
29153             valueField : 'value',
29154             store : new Roo.data.SimpleStore({
29155                 data : (function() {
29156                     var years = [];
29157                     _this.fireEvent('years', _this, years);
29158                     return years;
29159                 })(),
29160                 fields : [ 'value' ]
29161             }),
29162             listeners : {
29163                 select : function (_self, record, index)
29164                 {
29165                     _this.setValue(_this.getValue());
29166                 }
29167             }
29168         });
29169
29170         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29171     },
29172     
29173     setValue : function(v, format)
29174     {
29175         this.inputEl.dom.value = v;
29176         
29177         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29178         
29179         var d = Date.parseDate(v, f);
29180         
29181         if(!d){
29182             this.validate();
29183             return;
29184         }
29185         
29186         this.setDay(d.format(this.dayFormat));
29187         this.setMonth(d.format(this.monthFormat));
29188         this.setYear(d.format(this.yearFormat));
29189         
29190         this.validate();
29191         
29192         return;
29193     },
29194     
29195     setDay : function(v)
29196     {
29197         this.dayField.setValue(v);
29198         this.inputEl.dom.value = this.getValue();
29199         this.validate();
29200         return;
29201     },
29202     
29203     setMonth : function(v)
29204     {
29205         this.monthField.setValue(v, true);
29206         this.inputEl.dom.value = this.getValue();
29207         this.validate();
29208         return;
29209     },
29210     
29211     setYear : function(v)
29212     {
29213         this.yearField.setValue(v);
29214         this.inputEl.dom.value = this.getValue();
29215         this.validate();
29216         return;
29217     },
29218     
29219     getDay : function()
29220     {
29221         return this.dayField.getValue();
29222     },
29223     
29224     getMonth : function()
29225     {
29226         return this.monthField.getValue();
29227     },
29228     
29229     getYear : function()
29230     {
29231         return this.yearField.getValue();
29232     },
29233     
29234     getValue : function()
29235     {
29236         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29237         
29238         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29239         
29240         return date;
29241     },
29242     
29243     reset : function()
29244     {
29245         this.setDay('');
29246         this.setMonth('');
29247         this.setYear('');
29248         this.inputEl.dom.value = '';
29249         this.validate();
29250         return;
29251     },
29252     
29253     validate : function()
29254     {
29255         var d = this.dayField.validate();
29256         var m = this.monthField.validate();
29257         var y = this.yearField.validate();
29258         
29259         var valid = true;
29260         
29261         if(
29262                 (!this.dayAllowBlank && !d) ||
29263                 (!this.monthAllowBlank && !m) ||
29264                 (!this.yearAllowBlank && !y)
29265         ){
29266             valid = false;
29267         }
29268         
29269         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29270             return valid;
29271         }
29272         
29273         if(valid){
29274             this.markValid();
29275             return valid;
29276         }
29277         
29278         this.markInvalid();
29279         
29280         return valid;
29281     },
29282     
29283     markValid : function()
29284     {
29285         
29286         var label = this.el.select('label', true).first();
29287         var icon = this.el.select('i.fa-star', true).first();
29288
29289         if(label && icon){
29290             icon.remove();
29291         }
29292         
29293         this.fireEvent('valid', this);
29294     },
29295     
29296      /**
29297      * Mark this field as invalid
29298      * @param {String} msg The validation message
29299      */
29300     markInvalid : function(msg)
29301     {
29302         
29303         var label = this.el.select('label', true).first();
29304         var icon = this.el.select('i.fa-star', true).first();
29305
29306         if(label && !icon){
29307             this.el.select('.roo-date-split-field-label', true).createChild({
29308                 tag : 'i',
29309                 cls : 'text-danger fa fa-lg fa-star',
29310                 tooltip : 'This field is required',
29311                 style : 'margin-right:5px;'
29312             }, label, true);
29313         }
29314         
29315         this.fireEvent('invalid', this, msg);
29316     },
29317     
29318     clearInvalid : function()
29319     {
29320         var label = this.el.select('label', true).first();
29321         var icon = this.el.select('i.fa-star', true).first();
29322
29323         if(label && icon){
29324             icon.remove();
29325         }
29326         
29327         this.fireEvent('valid', this);
29328     },
29329     
29330     getName: function()
29331     {
29332         return this.name;
29333     }
29334     
29335 });
29336
29337  /**
29338  *
29339  * This is based on 
29340  * http://masonry.desandro.com
29341  *
29342  * The idea is to render all the bricks based on vertical width...
29343  *
29344  * The original code extends 'outlayer' - we might need to use that....
29345  * 
29346  */
29347
29348
29349 /**
29350  * @class Roo.bootstrap.LayoutMasonry
29351  * @extends Roo.bootstrap.Component
29352  * Bootstrap Layout Masonry class
29353  * 
29354  * @constructor
29355  * Create a new Element
29356  * @param {Object} config The config object
29357  */
29358
29359 Roo.bootstrap.LayoutMasonry = function(config){
29360     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29361     
29362     this.bricks = [];
29363     
29364 };
29365
29366 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29367     
29368     /**
29369      * @cfg {Boolean} isLayoutInstant = no animation?
29370      */   
29371     isLayoutInstant : false, // needed?
29372    
29373     /**
29374      * @cfg {Number} boxWidth  width of the columns
29375      */   
29376     boxWidth : 450,
29377     
29378       /**
29379      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29380      */   
29381     boxHeight : 0,
29382     
29383     /**
29384      * @cfg {Number} padWidth padding below box..
29385      */   
29386     padWidth : 10, 
29387     
29388     /**
29389      * @cfg {Number} gutter gutter width..
29390      */   
29391     gutter : 10,
29392     
29393      /**
29394      * @cfg {Number} maxCols maximum number of columns
29395      */   
29396     
29397     maxCols: 0,
29398     
29399     /**
29400      * @cfg {Boolean} isAutoInitial defalut true
29401      */   
29402     isAutoInitial : true, 
29403     
29404     containerWidth: 0,
29405     
29406     /**
29407      * @cfg {Boolean} isHorizontal defalut false
29408      */   
29409     isHorizontal : false, 
29410
29411     currentSize : null,
29412     
29413     tag: 'div',
29414     
29415     cls: '',
29416     
29417     bricks: null, //CompositeElement
29418     
29419     cols : 1,
29420     
29421     _isLayoutInited : false,
29422     
29423 //    isAlternative : false, // only use for vertical layout...
29424     
29425     /**
29426      * @cfg {Number} alternativePadWidth padding below box..
29427      */   
29428     alternativePadWidth : 50, 
29429     
29430     getAutoCreate : function(){
29431         
29432         var cfg = {
29433             tag: this.tag,
29434             cls: 'blog-masonary-wrapper ' + this.cls,
29435             cn : {
29436                 cls : 'mas-boxes masonary'
29437             }
29438         };
29439         
29440         return cfg;
29441     },
29442     
29443     getChildContainer: function( )
29444     {
29445         if (this.boxesEl) {
29446             return this.boxesEl;
29447         }
29448         
29449         this.boxesEl = this.el.select('.mas-boxes').first();
29450         
29451         return this.boxesEl;
29452     },
29453     
29454     
29455     initEvents : function()
29456     {
29457         var _this = this;
29458         
29459         if(this.isAutoInitial){
29460             Roo.log('hook children rendered');
29461             this.on('childrenrendered', function() {
29462                 Roo.log('children rendered');
29463                 _this.initial();
29464             } ,this);
29465         }
29466     },
29467     
29468     initial : function()
29469     {
29470         this.currentSize = this.el.getBox(true);
29471         
29472         Roo.EventManager.onWindowResize(this.resize, this); 
29473
29474         if(!this.isAutoInitial){
29475             this.layout();
29476             return;
29477         }
29478         
29479         this.layout();
29480         
29481         return;
29482         //this.layout.defer(500,this);
29483         
29484     },
29485     
29486     resize : function()
29487     {
29488         Roo.log('resize');
29489         
29490         var cs = this.el.getBox(true);
29491         
29492         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29493             Roo.log("no change in with or X");
29494             return;
29495         }
29496         
29497         this.currentSize = cs;
29498         
29499         this.layout();
29500         
29501     },
29502     
29503     layout : function()
29504     {   
29505         this._resetLayout();
29506         
29507         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29508         
29509         this.layoutItems( isInstant );
29510       
29511         this._isLayoutInited = true;
29512         
29513     },
29514     
29515     _resetLayout : function()
29516     {
29517         if(this.isHorizontal){
29518             this.horizontalMeasureColumns();
29519             return;
29520         }
29521         
29522         this.verticalMeasureColumns();
29523         
29524     },
29525     
29526     verticalMeasureColumns : function()
29527     {
29528         this.getContainerWidth();
29529         
29530 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29531 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29532 //            return;
29533 //        }
29534         
29535         var boxWidth = this.boxWidth + this.padWidth;
29536         
29537         if(this.containerWidth < this.boxWidth){
29538             boxWidth = this.containerWidth
29539         }
29540         
29541         var containerWidth = this.containerWidth;
29542         
29543         var cols = Math.floor(containerWidth / boxWidth);
29544         
29545         this.cols = Math.max( cols, 1 );
29546         
29547         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29548         
29549         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29550         
29551         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29552         
29553         this.colWidth = boxWidth + avail - this.padWidth;
29554         
29555         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29556         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29557     },
29558     
29559     horizontalMeasureColumns : function()
29560     {
29561         this.getContainerWidth();
29562         
29563         var boxWidth = this.boxWidth;
29564         
29565         if(this.containerWidth < boxWidth){
29566             boxWidth = this.containerWidth;
29567         }
29568         
29569         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29570         
29571         this.el.setHeight(boxWidth);
29572         
29573     },
29574     
29575     getContainerWidth : function()
29576     {
29577         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29578     },
29579     
29580     layoutItems : function( isInstant )
29581     {
29582         var items = Roo.apply([], this.bricks);
29583         
29584         if(this.isHorizontal){
29585             this._horizontalLayoutItems( items , isInstant );
29586             return;
29587         }
29588         
29589 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29590 //            this._verticalAlternativeLayoutItems( items , isInstant );
29591 //            return;
29592 //        }
29593         
29594         this._verticalLayoutItems( items , isInstant );
29595         
29596     },
29597     
29598     _verticalLayoutItems : function ( items , isInstant)
29599     {
29600         if ( !items || !items.length ) {
29601             return;
29602         }
29603         
29604         var standard = [
29605             ['xs', 'xs', 'xs', 'tall'],
29606             ['xs', 'xs', 'tall'],
29607             ['xs', 'xs', 'sm'],
29608             ['xs', 'xs', 'xs'],
29609             ['xs', 'tall'],
29610             ['xs', 'sm'],
29611             ['xs', 'xs'],
29612             ['xs'],
29613             
29614             ['sm', 'xs', 'xs'],
29615             ['sm', 'xs'],
29616             ['sm'],
29617             
29618             ['tall', 'xs', 'xs', 'xs'],
29619             ['tall', 'xs', 'xs'],
29620             ['tall', 'xs'],
29621             ['tall']
29622             
29623         ];
29624         
29625         var queue = [];
29626         
29627         var boxes = [];
29628         
29629         var box = [];
29630         
29631         Roo.each(items, function(item, k){
29632             
29633             switch (item.size) {
29634                 // these layouts take up a full box,
29635                 case 'md' :
29636                 case 'md-left' :
29637                 case 'md-right' :
29638                 case 'wide' :
29639                     
29640                     if(box.length){
29641                         boxes.push(box);
29642                         box = [];
29643                     }
29644                     
29645                     boxes.push([item]);
29646                     
29647                     break;
29648                     
29649                 case 'xs' :
29650                 case 'sm' :
29651                 case 'tall' :
29652                     
29653                     box.push(item);
29654                     
29655                     break;
29656                 default :
29657                     break;
29658                     
29659             }
29660             
29661         }, this);
29662         
29663         if(box.length){
29664             boxes.push(box);
29665             box = [];
29666         }
29667         
29668         var filterPattern = function(box, length)
29669         {
29670             if(!box.length){
29671                 return;
29672             }
29673             
29674             var match = false;
29675             
29676             var pattern = box.slice(0, length);
29677             
29678             var format = [];
29679             
29680             Roo.each(pattern, function(i){
29681                 format.push(i.size);
29682             }, this);
29683             
29684             Roo.each(standard, function(s){
29685                 
29686                 if(String(s) != String(format)){
29687                     return;
29688                 }
29689                 
29690                 match = true;
29691                 return false;
29692                 
29693             }, this);
29694             
29695             if(!match && length == 1){
29696                 return;
29697             }
29698             
29699             if(!match){
29700                 filterPattern(box, length - 1);
29701                 return;
29702             }
29703                 
29704             queue.push(pattern);
29705
29706             box = box.slice(length, box.length);
29707
29708             filterPattern(box, 4);
29709
29710             return;
29711             
29712         }
29713         
29714         Roo.each(boxes, function(box, k){
29715             
29716             if(!box.length){
29717                 return;
29718             }
29719             
29720             if(box.length == 1){
29721                 queue.push(box);
29722                 return;
29723             }
29724             
29725             filterPattern(box, 4);
29726             
29727         }, this);
29728         
29729         this._processVerticalLayoutQueue( queue, isInstant );
29730         
29731     },
29732     
29733 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29734 //    {
29735 //        if ( !items || !items.length ) {
29736 //            return;
29737 //        }
29738 //
29739 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29740 //        
29741 //    },
29742     
29743     _horizontalLayoutItems : function ( items , isInstant)
29744     {
29745         if ( !items || !items.length || items.length < 3) {
29746             return;
29747         }
29748         
29749         items.reverse();
29750         
29751         var eItems = items.slice(0, 3);
29752         
29753         items = items.slice(3, items.length);
29754         
29755         var standard = [
29756             ['xs', 'xs', 'xs', 'wide'],
29757             ['xs', 'xs', 'wide'],
29758             ['xs', 'xs', 'sm'],
29759             ['xs', 'xs', 'xs'],
29760             ['xs', 'wide'],
29761             ['xs', 'sm'],
29762             ['xs', 'xs'],
29763             ['xs'],
29764             
29765             ['sm', 'xs', 'xs'],
29766             ['sm', 'xs'],
29767             ['sm'],
29768             
29769             ['wide', 'xs', 'xs', 'xs'],
29770             ['wide', 'xs', 'xs'],
29771             ['wide', 'xs'],
29772             ['wide'],
29773             
29774             ['wide-thin']
29775         ];
29776         
29777         var queue = [];
29778         
29779         var boxes = [];
29780         
29781         var box = [];
29782         
29783         Roo.each(items, function(item, k){
29784             
29785             switch (item.size) {
29786                 case 'md' :
29787                 case 'md-left' :
29788                 case 'md-right' :
29789                 case 'tall' :
29790                     
29791                     if(box.length){
29792                         boxes.push(box);
29793                         box = [];
29794                     }
29795                     
29796                     boxes.push([item]);
29797                     
29798                     break;
29799                     
29800                 case 'xs' :
29801                 case 'sm' :
29802                 case 'wide' :
29803                 case 'wide-thin' :
29804                     
29805                     box.push(item);
29806                     
29807                     break;
29808                 default :
29809                     break;
29810                     
29811             }
29812             
29813         }, this);
29814         
29815         if(box.length){
29816             boxes.push(box);
29817             box = [];
29818         }
29819         
29820         var filterPattern = function(box, length)
29821         {
29822             if(!box.length){
29823                 return;
29824             }
29825             
29826             var match = false;
29827             
29828             var pattern = box.slice(0, length);
29829             
29830             var format = [];
29831             
29832             Roo.each(pattern, function(i){
29833                 format.push(i.size);
29834             }, this);
29835             
29836             Roo.each(standard, function(s){
29837                 
29838                 if(String(s) != String(format)){
29839                     return;
29840                 }
29841                 
29842                 match = true;
29843                 return false;
29844                 
29845             }, this);
29846             
29847             if(!match && length == 1){
29848                 return;
29849             }
29850             
29851             if(!match){
29852                 filterPattern(box, length - 1);
29853                 return;
29854             }
29855                 
29856             queue.push(pattern);
29857
29858             box = box.slice(length, box.length);
29859
29860             filterPattern(box, 4);
29861
29862             return;
29863             
29864         }
29865         
29866         Roo.each(boxes, function(box, k){
29867             
29868             if(!box.length){
29869                 return;
29870             }
29871             
29872             if(box.length == 1){
29873                 queue.push(box);
29874                 return;
29875             }
29876             
29877             filterPattern(box, 4);
29878             
29879         }, this);
29880         
29881         
29882         var prune = [];
29883         
29884         var pos = this.el.getBox(true);
29885         
29886         var minX = pos.x;
29887         
29888         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29889         
29890         var hit_end = false;
29891         
29892         Roo.each(queue, function(box){
29893             
29894             if(hit_end){
29895                 
29896                 Roo.each(box, function(b){
29897                 
29898                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29899                     b.el.hide();
29900
29901                 }, this);
29902
29903                 return;
29904             }
29905             
29906             var mx = 0;
29907             
29908             Roo.each(box, function(b){
29909                 
29910                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29911                 b.el.show();
29912
29913                 mx = Math.max(mx, b.x);
29914                 
29915             }, this);
29916             
29917             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29918             
29919             if(maxX < minX){
29920                 
29921                 Roo.each(box, function(b){
29922                 
29923                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29924                     b.el.hide();
29925                     
29926                 }, this);
29927                 
29928                 hit_end = true;
29929                 
29930                 return;
29931             }
29932             
29933             prune.push(box);
29934             
29935         }, this);
29936         
29937         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29938     },
29939     
29940     /** Sets position of item in DOM
29941     * @param {Element} item
29942     * @param {Number} x - horizontal position
29943     * @param {Number} y - vertical position
29944     * @param {Boolean} isInstant - disables transitions
29945     */
29946     _processVerticalLayoutQueue : function( queue, isInstant )
29947     {
29948         var pos = this.el.getBox(true);
29949         var x = pos.x;
29950         var y = pos.y;
29951         var maxY = [];
29952         
29953         for (var i = 0; i < this.cols; i++){
29954             maxY[i] = pos.y;
29955         }
29956         
29957         Roo.each(queue, function(box, k){
29958             
29959             var col = k % this.cols;
29960             
29961             Roo.each(box, function(b,kk){
29962                 
29963                 b.el.position('absolute');
29964                 
29965                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29966                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29967                 
29968                 if(b.size == 'md-left' || b.size == 'md-right'){
29969                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29970                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29971                 }
29972                 
29973                 b.el.setWidth(width);
29974                 b.el.setHeight(height);
29975                 // iframe?
29976                 b.el.select('iframe',true).setSize(width,height);
29977                 
29978             }, this);
29979             
29980             for (var i = 0; i < this.cols; i++){
29981                 
29982                 if(maxY[i] < maxY[col]){
29983                     col = i;
29984                     continue;
29985                 }
29986                 
29987                 col = Math.min(col, i);
29988                 
29989             }
29990             
29991             x = pos.x + col * (this.colWidth + this.padWidth);
29992             
29993             y = maxY[col];
29994             
29995             var positions = [];
29996             
29997             switch (box.length){
29998                 case 1 :
29999                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30000                     break;
30001                 case 2 :
30002                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30003                     break;
30004                 case 3 :
30005                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30006                     break;
30007                 case 4 :
30008                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30009                     break;
30010                 default :
30011                     break;
30012             }
30013             
30014             Roo.each(box, function(b,kk){
30015                 
30016                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30017                 
30018                 var sz = b.el.getSize();
30019                 
30020                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30021                 
30022             }, this);
30023             
30024         }, this);
30025         
30026         var mY = 0;
30027         
30028         for (var i = 0; i < this.cols; i++){
30029             mY = Math.max(mY, maxY[i]);
30030         }
30031         
30032         this.el.setHeight(mY - pos.y);
30033         
30034     },
30035     
30036 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30037 //    {
30038 //        var pos = this.el.getBox(true);
30039 //        var x = pos.x;
30040 //        var y = pos.y;
30041 //        var maxX = pos.right;
30042 //        
30043 //        var maxHeight = 0;
30044 //        
30045 //        Roo.each(items, function(item, k){
30046 //            
30047 //            var c = k % 2;
30048 //            
30049 //            item.el.position('absolute');
30050 //                
30051 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30052 //
30053 //            item.el.setWidth(width);
30054 //
30055 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30056 //
30057 //            item.el.setHeight(height);
30058 //            
30059 //            if(c == 0){
30060 //                item.el.setXY([x, y], isInstant ? false : true);
30061 //            } else {
30062 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30063 //            }
30064 //            
30065 //            y = y + height + this.alternativePadWidth;
30066 //            
30067 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30068 //            
30069 //        }, this);
30070 //        
30071 //        this.el.setHeight(maxHeight);
30072 //        
30073 //    },
30074     
30075     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30076     {
30077         var pos = this.el.getBox(true);
30078         
30079         var minX = pos.x;
30080         var minY = pos.y;
30081         
30082         var maxX = pos.right;
30083         
30084         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30085         
30086         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30087         
30088         Roo.each(queue, function(box, k){
30089             
30090             Roo.each(box, function(b, kk){
30091                 
30092                 b.el.position('absolute');
30093                 
30094                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30095                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30096                 
30097                 if(b.size == 'md-left' || b.size == 'md-right'){
30098                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30099                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30100                 }
30101                 
30102                 b.el.setWidth(width);
30103                 b.el.setHeight(height);
30104                 
30105             }, this);
30106             
30107             if(!box.length){
30108                 return;
30109             }
30110             
30111             var positions = [];
30112             
30113             switch (box.length){
30114                 case 1 :
30115                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30116                     break;
30117                 case 2 :
30118                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30119                     break;
30120                 case 3 :
30121                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30122                     break;
30123                 case 4 :
30124                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30125                     break;
30126                 default :
30127                     break;
30128             }
30129             
30130             Roo.each(box, function(b,kk){
30131                 
30132                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30133                 
30134                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30135                 
30136             }, this);
30137             
30138         }, this);
30139         
30140     },
30141     
30142     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30143     {
30144         Roo.each(eItems, function(b,k){
30145             
30146             b.size = (k == 0) ? 'sm' : 'xs';
30147             b.x = (k == 0) ? 2 : 1;
30148             b.y = (k == 0) ? 2 : 1;
30149             
30150             b.el.position('absolute');
30151             
30152             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30153                 
30154             b.el.setWidth(width);
30155             
30156             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30157             
30158             b.el.setHeight(height);
30159             
30160         }, this);
30161
30162         var positions = [];
30163         
30164         positions.push({
30165             x : maxX - this.unitWidth * 2 - this.gutter,
30166             y : minY
30167         });
30168         
30169         positions.push({
30170             x : maxX - this.unitWidth,
30171             y : minY + (this.unitWidth + this.gutter) * 2
30172         });
30173         
30174         positions.push({
30175             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30176             y : minY
30177         });
30178         
30179         Roo.each(eItems, function(b,k){
30180             
30181             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30182
30183         }, this);
30184         
30185     },
30186     
30187     getVerticalOneBoxColPositions : function(x, y, box)
30188     {
30189         var pos = [];
30190         
30191         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30192         
30193         if(box[0].size == 'md-left'){
30194             rand = 0;
30195         }
30196         
30197         if(box[0].size == 'md-right'){
30198             rand = 1;
30199         }
30200         
30201         pos.push({
30202             x : x + (this.unitWidth + this.gutter) * rand,
30203             y : y
30204         });
30205         
30206         return pos;
30207     },
30208     
30209     getVerticalTwoBoxColPositions : function(x, y, box)
30210     {
30211         var pos = [];
30212         
30213         if(box[0].size == 'xs'){
30214             
30215             pos.push({
30216                 x : x,
30217                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30218             });
30219
30220             pos.push({
30221                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30222                 y : y
30223             });
30224             
30225             return pos;
30226             
30227         }
30228         
30229         pos.push({
30230             x : x,
30231             y : y
30232         });
30233
30234         pos.push({
30235             x : x + (this.unitWidth + this.gutter) * 2,
30236             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30237         });
30238         
30239         return pos;
30240         
30241     },
30242     
30243     getVerticalThreeBoxColPositions : function(x, y, box)
30244     {
30245         var pos = [];
30246         
30247         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30248             
30249             pos.push({
30250                 x : x,
30251                 y : y
30252             });
30253
30254             pos.push({
30255                 x : x + (this.unitWidth + this.gutter) * 1,
30256                 y : y
30257             });
30258             
30259             pos.push({
30260                 x : x + (this.unitWidth + this.gutter) * 2,
30261                 y : y
30262             });
30263             
30264             return pos;
30265             
30266         }
30267         
30268         if(box[0].size == 'xs' && box[1].size == 'xs'){
30269             
30270             pos.push({
30271                 x : x,
30272                 y : y
30273             });
30274
30275             pos.push({
30276                 x : x,
30277                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30278             });
30279             
30280             pos.push({
30281                 x : x + (this.unitWidth + this.gutter) * 1,
30282                 y : y
30283             });
30284             
30285             return pos;
30286             
30287         }
30288         
30289         pos.push({
30290             x : x,
30291             y : y
30292         });
30293
30294         pos.push({
30295             x : x + (this.unitWidth + this.gutter) * 2,
30296             y : y
30297         });
30298
30299         pos.push({
30300             x : x + (this.unitWidth + this.gutter) * 2,
30301             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30302         });
30303             
30304         return pos;
30305         
30306     },
30307     
30308     getVerticalFourBoxColPositions : function(x, y, box)
30309     {
30310         var pos = [];
30311         
30312         if(box[0].size == 'xs'){
30313             
30314             pos.push({
30315                 x : x,
30316                 y : y
30317             });
30318
30319             pos.push({
30320                 x : x,
30321                 y : y + (this.unitHeight + this.gutter) * 1
30322             });
30323             
30324             pos.push({
30325                 x : x,
30326                 y : y + (this.unitHeight + this.gutter) * 2
30327             });
30328             
30329             pos.push({
30330                 x : x + (this.unitWidth + this.gutter) * 1,
30331                 y : y
30332             });
30333             
30334             return pos;
30335             
30336         }
30337         
30338         pos.push({
30339             x : x,
30340             y : y
30341         });
30342
30343         pos.push({
30344             x : x + (this.unitWidth + this.gutter) * 2,
30345             y : y
30346         });
30347
30348         pos.push({
30349             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30350             y : y + (this.unitHeight + this.gutter) * 1
30351         });
30352
30353         pos.push({
30354             x : x + (this.unitWidth + this.gutter) * 2,
30355             y : y + (this.unitWidth + this.gutter) * 2
30356         });
30357
30358         return pos;
30359         
30360     },
30361     
30362     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30363     {
30364         var pos = [];
30365         
30366         if(box[0].size == 'md-left'){
30367             pos.push({
30368                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30369                 y : minY
30370             });
30371             
30372             return pos;
30373         }
30374         
30375         if(box[0].size == 'md-right'){
30376             pos.push({
30377                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30378                 y : minY + (this.unitWidth + this.gutter) * 1
30379             });
30380             
30381             return pos;
30382         }
30383         
30384         var rand = Math.floor(Math.random() * (4 - box[0].y));
30385         
30386         pos.push({
30387             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30388             y : minY + (this.unitWidth + this.gutter) * rand
30389         });
30390         
30391         return pos;
30392         
30393     },
30394     
30395     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30396     {
30397         var pos = [];
30398         
30399         if(box[0].size == 'xs'){
30400             
30401             pos.push({
30402                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30403                 y : minY
30404             });
30405
30406             pos.push({
30407                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30408                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30409             });
30410             
30411             return pos;
30412             
30413         }
30414         
30415         pos.push({
30416             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30417             y : minY
30418         });
30419
30420         pos.push({
30421             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30422             y : minY + (this.unitWidth + this.gutter) * 2
30423         });
30424         
30425         return pos;
30426         
30427     },
30428     
30429     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30430     {
30431         var pos = [];
30432         
30433         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30434             
30435             pos.push({
30436                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30437                 y : minY
30438             });
30439
30440             pos.push({
30441                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30442                 y : minY + (this.unitWidth + this.gutter) * 1
30443             });
30444             
30445             pos.push({
30446                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30447                 y : minY + (this.unitWidth + this.gutter) * 2
30448             });
30449             
30450             return pos;
30451             
30452         }
30453         
30454         if(box[0].size == 'xs' && box[1].size == 'xs'){
30455             
30456             pos.push({
30457                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30458                 y : minY
30459             });
30460
30461             pos.push({
30462                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30463                 y : minY
30464             });
30465             
30466             pos.push({
30467                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30468                 y : minY + (this.unitWidth + this.gutter) * 1
30469             });
30470             
30471             return pos;
30472             
30473         }
30474         
30475         pos.push({
30476             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30477             y : minY
30478         });
30479
30480         pos.push({
30481             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30482             y : minY + (this.unitWidth + this.gutter) * 2
30483         });
30484
30485         pos.push({
30486             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30487             y : minY + (this.unitWidth + this.gutter) * 2
30488         });
30489             
30490         return pos;
30491         
30492     },
30493     
30494     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30495     {
30496         var pos = [];
30497         
30498         if(box[0].size == 'xs'){
30499             
30500             pos.push({
30501                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30502                 y : minY
30503             });
30504
30505             pos.push({
30506                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30507                 y : minY
30508             });
30509             
30510             pos.push({
30511                 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),
30512                 y : minY
30513             });
30514             
30515             pos.push({
30516                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30517                 y : minY + (this.unitWidth + this.gutter) * 1
30518             });
30519             
30520             return pos;
30521             
30522         }
30523         
30524         pos.push({
30525             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30526             y : minY
30527         });
30528         
30529         pos.push({
30530             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30531             y : minY + (this.unitWidth + this.gutter) * 2
30532         });
30533         
30534         pos.push({
30535             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30536             y : minY + (this.unitWidth + this.gutter) * 2
30537         });
30538         
30539         pos.push({
30540             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),
30541             y : minY + (this.unitWidth + this.gutter) * 2
30542         });
30543
30544         return pos;
30545         
30546     }
30547     
30548 });
30549
30550  
30551
30552  /**
30553  *
30554  * This is based on 
30555  * http://masonry.desandro.com
30556  *
30557  * The idea is to render all the bricks based on vertical width...
30558  *
30559  * The original code extends 'outlayer' - we might need to use that....
30560  * 
30561  */
30562
30563
30564 /**
30565  * @class Roo.bootstrap.LayoutMasonryAuto
30566  * @extends Roo.bootstrap.Component
30567  * Bootstrap Layout Masonry class
30568  * 
30569  * @constructor
30570  * Create a new Element
30571  * @param {Object} config The config object
30572  */
30573
30574 Roo.bootstrap.LayoutMasonryAuto = function(config){
30575     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30576 };
30577
30578 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30579     
30580       /**
30581      * @cfg {Boolean} isFitWidth  - resize the width..
30582      */   
30583     isFitWidth : false,  // options..
30584     /**
30585      * @cfg {Boolean} isOriginLeft = left align?
30586      */   
30587     isOriginLeft : true,
30588     /**
30589      * @cfg {Boolean} isOriginTop = top align?
30590      */   
30591     isOriginTop : false,
30592     /**
30593      * @cfg {Boolean} isLayoutInstant = no animation?
30594      */   
30595     isLayoutInstant : false, // needed?
30596     /**
30597      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30598      */   
30599     isResizingContainer : true,
30600     /**
30601      * @cfg {Number} columnWidth  width of the columns 
30602      */   
30603     
30604     columnWidth : 0,
30605     
30606     /**
30607      * @cfg {Number} maxCols maximum number of columns
30608      */   
30609     
30610     maxCols: 0,
30611     /**
30612      * @cfg {Number} padHeight padding below box..
30613      */   
30614     
30615     padHeight : 10, 
30616     
30617     /**
30618      * @cfg {Boolean} isAutoInitial defalut true
30619      */   
30620     
30621     isAutoInitial : true, 
30622     
30623     // private?
30624     gutter : 0,
30625     
30626     containerWidth: 0,
30627     initialColumnWidth : 0,
30628     currentSize : null,
30629     
30630     colYs : null, // array.
30631     maxY : 0,
30632     padWidth: 10,
30633     
30634     
30635     tag: 'div',
30636     cls: '',
30637     bricks: null, //CompositeElement
30638     cols : 0, // array?
30639     // element : null, // wrapped now this.el
30640     _isLayoutInited : null, 
30641     
30642     
30643     getAutoCreate : function(){
30644         
30645         var cfg = {
30646             tag: this.tag,
30647             cls: 'blog-masonary-wrapper ' + this.cls,
30648             cn : {
30649                 cls : 'mas-boxes masonary'
30650             }
30651         };
30652         
30653         return cfg;
30654     },
30655     
30656     getChildContainer: function( )
30657     {
30658         if (this.boxesEl) {
30659             return this.boxesEl;
30660         }
30661         
30662         this.boxesEl = this.el.select('.mas-boxes').first();
30663         
30664         return this.boxesEl;
30665     },
30666     
30667     
30668     initEvents : function()
30669     {
30670         var _this = this;
30671         
30672         if(this.isAutoInitial){
30673             Roo.log('hook children rendered');
30674             this.on('childrenrendered', function() {
30675                 Roo.log('children rendered');
30676                 _this.initial();
30677             } ,this);
30678         }
30679         
30680     },
30681     
30682     initial : function()
30683     {
30684         this.reloadItems();
30685
30686         this.currentSize = this.el.getBox(true);
30687
30688         /// was window resize... - let's see if this works..
30689         Roo.EventManager.onWindowResize(this.resize, this); 
30690
30691         if(!this.isAutoInitial){
30692             this.layout();
30693             return;
30694         }
30695         
30696         this.layout.defer(500,this);
30697     },
30698     
30699     reloadItems: function()
30700     {
30701         this.bricks = this.el.select('.masonry-brick', true);
30702         
30703         this.bricks.each(function(b) {
30704             //Roo.log(b.getSize());
30705             if (!b.attr('originalwidth')) {
30706                 b.attr('originalwidth',  b.getSize().width);
30707             }
30708             
30709         });
30710         
30711         Roo.log(this.bricks.elements.length);
30712     },
30713     
30714     resize : function()
30715     {
30716         Roo.log('resize');
30717         var cs = this.el.getBox(true);
30718         
30719         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30720             Roo.log("no change in with or X");
30721             return;
30722         }
30723         this.currentSize = cs;
30724         this.layout();
30725     },
30726     
30727     layout : function()
30728     {
30729          Roo.log('layout');
30730         this._resetLayout();
30731         //this._manageStamps();
30732       
30733         // don't animate first layout
30734         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30735         this.layoutItems( isInstant );
30736       
30737         // flag for initalized
30738         this._isLayoutInited = true;
30739     },
30740     
30741     layoutItems : function( isInstant )
30742     {
30743         //var items = this._getItemsForLayout( this.items );
30744         // original code supports filtering layout items.. we just ignore it..
30745         
30746         this._layoutItems( this.bricks , isInstant );
30747       
30748         this._postLayout();
30749     },
30750     _layoutItems : function ( items , isInstant)
30751     {
30752        //this.fireEvent( 'layout', this, items );
30753     
30754
30755         if ( !items || !items.elements.length ) {
30756           // no items, emit event with empty array
30757             return;
30758         }
30759
30760         var queue = [];
30761         items.each(function(item) {
30762             Roo.log("layout item");
30763             Roo.log(item);
30764             // get x/y object from method
30765             var position = this._getItemLayoutPosition( item );
30766             // enqueue
30767             position.item = item;
30768             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30769             queue.push( position );
30770         }, this);
30771       
30772         this._processLayoutQueue( queue );
30773     },
30774     /** Sets position of item in DOM
30775     * @param {Element} item
30776     * @param {Number} x - horizontal position
30777     * @param {Number} y - vertical position
30778     * @param {Boolean} isInstant - disables transitions
30779     */
30780     _processLayoutQueue : function( queue )
30781     {
30782         for ( var i=0, len = queue.length; i < len; i++ ) {
30783             var obj = queue[i];
30784             obj.item.position('absolute');
30785             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30786         }
30787     },
30788       
30789     
30790     /**
30791     * Any logic you want to do after each layout,
30792     * i.e. size the container
30793     */
30794     _postLayout : function()
30795     {
30796         this.resizeContainer();
30797     },
30798     
30799     resizeContainer : function()
30800     {
30801         if ( !this.isResizingContainer ) {
30802             return;
30803         }
30804         var size = this._getContainerSize();
30805         if ( size ) {
30806             this.el.setSize(size.width,size.height);
30807             this.boxesEl.setSize(size.width,size.height);
30808         }
30809     },
30810     
30811     
30812     
30813     _resetLayout : function()
30814     {
30815         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30816         this.colWidth = this.el.getWidth();
30817         //this.gutter = this.el.getWidth(); 
30818         
30819         this.measureColumns();
30820
30821         // reset column Y
30822         var i = this.cols;
30823         this.colYs = [];
30824         while (i--) {
30825             this.colYs.push( 0 );
30826         }
30827     
30828         this.maxY = 0;
30829     },
30830
30831     measureColumns : function()
30832     {
30833         this.getContainerWidth();
30834       // if columnWidth is 0, default to outerWidth of first item
30835         if ( !this.columnWidth ) {
30836             var firstItem = this.bricks.first();
30837             Roo.log(firstItem);
30838             this.columnWidth  = this.containerWidth;
30839             if (firstItem && firstItem.attr('originalwidth') ) {
30840                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30841             }
30842             // columnWidth fall back to item of first element
30843             Roo.log("set column width?");
30844                         this.initialColumnWidth = this.columnWidth  ;
30845
30846             // if first elem has no width, default to size of container
30847             
30848         }
30849         
30850         
30851         if (this.initialColumnWidth) {
30852             this.columnWidth = this.initialColumnWidth;
30853         }
30854         
30855         
30856             
30857         // column width is fixed at the top - however if container width get's smaller we should
30858         // reduce it...
30859         
30860         // this bit calcs how man columns..
30861             
30862         var columnWidth = this.columnWidth += this.gutter;
30863       
30864         // calculate columns
30865         var containerWidth = this.containerWidth + this.gutter;
30866         
30867         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30868         // fix rounding errors, typically with gutters
30869         var excess = columnWidth - containerWidth % columnWidth;
30870         
30871         
30872         // if overshoot is less than a pixel, round up, otherwise floor it
30873         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30874         cols = Math[ mathMethod ]( cols );
30875         this.cols = Math.max( cols, 1 );
30876         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30877         
30878          // padding positioning..
30879         var totalColWidth = this.cols * this.columnWidth;
30880         var padavail = this.containerWidth - totalColWidth;
30881         // so for 2 columns - we need 3 'pads'
30882         
30883         var padNeeded = (1+this.cols) * this.padWidth;
30884         
30885         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30886         
30887         this.columnWidth += padExtra
30888         //this.padWidth = Math.floor(padavail /  ( this.cols));
30889         
30890         // adjust colum width so that padding is fixed??
30891         
30892         // we have 3 columns ... total = width * 3
30893         // we have X left over... that should be used by 
30894         
30895         //if (this.expandC) {
30896             
30897         //}
30898         
30899         
30900         
30901     },
30902     
30903     getContainerWidth : function()
30904     {
30905        /* // container is parent if fit width
30906         var container = this.isFitWidth ? this.element.parentNode : this.element;
30907         // check that this.size and size are there
30908         // IE8 triggers resize on body size change, so they might not be
30909         
30910         var size = getSize( container );  //FIXME
30911         this.containerWidth = size && size.innerWidth; //FIXME
30912         */
30913          
30914         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30915         
30916     },
30917     
30918     _getItemLayoutPosition : function( item )  // what is item?
30919     {
30920         // we resize the item to our columnWidth..
30921       
30922         item.setWidth(this.columnWidth);
30923         item.autoBoxAdjust  = false;
30924         
30925         var sz = item.getSize();
30926  
30927         // how many columns does this brick span
30928         var remainder = this.containerWidth % this.columnWidth;
30929         
30930         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30931         // round if off by 1 pixel, otherwise use ceil
30932         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30933         colSpan = Math.min( colSpan, this.cols );
30934         
30935         // normally this should be '1' as we dont' currently allow multi width columns..
30936         
30937         var colGroup = this._getColGroup( colSpan );
30938         // get the minimum Y value from the columns
30939         var minimumY = Math.min.apply( Math, colGroup );
30940         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30941         
30942         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30943          
30944         // position the brick
30945         var position = {
30946             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30947             y: this.currentSize.y + minimumY + this.padHeight
30948         };
30949         
30950         Roo.log(position);
30951         // apply setHeight to necessary columns
30952         var setHeight = minimumY + sz.height + this.padHeight;
30953         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30954         
30955         var setSpan = this.cols + 1 - colGroup.length;
30956         for ( var i = 0; i < setSpan; i++ ) {
30957           this.colYs[ shortColIndex + i ] = setHeight ;
30958         }
30959       
30960         return position;
30961     },
30962     
30963     /**
30964      * @param {Number} colSpan - number of columns the element spans
30965      * @returns {Array} colGroup
30966      */
30967     _getColGroup : function( colSpan )
30968     {
30969         if ( colSpan < 2 ) {
30970           // if brick spans only one column, use all the column Ys
30971           return this.colYs;
30972         }
30973       
30974         var colGroup = [];
30975         // how many different places could this brick fit horizontally
30976         var groupCount = this.cols + 1 - colSpan;
30977         // for each group potential horizontal position
30978         for ( var i = 0; i < groupCount; i++ ) {
30979           // make an array of colY values for that one group
30980           var groupColYs = this.colYs.slice( i, i + colSpan );
30981           // and get the max value of the array
30982           colGroup[i] = Math.max.apply( Math, groupColYs );
30983         }
30984         return colGroup;
30985     },
30986     /*
30987     _manageStamp : function( stamp )
30988     {
30989         var stampSize =  stamp.getSize();
30990         var offset = stamp.getBox();
30991         // get the columns that this stamp affects
30992         var firstX = this.isOriginLeft ? offset.x : offset.right;
30993         var lastX = firstX + stampSize.width;
30994         var firstCol = Math.floor( firstX / this.columnWidth );
30995         firstCol = Math.max( 0, firstCol );
30996         
30997         var lastCol = Math.floor( lastX / this.columnWidth );
30998         // lastCol should not go over if multiple of columnWidth #425
30999         lastCol -= lastX % this.columnWidth ? 0 : 1;
31000         lastCol = Math.min( this.cols - 1, lastCol );
31001         
31002         // set colYs to bottom of the stamp
31003         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31004             stampSize.height;
31005             
31006         for ( var i = firstCol; i <= lastCol; i++ ) {
31007           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31008         }
31009     },
31010     */
31011     
31012     _getContainerSize : function()
31013     {
31014         this.maxY = Math.max.apply( Math, this.colYs );
31015         var size = {
31016             height: this.maxY
31017         };
31018       
31019         if ( this.isFitWidth ) {
31020             size.width = this._getContainerFitWidth();
31021         }
31022       
31023         return size;
31024     },
31025     
31026     _getContainerFitWidth : function()
31027     {
31028         var unusedCols = 0;
31029         // count unused columns
31030         var i = this.cols;
31031         while ( --i ) {
31032           if ( this.colYs[i] !== 0 ) {
31033             break;
31034           }
31035           unusedCols++;
31036         }
31037         // fit container to columns that have been used
31038         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31039     },
31040     
31041     needsResizeLayout : function()
31042     {
31043         var previousWidth = this.containerWidth;
31044         this.getContainerWidth();
31045         return previousWidth !== this.containerWidth;
31046     }
31047  
31048 });
31049
31050  
31051
31052  /*
31053  * - LGPL
31054  *
31055  * element
31056  * 
31057  */
31058
31059 /**
31060  * @class Roo.bootstrap.MasonryBrick
31061  * @extends Roo.bootstrap.Component
31062  * Bootstrap MasonryBrick class
31063  * 
31064  * @constructor
31065  * Create a new MasonryBrick
31066  * @param {Object} config The config object
31067  */
31068
31069 Roo.bootstrap.MasonryBrick = function(config){
31070     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31071     
31072     this.addEvents({
31073         // raw events
31074         /**
31075          * @event click
31076          * When a MasonryBrick is clcik
31077          * @param {Roo.bootstrap.MasonryBrick} this
31078          * @param {Roo.EventObject} e
31079          */
31080         "click" : true
31081     });
31082 };
31083
31084 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31085     
31086     /**
31087      * @cfg {String} title
31088      */   
31089     title : '',
31090     /**
31091      * @cfg {String} html
31092      */   
31093     html : '',
31094     /**
31095      * @cfg {String} bgimage
31096      */   
31097     bgimage : '',
31098     /**
31099      * @cfg {String} videourl
31100      */   
31101     videourl : '',
31102     /**
31103      * @cfg {String} cls
31104      */   
31105     cls : '',
31106     /**
31107      * @cfg {String} href
31108      */   
31109     href : '',
31110     /**
31111      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31112      */   
31113     size : 'xs',
31114     
31115     /**
31116      * @cfg {String} (center|bottom) placetitle
31117      */   
31118     placetitle : '',
31119     
31120     /**
31121      * @cfg {Boolean} isFitContainer defalut true
31122      */   
31123     isFitContainer : true, 
31124     
31125     /**
31126      * @cfg {Boolean} preventDefault defalut false
31127      */   
31128     preventDefault : false, 
31129     
31130     getAutoCreate : function()
31131     {
31132         if(!this.isFitContainer){
31133             return this.getSplitAutoCreate();
31134         }
31135         
31136         var cls = 'masonry-brick masonry-brick-full';
31137         
31138         if(this.href.length){
31139             cls += ' masonry-brick-link';
31140         }
31141         
31142         if(this.bgimage.length){
31143             cls += ' masonry-brick-image';
31144         }
31145         
31146         if(!this.html.length){
31147             cls += ' enable-mask';
31148         }
31149         
31150         if(this.size){
31151             cls += ' masonry-' + this.size + '-brick';
31152         }
31153         
31154         if(this.placetitle.length){
31155             
31156             switch (this.placetitle) {
31157                 case 'center' :
31158                     cls += ' masonry-center-title';
31159                     break;
31160                 case 'bottom' :
31161                     cls += ' masonry-bottom-title';
31162                     break;
31163                 default:
31164                     break;
31165             }
31166             
31167         } else {
31168             if(!this.html.length && !this.bgimage.length){
31169                 cls += ' masonry-center-title';
31170             }
31171
31172             if(!this.html.length && this.bgimage.length){
31173                 cls += ' masonry-bottom-title';
31174             }
31175         }
31176         
31177         if(this.cls){
31178             cls += ' ' + this.cls;
31179         }
31180         
31181         var cfg = {
31182             tag: (this.href.length) ? 'a' : 'div',
31183             cls: cls,
31184             cn: [
31185                 {
31186                     tag: 'div',
31187                     cls: 'masonry-brick-paragraph',
31188                     cn: []
31189                 }
31190             ]
31191         };
31192         
31193         if(this.href.length){
31194             cfg.href = this.href;
31195         }
31196         
31197         var cn = cfg.cn[0].cn;
31198         
31199         if(this.title.length){
31200             cn.push({
31201                 tag: 'h4',
31202                 cls: 'masonry-brick-title',
31203                 html: this.title
31204             });
31205         }
31206         
31207         if(this.html.length){
31208             cn.push({
31209                 tag: 'p',
31210                 cls: 'masonry-brick-text',
31211                 html: this.html
31212             });
31213         }  
31214         if (!this.title.length && !this.html.length) {
31215             cfg.cn[0].cls += ' hide';
31216         }
31217         
31218         if(this.bgimage.length){
31219             cfg.cn.push({
31220                 tag: 'img',
31221                 cls: 'masonry-brick-image-view',
31222                 src: this.bgimage
31223             });
31224         }
31225         
31226         if(this.videourl.length){
31227             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31228             // youtube support only?
31229             cfg.cn.push({
31230                 tag: 'iframe',
31231                 cls: 'masonry-brick-image-view',
31232                 src: vurl,
31233                 frameborder : 0,
31234                 allowfullscreen : true
31235             });
31236             
31237             
31238         }
31239         
31240         cfg.cn.push({
31241             tag: 'div',
31242             cls: 'masonry-brick-mask'
31243         });
31244         
31245         return cfg;
31246         
31247     },
31248     
31249     getSplitAutoCreate : function()
31250     {
31251         var cls = 'masonry-brick masonry-brick-split';
31252         
31253         if(this.href.length){
31254             cls += ' masonry-brick-link';
31255         }
31256         
31257         if(this.bgimage.length){
31258             cls += ' masonry-brick-image';
31259         }
31260         
31261         if(this.size){
31262             cls += ' masonry-' + this.size + '-brick';
31263         }
31264         
31265         switch (this.placetitle) {
31266             case 'center' :
31267                 cls += ' masonry-center-title';
31268                 break;
31269             case 'bottom' :
31270                 cls += ' masonry-bottom-title';
31271                 break;
31272             default:
31273                 if(!this.bgimage.length){
31274                     cls += ' masonry-center-title';
31275                 }
31276
31277                 if(this.bgimage.length){
31278                     cls += ' masonry-bottom-title';
31279                 }
31280                 break;
31281         }
31282         
31283         if(this.cls){
31284             cls += ' ' + this.cls;
31285         }
31286         
31287         var cfg = {
31288             tag: (this.href.length) ? 'a' : 'div',
31289             cls: cls,
31290             cn: [
31291                 {
31292                     tag: 'div',
31293                     cls: 'masonry-brick-split-head',
31294                     cn: [
31295                         {
31296                             tag: 'div',
31297                             cls: 'masonry-brick-paragraph',
31298                             cn: []
31299                         }
31300                     ]
31301                 },
31302                 {
31303                     tag: 'div',
31304                     cls: 'masonry-brick-split-body',
31305                     cn: []
31306                 }
31307             ]
31308         };
31309         
31310         if(this.href.length){
31311             cfg.href = this.href;
31312         }
31313         
31314         if(this.title.length){
31315             cfg.cn[0].cn[0].cn.push({
31316                 tag: 'h4',
31317                 cls: 'masonry-brick-title',
31318                 html: this.title
31319             });
31320         }
31321         
31322         if(this.html.length){
31323             cfg.cn[1].cn.push({
31324                 tag: 'p',
31325                 cls: 'masonry-brick-text',
31326                 html: this.html
31327             });
31328         }
31329
31330         if(this.bgimage.length){
31331             cfg.cn[0].cn.push({
31332                 tag: 'img',
31333                 cls: 'masonry-brick-image-view',
31334                 src: this.bgimage
31335             });
31336         }
31337         
31338         if(this.videourl.length){
31339             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31340             // youtube support only?
31341             cfg.cn[0].cn.cn.push({
31342                 tag: 'iframe',
31343                 cls: 'masonry-brick-image-view',
31344                 src: vurl,
31345                 frameborder : 0,
31346                 allowfullscreen : true
31347             });
31348         }
31349         
31350         return cfg;
31351     },
31352     
31353     initEvents: function() 
31354     {
31355         switch (this.size) {
31356             case 'xs' :
31357                 this.x = 1;
31358                 this.y = 1;
31359                 break;
31360             case 'sm' :
31361                 this.x = 2;
31362                 this.y = 2;
31363                 break;
31364             case 'md' :
31365             case 'md-left' :
31366             case 'md-right' :
31367                 this.x = 3;
31368                 this.y = 3;
31369                 break;
31370             case 'tall' :
31371                 this.x = 2;
31372                 this.y = 3;
31373                 break;
31374             case 'wide' :
31375                 this.x = 3;
31376                 this.y = 2;
31377                 break;
31378             case 'wide-thin' :
31379                 this.x = 3;
31380                 this.y = 1;
31381                 break;
31382                         
31383             default :
31384                 break;
31385         }
31386         
31387         if(Roo.isTouch){
31388             this.el.on('touchstart', this.onTouchStart, this);
31389             this.el.on('touchmove', this.onTouchMove, this);
31390             this.el.on('touchend', this.onTouchEnd, this);
31391             this.el.on('contextmenu', this.onContextMenu, this);
31392         } else {
31393             this.el.on('mouseenter'  ,this.enter, this);
31394             this.el.on('mouseleave', this.leave, this);
31395             this.el.on('click', this.onClick, this);
31396         }
31397         
31398         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31399             this.parent().bricks.push(this);   
31400         }
31401         
31402     },
31403     
31404     onClick: function(e, el)
31405     {
31406         var time = this.endTimer - this.startTimer;
31407         
31408         if(Roo.isTouch){
31409             if(time > 1000){
31410                 e.preventDefault();
31411                 return;
31412             }
31413         }
31414         
31415         if(!this.preventDefault){
31416             return;
31417         }
31418         
31419         e.preventDefault();
31420         this.fireEvent('click', this);
31421     },
31422     
31423     enter: function(e, el)
31424     {
31425         e.preventDefault();
31426         
31427         if(!this.isFitContainer){
31428             return;
31429         }
31430         
31431         if(this.bgimage.length && this.html.length){
31432             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31433         }
31434     },
31435     
31436     leave: function(e, el)
31437     {
31438         e.preventDefault();
31439         
31440         if(!this.isFitContainer){
31441             return;
31442         }
31443         
31444         if(this.bgimage.length && this.html.length){
31445             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31446         }
31447     },
31448     
31449     onTouchStart: function(e, el)
31450     {
31451 //        e.preventDefault();
31452         
31453         this.touchmoved = false;
31454         
31455         if(!this.isFitContainer){
31456             return;
31457         }
31458         
31459         if(!this.bgimage.length || !this.html.length){
31460             return;
31461         }
31462         
31463         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31464         
31465         this.timer = new Date().getTime();
31466         
31467     },
31468     
31469     onTouchMove: function(e, el)
31470     {
31471         this.touchmoved = true;
31472     },
31473     
31474     onContextMenu : function(e,el)
31475     {
31476         e.preventDefault();
31477         e.stopPropagation();
31478         return false;
31479     },
31480     
31481     onTouchEnd: function(e, el)
31482     {
31483 //        e.preventDefault();
31484         
31485         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31486         
31487             this.leave(e,el);
31488             
31489             return;
31490         }
31491         
31492         if(!this.bgimage.length || !this.html.length){
31493             
31494             if(this.href.length){
31495                 window.location.href = this.href;
31496             }
31497             
31498             return;
31499         }
31500         
31501         if(!this.isFitContainer){
31502             return;
31503         }
31504         
31505         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31506         
31507         window.location.href = this.href;
31508     }
31509     
31510 });
31511
31512  
31513
31514  /*
31515  * - LGPL
31516  *
31517  * element
31518  * 
31519  */
31520
31521 /**
31522  * @class Roo.bootstrap.Brick
31523  * @extends Roo.bootstrap.Component
31524  * Bootstrap Brick class
31525  * 
31526  * @constructor
31527  * Create a new Brick
31528  * @param {Object} config The config object
31529  */
31530
31531 Roo.bootstrap.Brick = function(config){
31532     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31533     
31534     this.addEvents({
31535         // raw events
31536         /**
31537          * @event click
31538          * When a Brick is click
31539          * @param {Roo.bootstrap.Brick} this
31540          * @param {Roo.EventObject} e
31541          */
31542         "click" : true
31543     });
31544 };
31545
31546 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31547     
31548     /**
31549      * @cfg {String} title
31550      */   
31551     title : '',
31552     /**
31553      * @cfg {String} html
31554      */   
31555     html : '',
31556     /**
31557      * @cfg {String} bgimage
31558      */   
31559     bgimage : '',
31560     /**
31561      * @cfg {String} cls
31562      */   
31563     cls : '',
31564     /**
31565      * @cfg {String} href
31566      */   
31567     href : '',
31568     /**
31569      * @cfg {String} video
31570      */   
31571     video : '',
31572     /**
31573      * @cfg {Boolean} square
31574      */   
31575     square : true,
31576     
31577     getAutoCreate : function()
31578     {
31579         var cls = 'roo-brick';
31580         
31581         if(this.href.length){
31582             cls += ' roo-brick-link';
31583         }
31584         
31585         if(this.bgimage.length){
31586             cls += ' roo-brick-image';
31587         }
31588         
31589         if(!this.html.length && !this.bgimage.length){
31590             cls += ' roo-brick-center-title';
31591         }
31592         
31593         if(!this.html.length && this.bgimage.length){
31594             cls += ' roo-brick-bottom-title';
31595         }
31596         
31597         if(this.cls){
31598             cls += ' ' + this.cls;
31599         }
31600         
31601         var cfg = {
31602             tag: (this.href.length) ? 'a' : 'div',
31603             cls: cls,
31604             cn: [
31605                 {
31606                     tag: 'div',
31607                     cls: 'roo-brick-paragraph',
31608                     cn: []
31609                 }
31610             ]
31611         };
31612         
31613         if(this.href.length){
31614             cfg.href = this.href;
31615         }
31616         
31617         var cn = cfg.cn[0].cn;
31618         
31619         if(this.title.length){
31620             cn.push({
31621                 tag: 'h4',
31622                 cls: 'roo-brick-title',
31623                 html: this.title
31624             });
31625         }
31626         
31627         if(this.html.length){
31628             cn.push({
31629                 tag: 'p',
31630                 cls: 'roo-brick-text',
31631                 html: this.html
31632             });
31633         } else {
31634             cn.cls += ' hide';
31635         }
31636         
31637         if(this.bgimage.length){
31638             cfg.cn.push({
31639                 tag: 'img',
31640                 cls: 'roo-brick-image-view',
31641                 src: this.bgimage
31642             });
31643         }
31644         
31645         return cfg;
31646     },
31647     
31648     initEvents: function() 
31649     {
31650         if(this.title.length || this.html.length){
31651             this.el.on('mouseenter'  ,this.enter, this);
31652             this.el.on('mouseleave', this.leave, this);
31653         }
31654         
31655         
31656         Roo.EventManager.onWindowResize(this.resize, this); 
31657         
31658         this.resize();
31659     },
31660     
31661     resize : function()
31662     {
31663         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31664         
31665         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31666         
31667         if(this.bgimage.length){
31668             var image = this.el.select('.roo-brick-image-view', true).first();
31669             image.setWidth(paragraph.getWidth());
31670             image.setHeight(paragraph.getWidth());
31671             
31672             this.el.setHeight(paragraph.getWidth());
31673             
31674         }
31675         
31676     },
31677     
31678     enter: function(e, el)
31679     {
31680         e.preventDefault();
31681         
31682         if(this.bgimage.length){
31683             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31684             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31685         }
31686     },
31687     
31688     leave: function(e, el)
31689     {
31690         e.preventDefault();
31691         
31692         if(this.bgimage.length){
31693             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31694             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31695         }
31696     }
31697     
31698 });
31699
31700  
31701
31702  /*
31703  * - LGPL
31704  *
31705  * Input
31706  * 
31707  */
31708
31709 /**
31710  * @class Roo.bootstrap.NumberField
31711  * @extends Roo.bootstrap.Input
31712  * Bootstrap NumberField class
31713  * 
31714  * 
31715  * 
31716  * 
31717  * @constructor
31718  * Create a new NumberField
31719  * @param {Object} config The config object
31720  */
31721
31722 Roo.bootstrap.NumberField = function(config){
31723     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31724 };
31725
31726 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31727     
31728     /**
31729      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31730      */
31731     allowDecimals : true,
31732     /**
31733      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31734      */
31735     decimalSeparator : ".",
31736     /**
31737      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31738      */
31739     decimalPrecision : 2,
31740     /**
31741      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31742      */
31743     allowNegative : true,
31744     /**
31745      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31746      */
31747     minValue : Number.NEGATIVE_INFINITY,
31748     /**
31749      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31750      */
31751     maxValue : Number.MAX_VALUE,
31752     /**
31753      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31754      */
31755     minText : "The minimum value for this field is {0}",
31756     /**
31757      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31758      */
31759     maxText : "The maximum value for this field is {0}",
31760     /**
31761      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
31762      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31763      */
31764     nanText : "{0} is not a valid number",
31765     /**
31766      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31767      */
31768     castInt : true,
31769
31770     // private
31771     initEvents : function()
31772     {   
31773         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31774         
31775         var allowed = "0123456789";
31776         
31777         if(this.allowDecimals){
31778             allowed += this.decimalSeparator;
31779         }
31780         
31781         if(this.allowNegative){
31782             allowed += "-";
31783         }
31784         
31785         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31786         
31787         var keyPress = function(e){
31788             
31789             var k = e.getKey();
31790             
31791             var c = e.getCharCode();
31792             
31793             if(
31794                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31795                     allowed.indexOf(String.fromCharCode(c)) === -1
31796             ){
31797                 e.stopEvent();
31798                 return;
31799             }
31800             
31801             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31802                 return;
31803             }
31804             
31805             if(allowed.indexOf(String.fromCharCode(c)) === -1){
31806                 e.stopEvent();
31807             }
31808         };
31809         
31810         this.el.on("keypress", keyPress, this);
31811     },
31812     
31813     validateValue : function(value)
31814     {
31815         
31816         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31817             return false;
31818         }
31819         
31820         var num = this.parseValue(value);
31821         
31822         if(isNaN(num)){
31823             this.markInvalid(String.format(this.nanText, value));
31824             return false;
31825         }
31826         
31827         if(num < this.minValue){
31828             this.markInvalid(String.format(this.minText, this.minValue));
31829             return false;
31830         }
31831         
31832         if(num > this.maxValue){
31833             this.markInvalid(String.format(this.maxText, this.maxValue));
31834             return false;
31835         }
31836         
31837         return true;
31838     },
31839
31840     getValue : function()
31841     {
31842         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31843     },
31844
31845     parseValue : function(value)
31846     {
31847         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31848         return isNaN(value) ? '' : value;
31849     },
31850
31851     fixPrecision : function(value)
31852     {
31853         var nan = isNaN(value);
31854         
31855         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31856             return nan ? '' : value;
31857         }
31858         return parseFloat(value).toFixed(this.decimalPrecision);
31859     },
31860
31861     setValue : function(v)
31862     {
31863         v = this.fixPrecision(v);
31864         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31865     },
31866
31867     decimalPrecisionFcn : function(v)
31868     {
31869         return Math.floor(v);
31870     },
31871
31872     beforeBlur : function()
31873     {
31874         if(!this.castInt){
31875             return;
31876         }
31877         
31878         var v = this.parseValue(this.getRawValue());
31879         if(v){
31880             this.setValue(v);
31881         }
31882     }
31883     
31884 });
31885
31886  
31887
31888 /*
31889 * Licence: LGPL
31890 */
31891
31892 /**
31893  * @class Roo.bootstrap.DocumentSlider
31894  * @extends Roo.bootstrap.Component
31895  * Bootstrap DocumentSlider class
31896  * 
31897  * @constructor
31898  * Create a new DocumentViewer
31899  * @param {Object} config The config object
31900  */
31901
31902 Roo.bootstrap.DocumentSlider = function(config){
31903     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31904     
31905     this.files = [];
31906     
31907     this.addEvents({
31908         /**
31909          * @event initial
31910          * Fire after initEvent
31911          * @param {Roo.bootstrap.DocumentSlider} this
31912          */
31913         "initial" : true,
31914         /**
31915          * @event update
31916          * Fire after update
31917          * @param {Roo.bootstrap.DocumentSlider} this
31918          */
31919         "update" : true,
31920         /**
31921          * @event click
31922          * Fire after click
31923          * @param {Roo.bootstrap.DocumentSlider} this
31924          */
31925         "click" : true
31926     });
31927 };
31928
31929 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
31930     
31931     files : false,
31932     
31933     indicator : 0,
31934     
31935     getAutoCreate : function()
31936     {
31937         var cfg = {
31938             tag : 'div',
31939             cls : 'roo-document-slider',
31940             cn : [
31941                 {
31942                     tag : 'div',
31943                     cls : 'roo-document-slider-header',
31944                     cn : [
31945                         {
31946                             tag : 'div',
31947                             cls : 'roo-document-slider-header-title'
31948                         }
31949                     ]
31950                 },
31951                 {
31952                     tag : 'div',
31953                     cls : 'roo-document-slider-body',
31954                     cn : [
31955                         {
31956                             tag : 'div',
31957                             cls : 'roo-document-slider-prev',
31958                             cn : [
31959                                 {
31960                                     tag : 'i',
31961                                     cls : 'fa fa-chevron-left'
31962                                 }
31963                             ]
31964                         },
31965                         {
31966                             tag : 'div',
31967                             cls : 'roo-document-slider-thumb',
31968                             cn : [
31969                                 {
31970                                     tag : 'img',
31971                                     cls : 'roo-document-slider-image'
31972                                 }
31973                             ]
31974                         },
31975                         {
31976                             tag : 'div',
31977                             cls : 'roo-document-slider-next',
31978                             cn : [
31979                                 {
31980                                     tag : 'i',
31981                                     cls : 'fa fa-chevron-right'
31982                                 }
31983                             ]
31984                         }
31985                     ]
31986                 }
31987             ]
31988         };
31989         
31990         return cfg;
31991     },
31992     
31993     initEvents : function()
31994     {
31995         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31996         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31997         
31998         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31999         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32000         
32001         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32002         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32003         
32004         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32005         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32006         
32007         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32008         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32009         
32010         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32011         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32012         
32013         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32014         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32015         
32016         this.thumbEl.on('click', this.onClick, this);
32017         
32018         this.prevIndicator.on('click', this.prev, this);
32019         
32020         this.nextIndicator.on('click', this.next, this);
32021         
32022     },
32023     
32024     initial : function()
32025     {
32026         if(this.files.length){
32027             this.indicator = 1;
32028             this.update()
32029         }
32030         
32031         this.fireEvent('initial', this);
32032     },
32033     
32034     update : function()
32035     {
32036         this.imageEl.attr('src', this.files[this.indicator - 1]);
32037         
32038         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32039         
32040         this.prevIndicator.show();
32041         
32042         if(this.indicator == 1){
32043             this.prevIndicator.hide();
32044         }
32045         
32046         this.nextIndicator.show();
32047         
32048         if(this.indicator == this.files.length){
32049             this.nextIndicator.hide();
32050         }
32051         
32052         this.thumbEl.scrollTo('top');
32053         
32054         this.fireEvent('update', this);
32055     },
32056     
32057     onClick : function(e)
32058     {
32059         e.preventDefault();
32060         
32061         this.fireEvent('click', this);
32062     },
32063     
32064     prev : function(e)
32065     {
32066         e.preventDefault();
32067         
32068         this.indicator = Math.max(1, this.indicator - 1);
32069         
32070         this.update();
32071     },
32072     
32073     next : function(e)
32074     {
32075         e.preventDefault();
32076         
32077         this.indicator = Math.min(this.files.length, this.indicator + 1);
32078         
32079         this.update();
32080     }
32081 });
32082 /*
32083  * Based on:
32084  * Ext JS Library 1.1.1
32085  * Copyright(c) 2006-2007, Ext JS, LLC.
32086  *
32087  * Originally Released Under LGPL - original licence link has changed is not relivant.
32088  *
32089  * Fork - LGPL
32090  * <script type="text/javascript">
32091  */
32092
32093
32094 /**
32095  * @class Roo.bootstrap.SplitBar
32096  * @extends Roo.util.Observable
32097  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32098  * <br><br>
32099  * Usage:
32100  * <pre><code>
32101 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32102                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32103 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32104 split.minSize = 100;
32105 split.maxSize = 600;
32106 split.animate = true;
32107 split.on('moved', splitterMoved);
32108 </code></pre>
32109  * @constructor
32110  * Create a new SplitBar
32111  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32112  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32113  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32114  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32115                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32116                         position of the SplitBar).
32117  */
32118 Roo.bootstrap.SplitBar = function(cfg){
32119     
32120     /** @private */
32121     
32122     //{
32123     //  dragElement : elm
32124     //  resizingElement: el,
32125         // optional..
32126     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32127     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32128         // existingProxy ???
32129     //}
32130     
32131     this.el = Roo.get(cfg.dragElement, true);
32132     this.el.dom.unselectable = "on";
32133     /** @private */
32134     this.resizingEl = Roo.get(cfg.resizingElement, true);
32135
32136     /**
32137      * @private
32138      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32139      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32140      * @type Number
32141      */
32142     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32143     
32144     /**
32145      * The minimum size of the resizing element. (Defaults to 0)
32146      * @type Number
32147      */
32148     this.minSize = 0;
32149     
32150     /**
32151      * The maximum size of the resizing element. (Defaults to 2000)
32152      * @type Number
32153      */
32154     this.maxSize = 2000;
32155     
32156     /**
32157      * Whether to animate the transition to the new size
32158      * @type Boolean
32159      */
32160     this.animate = false;
32161     
32162     /**
32163      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32164      * @type Boolean
32165      */
32166     this.useShim = false;
32167     
32168     /** @private */
32169     this.shim = null;
32170     
32171     if(!cfg.existingProxy){
32172         /** @private */
32173         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32174     }else{
32175         this.proxy = Roo.get(cfg.existingProxy).dom;
32176     }
32177     /** @private */
32178     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32179     
32180     /** @private */
32181     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32182     
32183     /** @private */
32184     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32185     
32186     /** @private */
32187     this.dragSpecs = {};
32188     
32189     /**
32190      * @private The adapter to use to positon and resize elements
32191      */
32192     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32193     this.adapter.init(this);
32194     
32195     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32196         /** @private */
32197         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32198         this.el.addClass("roo-splitbar-h");
32199     }else{
32200         /** @private */
32201         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32202         this.el.addClass("roo-splitbar-v");
32203     }
32204     
32205     this.addEvents({
32206         /**
32207          * @event resize
32208          * Fires when the splitter is moved (alias for {@link #event-moved})
32209          * @param {Roo.bootstrap.SplitBar} this
32210          * @param {Number} newSize the new width or height
32211          */
32212         "resize" : true,
32213         /**
32214          * @event moved
32215          * Fires when the splitter is moved
32216          * @param {Roo.bootstrap.SplitBar} this
32217          * @param {Number} newSize the new width or height
32218          */
32219         "moved" : true,
32220         /**
32221          * @event beforeresize
32222          * Fires before the splitter is dragged
32223          * @param {Roo.bootstrap.SplitBar} this
32224          */
32225         "beforeresize" : true,
32226
32227         "beforeapply" : true
32228     });
32229
32230     Roo.util.Observable.call(this);
32231 };
32232
32233 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32234     onStartProxyDrag : function(x, y){
32235         this.fireEvent("beforeresize", this);
32236         if(!this.overlay){
32237             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32238             o.unselectable();
32239             o.enableDisplayMode("block");
32240             // all splitbars share the same overlay
32241             Roo.bootstrap.SplitBar.prototype.overlay = o;
32242         }
32243         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32244         this.overlay.show();
32245         Roo.get(this.proxy).setDisplayed("block");
32246         var size = this.adapter.getElementSize(this);
32247         this.activeMinSize = this.getMinimumSize();;
32248         this.activeMaxSize = this.getMaximumSize();;
32249         var c1 = size - this.activeMinSize;
32250         var c2 = Math.max(this.activeMaxSize - size, 0);
32251         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32252             this.dd.resetConstraints();
32253             this.dd.setXConstraint(
32254                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32255                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32256             );
32257             this.dd.setYConstraint(0, 0);
32258         }else{
32259             this.dd.resetConstraints();
32260             this.dd.setXConstraint(0, 0);
32261             this.dd.setYConstraint(
32262                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32263                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32264             );
32265          }
32266         this.dragSpecs.startSize = size;
32267         this.dragSpecs.startPoint = [x, y];
32268         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32269     },
32270     
32271     /** 
32272      * @private Called after the drag operation by the DDProxy
32273      */
32274     onEndProxyDrag : function(e){
32275         Roo.get(this.proxy).setDisplayed(false);
32276         var endPoint = Roo.lib.Event.getXY(e);
32277         if(this.overlay){
32278             this.overlay.hide();
32279         }
32280         var newSize;
32281         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32282             newSize = this.dragSpecs.startSize + 
32283                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32284                     endPoint[0] - this.dragSpecs.startPoint[0] :
32285                     this.dragSpecs.startPoint[0] - endPoint[0]
32286                 );
32287         }else{
32288             newSize = this.dragSpecs.startSize + 
32289                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32290                     endPoint[1] - this.dragSpecs.startPoint[1] :
32291                     this.dragSpecs.startPoint[1] - endPoint[1]
32292                 );
32293         }
32294         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32295         if(newSize != this.dragSpecs.startSize){
32296             if(this.fireEvent('beforeapply', this, newSize) !== false){
32297                 this.adapter.setElementSize(this, newSize);
32298                 this.fireEvent("moved", this, newSize);
32299                 this.fireEvent("resize", this, newSize);
32300             }
32301         }
32302     },
32303     
32304     /**
32305      * Get the adapter this SplitBar uses
32306      * @return The adapter object
32307      */
32308     getAdapter : function(){
32309         return this.adapter;
32310     },
32311     
32312     /**
32313      * Set the adapter this SplitBar uses
32314      * @param {Object} adapter A SplitBar adapter object
32315      */
32316     setAdapter : function(adapter){
32317         this.adapter = adapter;
32318         this.adapter.init(this);
32319     },
32320     
32321     /**
32322      * Gets the minimum size for the resizing element
32323      * @return {Number} The minimum size
32324      */
32325     getMinimumSize : function(){
32326         return this.minSize;
32327     },
32328     
32329     /**
32330      * Sets the minimum size for the resizing element
32331      * @param {Number} minSize The minimum size
32332      */
32333     setMinimumSize : function(minSize){
32334         this.minSize = minSize;
32335     },
32336     
32337     /**
32338      * Gets the maximum size for the resizing element
32339      * @return {Number} The maximum size
32340      */
32341     getMaximumSize : function(){
32342         return this.maxSize;
32343     },
32344     
32345     /**
32346      * Sets the maximum size for the resizing element
32347      * @param {Number} maxSize The maximum size
32348      */
32349     setMaximumSize : function(maxSize){
32350         this.maxSize = maxSize;
32351     },
32352     
32353     /**
32354      * Sets the initialize size for the resizing element
32355      * @param {Number} size The initial size
32356      */
32357     setCurrentSize : function(size){
32358         var oldAnimate = this.animate;
32359         this.animate = false;
32360         this.adapter.setElementSize(this, size);
32361         this.animate = oldAnimate;
32362     },
32363     
32364     /**
32365      * Destroy this splitbar. 
32366      * @param {Boolean} removeEl True to remove the element
32367      */
32368     destroy : function(removeEl){
32369         if(this.shim){
32370             this.shim.remove();
32371         }
32372         this.dd.unreg();
32373         this.proxy.parentNode.removeChild(this.proxy);
32374         if(removeEl){
32375             this.el.remove();
32376         }
32377     }
32378 });
32379
32380 /**
32381  * @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.
32382  */
32383 Roo.bootstrap.SplitBar.createProxy = function(dir){
32384     var proxy = new Roo.Element(document.createElement("div"));
32385     proxy.unselectable();
32386     var cls = 'roo-splitbar-proxy';
32387     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32388     document.body.appendChild(proxy.dom);
32389     return proxy.dom;
32390 };
32391
32392 /** 
32393  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32394  * Default Adapter. It assumes the splitter and resizing element are not positioned
32395  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32396  */
32397 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32398 };
32399
32400 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32401     // do nothing for now
32402     init : function(s){
32403     
32404     },
32405     /**
32406      * Called before drag operations to get the current size of the resizing element. 
32407      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32408      */
32409      getElementSize : function(s){
32410         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32411             return s.resizingEl.getWidth();
32412         }else{
32413             return s.resizingEl.getHeight();
32414         }
32415     },
32416     
32417     /**
32418      * Called after drag operations to set the size of the resizing element.
32419      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32420      * @param {Number} newSize The new size to set
32421      * @param {Function} onComplete A function to be invoked when resizing is complete
32422      */
32423     setElementSize : function(s, newSize, onComplete){
32424         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32425             if(!s.animate){
32426                 s.resizingEl.setWidth(newSize);
32427                 if(onComplete){
32428                     onComplete(s, newSize);
32429                 }
32430             }else{
32431                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32432             }
32433         }else{
32434             
32435             if(!s.animate){
32436                 s.resizingEl.setHeight(newSize);
32437                 if(onComplete){
32438                     onComplete(s, newSize);
32439                 }
32440             }else{
32441                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32442             }
32443         }
32444     }
32445 };
32446
32447 /** 
32448  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32449  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32450  * Adapter that  moves the splitter element to align with the resized sizing element. 
32451  * Used with an absolute positioned SplitBar.
32452  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32453  * document.body, make sure you assign an id to the body element.
32454  */
32455 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32456     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32457     this.container = Roo.get(container);
32458 };
32459
32460 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32461     init : function(s){
32462         this.basic.init(s);
32463     },
32464     
32465     getElementSize : function(s){
32466         return this.basic.getElementSize(s);
32467     },
32468     
32469     setElementSize : function(s, newSize, onComplete){
32470         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32471     },
32472     
32473     moveSplitter : function(s){
32474         var yes = Roo.bootstrap.SplitBar;
32475         switch(s.placement){
32476             case yes.LEFT:
32477                 s.el.setX(s.resizingEl.getRight());
32478                 break;
32479             case yes.RIGHT:
32480                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32481                 break;
32482             case yes.TOP:
32483                 s.el.setY(s.resizingEl.getBottom());
32484                 break;
32485             case yes.BOTTOM:
32486                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32487                 break;
32488         }
32489     }
32490 };
32491
32492 /**
32493  * Orientation constant - Create a vertical SplitBar
32494  * @static
32495  * @type Number
32496  */
32497 Roo.bootstrap.SplitBar.VERTICAL = 1;
32498
32499 /**
32500  * Orientation constant - Create a horizontal SplitBar
32501  * @static
32502  * @type Number
32503  */
32504 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32505
32506 /**
32507  * Placement constant - The resizing element is to the left of the splitter element
32508  * @static
32509  * @type Number
32510  */
32511 Roo.bootstrap.SplitBar.LEFT = 1;
32512
32513 /**
32514  * Placement constant - The resizing element is to the right of the splitter element
32515  * @static
32516  * @type Number
32517  */
32518 Roo.bootstrap.SplitBar.RIGHT = 2;
32519
32520 /**
32521  * Placement constant - The resizing element is positioned above the splitter element
32522  * @static
32523  * @type Number
32524  */
32525 Roo.bootstrap.SplitBar.TOP = 3;
32526
32527 /**
32528  * Placement constant - The resizing element is positioned under splitter element
32529  * @static
32530  * @type Number
32531  */
32532 Roo.bootstrap.SplitBar.BOTTOM = 4;
32533 Roo.namespace("Roo.bootstrap.layout");/*
32534  * Based on:
32535  * Ext JS Library 1.1.1
32536  * Copyright(c) 2006-2007, Ext JS, LLC.
32537  *
32538  * Originally Released Under LGPL - original licence link has changed is not relivant.
32539  *
32540  * Fork - LGPL
32541  * <script type="text/javascript">
32542  */
32543
32544 /**
32545  * @class Roo.bootstrap.layout.Manager
32546  * @extends Roo.bootstrap.Component
32547  * Base class for layout managers.
32548  */
32549 Roo.bootstrap.layout.Manager = function(config)
32550 {
32551     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32552
32553
32554
32555
32556
32557     /** false to disable window resize monitoring @type Boolean */
32558     this.monitorWindowResize = true;
32559     this.regions = {};
32560     this.addEvents({
32561         /**
32562          * @event layout
32563          * Fires when a layout is performed.
32564          * @param {Roo.LayoutManager} this
32565          */
32566         "layout" : true,
32567         /**
32568          * @event regionresized
32569          * Fires when the user resizes a region.
32570          * @param {Roo.LayoutRegion} region The resized region
32571          * @param {Number} newSize The new size (width for east/west, height for north/south)
32572          */
32573         "regionresized" : true,
32574         /**
32575          * @event regioncollapsed
32576          * Fires when a region is collapsed.
32577          * @param {Roo.LayoutRegion} region The collapsed region
32578          */
32579         "regioncollapsed" : true,
32580         /**
32581          * @event regionexpanded
32582          * Fires when a region is expanded.
32583          * @param {Roo.LayoutRegion} region The expanded region
32584          */
32585         "regionexpanded" : true
32586     });
32587     this.updating = false;
32588
32589     if (config.el) {
32590         this.el = Roo.get(config.el);
32591         this.initEvents();
32592     }
32593
32594 };
32595
32596 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32597
32598
32599     regions : null,
32600
32601     monitorWindowResize : true,
32602
32603
32604     updating : false,
32605
32606
32607     onRender : function(ct, position)
32608     {
32609         if(!this.el){
32610             this.el = Roo.get(ct);
32611             this.initEvents();
32612         }
32613         //this.fireEvent('render',this);
32614     },
32615
32616
32617     initEvents: function()
32618     {
32619
32620
32621         // ie scrollbar fix
32622         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32623             document.body.scroll = "no";
32624         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32625             this.el.position('relative');
32626         }
32627         this.id = this.el.id;
32628         this.el.addClass("roo-layout-container");
32629         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32630         if(this.el.dom != document.body ) {
32631             this.el.on('resize', this.layout,this);
32632             this.el.on('show', this.layout,this);
32633         }
32634
32635     },
32636
32637     /**
32638      * Returns true if this layout is currently being updated
32639      * @return {Boolean}
32640      */
32641     isUpdating : function(){
32642         return this.updating;
32643     },
32644
32645     /**
32646      * Suspend the LayoutManager from doing auto-layouts while
32647      * making multiple add or remove calls
32648      */
32649     beginUpdate : function(){
32650         this.updating = true;
32651     },
32652
32653     /**
32654      * Restore auto-layouts and optionally disable the manager from performing a layout
32655      * @param {Boolean} noLayout true to disable a layout update
32656      */
32657     endUpdate : function(noLayout){
32658         this.updating = false;
32659         if(!noLayout){
32660             this.layout();
32661         }
32662     },
32663
32664     layout: function(){
32665         // abstract...
32666     },
32667
32668     onRegionResized : function(region, newSize){
32669         this.fireEvent("regionresized", region, newSize);
32670         this.layout();
32671     },
32672
32673     onRegionCollapsed : function(region){
32674         this.fireEvent("regioncollapsed", region);
32675     },
32676
32677     onRegionExpanded : function(region){
32678         this.fireEvent("regionexpanded", region);
32679     },
32680
32681     /**
32682      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32683      * performs box-model adjustments.
32684      * @return {Object} The size as an object {width: (the width), height: (the height)}
32685      */
32686     getViewSize : function()
32687     {
32688         var size;
32689         if(this.el.dom != document.body){
32690             size = this.el.getSize();
32691         }else{
32692             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32693         }
32694         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32695         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32696         return size;
32697     },
32698
32699     /**
32700      * Returns the Element this layout is bound to.
32701      * @return {Roo.Element}
32702      */
32703     getEl : function(){
32704         return this.el;
32705     },
32706
32707     /**
32708      * Returns the specified region.
32709      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32710      * @return {Roo.LayoutRegion}
32711      */
32712     getRegion : function(target){
32713         return this.regions[target.toLowerCase()];
32714     },
32715
32716     onWindowResize : function(){
32717         if(this.monitorWindowResize){
32718             this.layout();
32719         }
32720     }
32721 });
32722 /*
32723  * Based on:
32724  * Ext JS Library 1.1.1
32725  * Copyright(c) 2006-2007, Ext JS, LLC.
32726  *
32727  * Originally Released Under LGPL - original licence link has changed is not relivant.
32728  *
32729  * Fork - LGPL
32730  * <script type="text/javascript">
32731  */
32732 /**
32733  * @class Roo.bootstrap.layout.Border
32734  * @extends Roo.bootstrap.layout.Manager
32735  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32736  * please see: examples/bootstrap/nested.html<br><br>
32737  
32738 <b>The container the layout is rendered into can be either the body element or any other element.
32739 If it is not the body element, the container needs to either be an absolute positioned element,
32740 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32741 the container size if it is not the body element.</b>
32742
32743 * @constructor
32744 * Create a new Border
32745 * @param {Object} config Configuration options
32746  */
32747 Roo.bootstrap.layout.Border = function(config){
32748     config = config || {};
32749     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32750     
32751     
32752     
32753     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32754         if(config[region]){
32755             config[region].region = region;
32756             this.addRegion(config[region]);
32757         }
32758     },this);
32759     
32760 };
32761
32762 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32763
32764 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32765     /**
32766      * Creates and adds a new region if it doesn't already exist.
32767      * @param {String} target The target region key (north, south, east, west or center).
32768      * @param {Object} config The regions config object
32769      * @return {BorderLayoutRegion} The new region
32770      */
32771     addRegion : function(config)
32772     {
32773         if(!this.regions[config.region]){
32774             var r = this.factory(config);
32775             this.bindRegion(r);
32776         }
32777         return this.regions[config.region];
32778     },
32779
32780     // private (kinda)
32781     bindRegion : function(r){
32782         this.regions[r.config.region] = r;
32783         
32784         r.on("visibilitychange",    this.layout, this);
32785         r.on("paneladded",          this.layout, this);
32786         r.on("panelremoved",        this.layout, this);
32787         r.on("invalidated",         this.layout, this);
32788         r.on("resized",             this.onRegionResized, this);
32789         r.on("collapsed",           this.onRegionCollapsed, this);
32790         r.on("expanded",            this.onRegionExpanded, this);
32791     },
32792
32793     /**
32794      * Performs a layout update.
32795      */
32796     layout : function()
32797     {
32798         if(this.updating) {
32799             return;
32800         }
32801         
32802         // render all the rebions if they have not been done alreayd?
32803         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32804             if(this.regions[region] && !this.regions[region].bodyEl){
32805                 this.regions[region].onRender(this.el)
32806             }
32807         },this);
32808         
32809         var size = this.getViewSize();
32810         var w = size.width;
32811         var h = size.height;
32812         var centerW = w;
32813         var centerH = h;
32814         var centerY = 0;
32815         var centerX = 0;
32816         //var x = 0, y = 0;
32817
32818         var rs = this.regions;
32819         var north = rs["north"];
32820         var south = rs["south"]; 
32821         var west = rs["west"];
32822         var east = rs["east"];
32823         var center = rs["center"];
32824         //if(this.hideOnLayout){ // not supported anymore
32825             //c.el.setStyle("display", "none");
32826         //}
32827         if(north && north.isVisible()){
32828             var b = north.getBox();
32829             var m = north.getMargins();
32830             b.width = w - (m.left+m.right);
32831             b.x = m.left;
32832             b.y = m.top;
32833             centerY = b.height + b.y + m.bottom;
32834             centerH -= centerY;
32835             north.updateBox(this.safeBox(b));
32836         }
32837         if(south && south.isVisible()){
32838             var b = south.getBox();
32839             var m = south.getMargins();
32840             b.width = w - (m.left+m.right);
32841             b.x = m.left;
32842             var totalHeight = (b.height + m.top + m.bottom);
32843             b.y = h - totalHeight + m.top;
32844             centerH -= totalHeight;
32845             south.updateBox(this.safeBox(b));
32846         }
32847         if(west && west.isVisible()){
32848             var b = west.getBox();
32849             var m = west.getMargins();
32850             b.height = centerH - (m.top+m.bottom);
32851             b.x = m.left;
32852             b.y = centerY + m.top;
32853             var totalWidth = (b.width + m.left + m.right);
32854             centerX += totalWidth;
32855             centerW -= totalWidth;
32856             west.updateBox(this.safeBox(b));
32857         }
32858         if(east && east.isVisible()){
32859             var b = east.getBox();
32860             var m = east.getMargins();
32861             b.height = centerH - (m.top+m.bottom);
32862             var totalWidth = (b.width + m.left + m.right);
32863             b.x = w - totalWidth + m.left;
32864             b.y = centerY + m.top;
32865             centerW -= totalWidth;
32866             east.updateBox(this.safeBox(b));
32867         }
32868         if(center){
32869             var m = center.getMargins();
32870             var centerBox = {
32871                 x: centerX + m.left,
32872                 y: centerY + m.top,
32873                 width: centerW - (m.left+m.right),
32874                 height: centerH - (m.top+m.bottom)
32875             };
32876             //if(this.hideOnLayout){
32877                 //center.el.setStyle("display", "block");
32878             //}
32879             center.updateBox(this.safeBox(centerBox));
32880         }
32881         this.el.repaint();
32882         this.fireEvent("layout", this);
32883     },
32884
32885     // private
32886     safeBox : function(box){
32887         box.width = Math.max(0, box.width);
32888         box.height = Math.max(0, box.height);
32889         return box;
32890     },
32891
32892     /**
32893      * Adds a ContentPanel (or subclass) to this layout.
32894      * @param {String} target The target region key (north, south, east, west or center).
32895      * @param {Roo.ContentPanel} panel The panel to add
32896      * @return {Roo.ContentPanel} The added panel
32897      */
32898     add : function(target, panel){
32899          
32900         target = target.toLowerCase();
32901         return this.regions[target].add(panel);
32902     },
32903
32904     /**
32905      * Remove a ContentPanel (or subclass) to this layout.
32906      * @param {String} target The target region key (north, south, east, west or center).
32907      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32908      * @return {Roo.ContentPanel} The removed panel
32909      */
32910     remove : function(target, panel){
32911         target = target.toLowerCase();
32912         return this.regions[target].remove(panel);
32913     },
32914
32915     /**
32916      * Searches all regions for a panel with the specified id
32917      * @param {String} panelId
32918      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32919      */
32920     findPanel : function(panelId){
32921         var rs = this.regions;
32922         for(var target in rs){
32923             if(typeof rs[target] != "function"){
32924                 var p = rs[target].getPanel(panelId);
32925                 if(p){
32926                     return p;
32927                 }
32928             }
32929         }
32930         return null;
32931     },
32932
32933     /**
32934      * Searches all regions for a panel with the specified id and activates (shows) it.
32935      * @param {String/ContentPanel} panelId The panels id or the panel itself
32936      * @return {Roo.ContentPanel} The shown panel or null
32937      */
32938     showPanel : function(panelId) {
32939       var rs = this.regions;
32940       for(var target in rs){
32941          var r = rs[target];
32942          if(typeof r != "function"){
32943             if(r.hasPanel(panelId)){
32944                return r.showPanel(panelId);
32945             }
32946          }
32947       }
32948       return null;
32949    },
32950
32951    /**
32952      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32953      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32954      */
32955    /*
32956     restoreState : function(provider){
32957         if(!provider){
32958             provider = Roo.state.Manager;
32959         }
32960         var sm = new Roo.LayoutStateManager();
32961         sm.init(this, provider);
32962     },
32963 */
32964  
32965  
32966     /**
32967      * Adds a xtype elements to the layout.
32968      * <pre><code>
32969
32970 layout.addxtype({
32971        xtype : 'ContentPanel',
32972        region: 'west',
32973        items: [ .... ]
32974    }
32975 );
32976
32977 layout.addxtype({
32978         xtype : 'NestedLayoutPanel',
32979         region: 'west',
32980         layout: {
32981            center: { },
32982            west: { }   
32983         },
32984         items : [ ... list of content panels or nested layout panels.. ]
32985    }
32986 );
32987 </code></pre>
32988      * @param {Object} cfg Xtype definition of item to add.
32989      */
32990     addxtype : function(cfg)
32991     {
32992         // basically accepts a pannel...
32993         // can accept a layout region..!?!?
32994         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32995         
32996         
32997         // theory?  children can only be panels??
32998         
32999         //if (!cfg.xtype.match(/Panel$/)) {
33000         //    return false;
33001         //}
33002         var ret = false;
33003         
33004         if (typeof(cfg.region) == 'undefined') {
33005             Roo.log("Failed to add Panel, region was not set");
33006             Roo.log(cfg);
33007             return false;
33008         }
33009         var region = cfg.region;
33010         delete cfg.region;
33011         
33012           
33013         var xitems = [];
33014         if (cfg.items) {
33015             xitems = cfg.items;
33016             delete cfg.items;
33017         }
33018         var nb = false;
33019         
33020         switch(cfg.xtype) 
33021         {
33022             case 'Content':  // ContentPanel (el, cfg)
33023             case 'Scroll':  // ContentPanel (el, cfg)
33024             case 'View': 
33025                 cfg.autoCreate = true;
33026                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33027                 //} else {
33028                 //    var el = this.el.createChild();
33029                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33030                 //}
33031                 
33032                 this.add(region, ret);
33033                 break;
33034             
33035             /*
33036             case 'TreePanel': // our new panel!
33037                 cfg.el = this.el.createChild();
33038                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33039                 this.add(region, ret);
33040                 break;
33041             */
33042             
33043             case 'Nest': 
33044                 // create a new Layout (which is  a Border Layout...
33045                 
33046                 var clayout = cfg.layout;
33047                 clayout.el  = this.el.createChild();
33048                 clayout.items   = clayout.items  || [];
33049                 
33050                 delete cfg.layout;
33051                 
33052                 // replace this exitems with the clayout ones..
33053                 xitems = clayout.items;
33054                  
33055                 // force background off if it's in center...
33056                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33057                     cfg.background = false;
33058                 }
33059                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33060                 
33061                 
33062                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33063                 //console.log('adding nested layout panel '  + cfg.toSource());
33064                 this.add(region, ret);
33065                 nb = {}; /// find first...
33066                 break;
33067             
33068             case 'Grid':
33069                 
33070                 // needs grid and region
33071                 
33072                 //var el = this.getRegion(region).el.createChild();
33073                 /*
33074                  *var el = this.el.createChild();
33075                 // create the grid first...
33076                 cfg.grid.container = el;
33077                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33078                 */
33079                 
33080                 if (region == 'center' && this.active ) {
33081                     cfg.background = false;
33082                 }
33083                 
33084                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33085                 
33086                 this.add(region, ret);
33087                 /*
33088                 if (cfg.background) {
33089                     // render grid on panel activation (if panel background)
33090                     ret.on('activate', function(gp) {
33091                         if (!gp.grid.rendered) {
33092                     //        gp.grid.render(el);
33093                         }
33094                     });
33095                 } else {
33096                   //  cfg.grid.render(el);
33097                 }
33098                 */
33099                 break;
33100            
33101            
33102             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33103                 // it was the old xcomponent building that caused this before.
33104                 // espeically if border is the top element in the tree.
33105                 ret = this;
33106                 break; 
33107                 
33108                     
33109                 
33110                 
33111                 
33112             default:
33113                 /*
33114                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33115                     
33116                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33117                     this.add(region, ret);
33118                 } else {
33119                 */
33120                     Roo.log(cfg);
33121                     throw "Can not add '" + cfg.xtype + "' to Border";
33122                     return null;
33123              
33124                                 
33125              
33126         }
33127         this.beginUpdate();
33128         // add children..
33129         var region = '';
33130         var abn = {};
33131         Roo.each(xitems, function(i)  {
33132             region = nb && i.region ? i.region : false;
33133             
33134             var add = ret.addxtype(i);
33135            
33136             if (region) {
33137                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33138                 if (!i.background) {
33139                     abn[region] = nb[region] ;
33140                 }
33141             }
33142             
33143         });
33144         this.endUpdate();
33145
33146         // make the last non-background panel active..
33147         //if (nb) { Roo.log(abn); }
33148         if (nb) {
33149             
33150             for(var r in abn) {
33151                 region = this.getRegion(r);
33152                 if (region) {
33153                     // tried using nb[r], but it does not work..
33154                      
33155                     region.showPanel(abn[r]);
33156                    
33157                 }
33158             }
33159         }
33160         return ret;
33161         
33162     },
33163     
33164     
33165 // private
33166     factory : function(cfg)
33167     {
33168         
33169         var validRegions = Roo.bootstrap.layout.Border.regions;
33170
33171         var target = cfg.region;
33172         cfg.mgr = this;
33173         
33174         var r = Roo.bootstrap.layout;
33175         Roo.log(target);
33176         switch(target){
33177             case "north":
33178                 return new r.North(cfg);
33179             case "south":
33180                 return new r.South(cfg);
33181             case "east":
33182                 return new r.East(cfg);
33183             case "west":
33184                 return new r.West(cfg);
33185             case "center":
33186                 return new r.Center(cfg);
33187         }
33188         throw 'Layout region "'+target+'" not supported.';
33189     }
33190     
33191     
33192 });
33193  /*
33194  * Based on:
33195  * Ext JS Library 1.1.1
33196  * Copyright(c) 2006-2007, Ext JS, LLC.
33197  *
33198  * Originally Released Under LGPL - original licence link has changed is not relivant.
33199  *
33200  * Fork - LGPL
33201  * <script type="text/javascript">
33202  */
33203  
33204 /**
33205  * @class Roo.bootstrap.layout.Basic
33206  * @extends Roo.util.Observable
33207  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33208  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33209  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33210  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33211  * @cfg {string}   region  the region that it inhabits..
33212  * @cfg {bool}   skipConfig skip config?
33213  * 
33214
33215  */
33216 Roo.bootstrap.layout.Basic = function(config){
33217     
33218     this.mgr = config.mgr;
33219     
33220     this.position = config.region;
33221     
33222     var skipConfig = config.skipConfig;
33223     
33224     this.events = {
33225         /**
33226          * @scope Roo.BasicLayoutRegion
33227          */
33228         
33229         /**
33230          * @event beforeremove
33231          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33232          * @param {Roo.LayoutRegion} this
33233          * @param {Roo.ContentPanel} panel The panel
33234          * @param {Object} e The cancel event object
33235          */
33236         "beforeremove" : true,
33237         /**
33238          * @event invalidated
33239          * Fires when the layout for this region is changed.
33240          * @param {Roo.LayoutRegion} this
33241          */
33242         "invalidated" : true,
33243         /**
33244          * @event visibilitychange
33245          * Fires when this region is shown or hidden 
33246          * @param {Roo.LayoutRegion} this
33247          * @param {Boolean} visibility true or false
33248          */
33249         "visibilitychange" : true,
33250         /**
33251          * @event paneladded
33252          * Fires when a panel is added. 
33253          * @param {Roo.LayoutRegion} this
33254          * @param {Roo.ContentPanel} panel The panel
33255          */
33256         "paneladded" : true,
33257         /**
33258          * @event panelremoved
33259          * Fires when a panel is removed. 
33260          * @param {Roo.LayoutRegion} this
33261          * @param {Roo.ContentPanel} panel The panel
33262          */
33263         "panelremoved" : true,
33264         /**
33265          * @event beforecollapse
33266          * Fires when this region before collapse.
33267          * @param {Roo.LayoutRegion} this
33268          */
33269         "beforecollapse" : true,
33270         /**
33271          * @event collapsed
33272          * Fires when this region is collapsed.
33273          * @param {Roo.LayoutRegion} this
33274          */
33275         "collapsed" : true,
33276         /**
33277          * @event expanded
33278          * Fires when this region is expanded.
33279          * @param {Roo.LayoutRegion} this
33280          */
33281         "expanded" : true,
33282         /**
33283          * @event slideshow
33284          * Fires when this region is slid into view.
33285          * @param {Roo.LayoutRegion} this
33286          */
33287         "slideshow" : true,
33288         /**
33289          * @event slidehide
33290          * Fires when this region slides out of view. 
33291          * @param {Roo.LayoutRegion} this
33292          */
33293         "slidehide" : true,
33294         /**
33295          * @event panelactivated
33296          * Fires when a panel is activated. 
33297          * @param {Roo.LayoutRegion} this
33298          * @param {Roo.ContentPanel} panel The activated panel
33299          */
33300         "panelactivated" : true,
33301         /**
33302          * @event resized
33303          * Fires when the user resizes this region. 
33304          * @param {Roo.LayoutRegion} this
33305          * @param {Number} newSize The new size (width for east/west, height for north/south)
33306          */
33307         "resized" : true
33308     };
33309     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33310     this.panels = new Roo.util.MixedCollection();
33311     this.panels.getKey = this.getPanelId.createDelegate(this);
33312     this.box = null;
33313     this.activePanel = null;
33314     // ensure listeners are added...
33315     
33316     if (config.listeners || config.events) {
33317         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33318             listeners : config.listeners || {},
33319             events : config.events || {}
33320         });
33321     }
33322     
33323     if(skipConfig !== true){
33324         this.applyConfig(config);
33325     }
33326 };
33327
33328 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33329 {
33330     getPanelId : function(p){
33331         return p.getId();
33332     },
33333     
33334     applyConfig : function(config){
33335         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33336         this.config = config;
33337         
33338     },
33339     
33340     /**
33341      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33342      * the width, for horizontal (north, south) the height.
33343      * @param {Number} newSize The new width or height
33344      */
33345     resizeTo : function(newSize){
33346         var el = this.el ? this.el :
33347                  (this.activePanel ? this.activePanel.getEl() : null);
33348         if(el){
33349             switch(this.position){
33350                 case "east":
33351                 case "west":
33352                     el.setWidth(newSize);
33353                     this.fireEvent("resized", this, newSize);
33354                 break;
33355                 case "north":
33356                 case "south":
33357                     el.setHeight(newSize);
33358                     this.fireEvent("resized", this, newSize);
33359                 break;                
33360             }
33361         }
33362     },
33363     
33364     getBox : function(){
33365         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33366     },
33367     
33368     getMargins : function(){
33369         return this.margins;
33370     },
33371     
33372     updateBox : function(box){
33373         this.box = box;
33374         var el = this.activePanel.getEl();
33375         el.dom.style.left = box.x + "px";
33376         el.dom.style.top = box.y + "px";
33377         this.activePanel.setSize(box.width, box.height);
33378     },
33379     
33380     /**
33381      * Returns the container element for this region.
33382      * @return {Roo.Element}
33383      */
33384     getEl : function(){
33385         return this.activePanel;
33386     },
33387     
33388     /**
33389      * Returns true if this region is currently visible.
33390      * @return {Boolean}
33391      */
33392     isVisible : function(){
33393         return this.activePanel ? true : false;
33394     },
33395     
33396     setActivePanel : function(panel){
33397         panel = this.getPanel(panel);
33398         if(this.activePanel && this.activePanel != panel){
33399             this.activePanel.setActiveState(false);
33400             this.activePanel.getEl().setLeftTop(-10000,-10000);
33401         }
33402         this.activePanel = panel;
33403         panel.setActiveState(true);
33404         if(this.box){
33405             panel.setSize(this.box.width, this.box.height);
33406         }
33407         this.fireEvent("panelactivated", this, panel);
33408         this.fireEvent("invalidated");
33409     },
33410     
33411     /**
33412      * Show the specified panel.
33413      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33414      * @return {Roo.ContentPanel} The shown panel or null
33415      */
33416     showPanel : function(panel){
33417         panel = this.getPanel(panel);
33418         if(panel){
33419             this.setActivePanel(panel);
33420         }
33421         return panel;
33422     },
33423     
33424     /**
33425      * Get the active panel for this region.
33426      * @return {Roo.ContentPanel} The active panel or null
33427      */
33428     getActivePanel : function(){
33429         return this.activePanel;
33430     },
33431     
33432     /**
33433      * Add the passed ContentPanel(s)
33434      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33435      * @return {Roo.ContentPanel} The panel added (if only one was added)
33436      */
33437     add : function(panel){
33438         if(arguments.length > 1){
33439             for(var i = 0, len = arguments.length; i < len; i++) {
33440                 this.add(arguments[i]);
33441             }
33442             return null;
33443         }
33444         if(this.hasPanel(panel)){
33445             this.showPanel(panel);
33446             return panel;
33447         }
33448         var el = panel.getEl();
33449         if(el.dom.parentNode != this.mgr.el.dom){
33450             this.mgr.el.dom.appendChild(el.dom);
33451         }
33452         if(panel.setRegion){
33453             panel.setRegion(this);
33454         }
33455         this.panels.add(panel);
33456         el.setStyle("position", "absolute");
33457         if(!panel.background){
33458             this.setActivePanel(panel);
33459             if(this.config.initialSize && this.panels.getCount()==1){
33460                 this.resizeTo(this.config.initialSize);
33461             }
33462         }
33463         this.fireEvent("paneladded", this, panel);
33464         return panel;
33465     },
33466     
33467     /**
33468      * Returns true if the panel is in this region.
33469      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33470      * @return {Boolean}
33471      */
33472     hasPanel : function(panel){
33473         if(typeof panel == "object"){ // must be panel obj
33474             panel = panel.getId();
33475         }
33476         return this.getPanel(panel) ? true : false;
33477     },
33478     
33479     /**
33480      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33481      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33482      * @param {Boolean} preservePanel Overrides the config preservePanel option
33483      * @return {Roo.ContentPanel} The panel that was removed
33484      */
33485     remove : function(panel, preservePanel){
33486         panel = this.getPanel(panel);
33487         if(!panel){
33488             return null;
33489         }
33490         var e = {};
33491         this.fireEvent("beforeremove", this, panel, e);
33492         if(e.cancel === true){
33493             return null;
33494         }
33495         var panelId = panel.getId();
33496         this.panels.removeKey(panelId);
33497         return panel;
33498     },
33499     
33500     /**
33501      * Returns the panel specified or null if it's not in this region.
33502      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33503      * @return {Roo.ContentPanel}
33504      */
33505     getPanel : function(id){
33506         if(typeof id == "object"){ // must be panel obj
33507             return id;
33508         }
33509         return this.panels.get(id);
33510     },
33511     
33512     /**
33513      * Returns this regions position (north/south/east/west/center).
33514      * @return {String} 
33515      */
33516     getPosition: function(){
33517         return this.position;    
33518     }
33519 });/*
33520  * Based on:
33521  * Ext JS Library 1.1.1
33522  * Copyright(c) 2006-2007, Ext JS, LLC.
33523  *
33524  * Originally Released Under LGPL - original licence link has changed is not relivant.
33525  *
33526  * Fork - LGPL
33527  * <script type="text/javascript">
33528  */
33529  
33530 /**
33531  * @class Roo.bootstrap.layout.Region
33532  * @extends Roo.bootstrap.layout.Basic
33533  * This class represents a region in a layout manager.
33534  
33535  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33536  * @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})
33537  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33538  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33539  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33540  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33541  * @cfg {String}    title           The title for the region (overrides panel titles)
33542  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33543  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33544  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33545  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33546  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33547  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33548  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33549  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33550  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33551  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33552
33553  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33554  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33555  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33556  * @cfg {Number}    width           For East/West panels
33557  * @cfg {Number}    height          For North/South panels
33558  * @cfg {Boolean}   split           To show the splitter
33559  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33560  * 
33561  * @cfg {string}   cls             Extra CSS classes to add to region
33562  * 
33563  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33564  * @cfg {string}   region  the region that it inhabits..
33565  *
33566
33567  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33568  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33569
33570  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33571  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33572  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33573  */
33574 Roo.bootstrap.layout.Region = function(config)
33575 {
33576     this.applyConfig(config);
33577
33578     var mgr = config.mgr;
33579     var pos = config.region;
33580     config.skipConfig = true;
33581     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33582     
33583     if (mgr.el) {
33584         this.onRender(mgr.el);   
33585     }
33586      
33587     this.visible = true;
33588     this.collapsed = false;
33589     this.unrendered_panels = [];
33590 };
33591
33592 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33593
33594     position: '', // set by wrapper (eg. north/south etc..)
33595     unrendered_panels : null,  // unrendered panels.
33596     createBody : function(){
33597         /** This region's body element 
33598         * @type Roo.Element */
33599         this.bodyEl = this.el.createChild({
33600                 tag: "div",
33601                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33602         });
33603     },
33604
33605     onRender: function(ctr, pos)
33606     {
33607         var dh = Roo.DomHelper;
33608         /** This region's container element 
33609         * @type Roo.Element */
33610         this.el = dh.append(ctr.dom, {
33611                 tag: "div",
33612                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33613             }, true);
33614         /** This region's title element 
33615         * @type Roo.Element */
33616     
33617         this.titleEl = dh.append(this.el.dom,
33618             {
33619                     tag: "div",
33620                     unselectable: "on",
33621                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33622                     children:[
33623                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33624                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33625                     ]}, true);
33626         
33627         this.titleEl.enableDisplayMode();
33628         /** This region's title text element 
33629         * @type HTMLElement */
33630         this.titleTextEl = this.titleEl.dom.firstChild;
33631         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33632         /*
33633         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33634         this.closeBtn.enableDisplayMode();
33635         this.closeBtn.on("click", this.closeClicked, this);
33636         this.closeBtn.hide();
33637     */
33638         this.createBody(this.config);
33639         if(this.config.hideWhenEmpty){
33640             this.hide();
33641             this.on("paneladded", this.validateVisibility, this);
33642             this.on("panelremoved", this.validateVisibility, this);
33643         }
33644         if(this.autoScroll){
33645             this.bodyEl.setStyle("overflow", "auto");
33646         }else{
33647             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33648         }
33649         //if(c.titlebar !== false){
33650             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33651                 this.titleEl.hide();
33652             }else{
33653                 this.titleEl.show();
33654                 if(this.config.title){
33655                     this.titleTextEl.innerHTML = this.config.title;
33656                 }
33657             }
33658         //}
33659         if(this.config.collapsed){
33660             this.collapse(true);
33661         }
33662         if(this.config.hidden){
33663             this.hide();
33664         }
33665         
33666         if (this.unrendered_panels && this.unrendered_panels.length) {
33667             for (var i =0;i< this.unrendered_panels.length; i++) {
33668                 this.add(this.unrendered_panels[i]);
33669             }
33670             this.unrendered_panels = null;
33671             
33672         }
33673         
33674     },
33675     
33676     applyConfig : function(c)
33677     {
33678         /*
33679          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33680             var dh = Roo.DomHelper;
33681             if(c.titlebar !== false){
33682                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33683                 this.collapseBtn.on("click", this.collapse, this);
33684                 this.collapseBtn.enableDisplayMode();
33685                 /*
33686                 if(c.showPin === true || this.showPin){
33687                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33688                     this.stickBtn.enableDisplayMode();
33689                     this.stickBtn.on("click", this.expand, this);
33690                     this.stickBtn.hide();
33691                 }
33692                 
33693             }
33694             */
33695             /** This region's collapsed element
33696             * @type Roo.Element */
33697             /*
33698              *
33699             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33700                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33701             ]}, true);
33702             
33703             if(c.floatable !== false){
33704                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33705                this.collapsedEl.on("click", this.collapseClick, this);
33706             }
33707
33708             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33709                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33710                    id: "message", unselectable: "on", style:{"float":"left"}});
33711                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33712              }
33713             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33714             this.expandBtn.on("click", this.expand, this);
33715             
33716         }
33717         
33718         if(this.collapseBtn){
33719             this.collapseBtn.setVisible(c.collapsible == true);
33720         }
33721         
33722         this.cmargins = c.cmargins || this.cmargins ||
33723                          (this.position == "west" || this.position == "east" ?
33724                              {top: 0, left: 2, right:2, bottom: 0} :
33725                              {top: 2, left: 0, right:0, bottom: 2});
33726         */
33727         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33728         
33729         
33730         this.bottomTabs = c.tabPosition != "top";
33731         
33732         this.autoScroll = c.autoScroll || false;
33733         
33734         
33735        
33736         
33737         this.duration = c.duration || .30;
33738         this.slideDuration = c.slideDuration || .45;
33739         this.config = c;
33740        
33741     },
33742     /**
33743      * Returns true if this region is currently visible.
33744      * @return {Boolean}
33745      */
33746     isVisible : function(){
33747         return this.visible;
33748     },
33749
33750     /**
33751      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33752      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33753      */
33754     //setCollapsedTitle : function(title){
33755     //    title = title || "&#160;";
33756      //   if(this.collapsedTitleTextEl){
33757       //      this.collapsedTitleTextEl.innerHTML = title;
33758        // }
33759     //},
33760
33761     getBox : function(){
33762         var b;
33763       //  if(!this.collapsed){
33764             b = this.el.getBox(false, true);
33765        // }else{
33766           //  b = this.collapsedEl.getBox(false, true);
33767         //}
33768         return b;
33769     },
33770
33771     getMargins : function(){
33772         return this.margins;
33773         //return this.collapsed ? this.cmargins : this.margins;
33774     },
33775 /*
33776     highlight : function(){
33777         this.el.addClass("x-layout-panel-dragover");
33778     },
33779
33780     unhighlight : function(){
33781         this.el.removeClass("x-layout-panel-dragover");
33782     },
33783 */
33784     updateBox : function(box)
33785     {
33786         if (!this.bodyEl) {
33787             return; // not rendered yet..
33788         }
33789         
33790         this.box = box;
33791         if(!this.collapsed){
33792             this.el.dom.style.left = box.x + "px";
33793             this.el.dom.style.top = box.y + "px";
33794             this.updateBody(box.width, box.height);
33795         }else{
33796             this.collapsedEl.dom.style.left = box.x + "px";
33797             this.collapsedEl.dom.style.top = box.y + "px";
33798             this.collapsedEl.setSize(box.width, box.height);
33799         }
33800         if(this.tabs){
33801             this.tabs.autoSizeTabs();
33802         }
33803     },
33804
33805     updateBody : function(w, h)
33806     {
33807         if(w !== null){
33808             this.el.setWidth(w);
33809             w -= this.el.getBorderWidth("rl");
33810             if(this.config.adjustments){
33811                 w += this.config.adjustments[0];
33812             }
33813         }
33814         if(h !== null && h > 0){
33815             this.el.setHeight(h);
33816             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33817             h -= this.el.getBorderWidth("tb");
33818             if(this.config.adjustments){
33819                 h += this.config.adjustments[1];
33820             }
33821             this.bodyEl.setHeight(h);
33822             if(this.tabs){
33823                 h = this.tabs.syncHeight(h);
33824             }
33825         }
33826         if(this.panelSize){
33827             w = w !== null ? w : this.panelSize.width;
33828             h = h !== null ? h : this.panelSize.height;
33829         }
33830         if(this.activePanel){
33831             var el = this.activePanel.getEl();
33832             w = w !== null ? w : el.getWidth();
33833             h = h !== null ? h : el.getHeight();
33834             this.panelSize = {width: w, height: h};
33835             this.activePanel.setSize(w, h);
33836         }
33837         if(Roo.isIE && this.tabs){
33838             this.tabs.el.repaint();
33839         }
33840     },
33841
33842     /**
33843      * Returns the container element for this region.
33844      * @return {Roo.Element}
33845      */
33846     getEl : function(){
33847         return this.el;
33848     },
33849
33850     /**
33851      * Hides this region.
33852      */
33853     hide : function(){
33854         //if(!this.collapsed){
33855             this.el.dom.style.left = "-2000px";
33856             this.el.hide();
33857         //}else{
33858          //   this.collapsedEl.dom.style.left = "-2000px";
33859          //   this.collapsedEl.hide();
33860        // }
33861         this.visible = false;
33862         this.fireEvent("visibilitychange", this, false);
33863     },
33864
33865     /**
33866      * Shows this region if it was previously hidden.
33867      */
33868     show : function(){
33869         //if(!this.collapsed){
33870             this.el.show();
33871         //}else{
33872         //    this.collapsedEl.show();
33873        // }
33874         this.visible = true;
33875         this.fireEvent("visibilitychange", this, true);
33876     },
33877 /*
33878     closeClicked : function(){
33879         if(this.activePanel){
33880             this.remove(this.activePanel);
33881         }
33882     },
33883
33884     collapseClick : function(e){
33885         if(this.isSlid){
33886            e.stopPropagation();
33887            this.slideIn();
33888         }else{
33889            e.stopPropagation();
33890            this.slideOut();
33891         }
33892     },
33893 */
33894     /**
33895      * Collapses this region.
33896      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33897      */
33898     /*
33899     collapse : function(skipAnim, skipCheck = false){
33900         if(this.collapsed) {
33901             return;
33902         }
33903         
33904         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33905             
33906             this.collapsed = true;
33907             if(this.split){
33908                 this.split.el.hide();
33909             }
33910             if(this.config.animate && skipAnim !== true){
33911                 this.fireEvent("invalidated", this);
33912                 this.animateCollapse();
33913             }else{
33914                 this.el.setLocation(-20000,-20000);
33915                 this.el.hide();
33916                 this.collapsedEl.show();
33917                 this.fireEvent("collapsed", this);
33918                 this.fireEvent("invalidated", this);
33919             }
33920         }
33921         
33922     },
33923 */
33924     animateCollapse : function(){
33925         // overridden
33926     },
33927
33928     /**
33929      * Expands this region if it was previously collapsed.
33930      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33931      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33932      */
33933     /*
33934     expand : function(e, skipAnim){
33935         if(e) {
33936             e.stopPropagation();
33937         }
33938         if(!this.collapsed || this.el.hasActiveFx()) {
33939             return;
33940         }
33941         if(this.isSlid){
33942             this.afterSlideIn();
33943             skipAnim = true;
33944         }
33945         this.collapsed = false;
33946         if(this.config.animate && skipAnim !== true){
33947             this.animateExpand();
33948         }else{
33949             this.el.show();
33950             if(this.split){
33951                 this.split.el.show();
33952             }
33953             this.collapsedEl.setLocation(-2000,-2000);
33954             this.collapsedEl.hide();
33955             this.fireEvent("invalidated", this);
33956             this.fireEvent("expanded", this);
33957         }
33958     },
33959 */
33960     animateExpand : function(){
33961         // overridden
33962     },
33963
33964     initTabs : function()
33965     {
33966         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33967         
33968         var ts = new Roo.bootstrap.panel.Tabs({
33969                 el: this.bodyEl.dom,
33970                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33971                 disableTooltips: this.config.disableTabTips,
33972                 toolbar : this.config.toolbar
33973             });
33974         
33975         if(this.config.hideTabs){
33976             ts.stripWrap.setDisplayed(false);
33977         }
33978         this.tabs = ts;
33979         ts.resizeTabs = this.config.resizeTabs === true;
33980         ts.minTabWidth = this.config.minTabWidth || 40;
33981         ts.maxTabWidth = this.config.maxTabWidth || 250;
33982         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33983         ts.monitorResize = false;
33984         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33985         ts.bodyEl.addClass('roo-layout-tabs-body');
33986         this.panels.each(this.initPanelAsTab, this);
33987     },
33988
33989     initPanelAsTab : function(panel){
33990         var ti = this.tabs.addTab(
33991             panel.getEl().id,
33992             panel.getTitle(),
33993             null,
33994             this.config.closeOnTab && panel.isClosable(),
33995             panel.tpl
33996         );
33997         if(panel.tabTip !== undefined){
33998             ti.setTooltip(panel.tabTip);
33999         }
34000         ti.on("activate", function(){
34001               this.setActivePanel(panel);
34002         }, this);
34003         
34004         if(this.config.closeOnTab){
34005             ti.on("beforeclose", function(t, e){
34006                 e.cancel = true;
34007                 this.remove(panel);
34008             }, this);
34009         }
34010         
34011         panel.tabItem = ti;
34012         
34013         return ti;
34014     },
34015
34016     updatePanelTitle : function(panel, title)
34017     {
34018         if(this.activePanel == panel){
34019             this.updateTitle(title);
34020         }
34021         if(this.tabs){
34022             var ti = this.tabs.getTab(panel.getEl().id);
34023             ti.setText(title);
34024             if(panel.tabTip !== undefined){
34025                 ti.setTooltip(panel.tabTip);
34026             }
34027         }
34028     },
34029
34030     updateTitle : function(title){
34031         if(this.titleTextEl && !this.config.title){
34032             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34033         }
34034     },
34035
34036     setActivePanel : function(panel)
34037     {
34038         panel = this.getPanel(panel);
34039         if(this.activePanel && this.activePanel != panel){
34040             this.activePanel.setActiveState(false);
34041         }
34042         this.activePanel = panel;
34043         panel.setActiveState(true);
34044         if(this.panelSize){
34045             panel.setSize(this.panelSize.width, this.panelSize.height);
34046         }
34047         if(this.closeBtn){
34048             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34049         }
34050         this.updateTitle(panel.getTitle());
34051         if(this.tabs){
34052             this.fireEvent("invalidated", this);
34053         }
34054         this.fireEvent("panelactivated", this, panel);
34055     },
34056
34057     /**
34058      * Shows the specified panel.
34059      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34060      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34061      */
34062     showPanel : function(panel)
34063     {
34064         panel = this.getPanel(panel);
34065         if(panel){
34066             if(this.tabs){
34067                 var tab = this.tabs.getTab(panel.getEl().id);
34068                 if(tab.isHidden()){
34069                     this.tabs.unhideTab(tab.id);
34070                 }
34071                 tab.activate();
34072             }else{
34073                 this.setActivePanel(panel);
34074             }
34075         }
34076         return panel;
34077     },
34078
34079     /**
34080      * Get the active panel for this region.
34081      * @return {Roo.ContentPanel} The active panel or null
34082      */
34083     getActivePanel : function(){
34084         return this.activePanel;
34085     },
34086
34087     validateVisibility : function(){
34088         if(this.panels.getCount() < 1){
34089             this.updateTitle("&#160;");
34090             this.closeBtn.hide();
34091             this.hide();
34092         }else{
34093             if(!this.isVisible()){
34094                 this.show();
34095             }
34096         }
34097     },
34098
34099     /**
34100      * Adds the passed ContentPanel(s) to this region.
34101      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34102      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34103      */
34104     add : function(panel)
34105     {
34106         if(arguments.length > 1){
34107             for(var i = 0, len = arguments.length; i < len; i++) {
34108                 this.add(arguments[i]);
34109             }
34110             return null;
34111         }
34112         
34113         // if we have not been rendered yet, then we can not really do much of this..
34114         if (!this.bodyEl) {
34115             this.unrendered_panels.push(panel);
34116             return panel;
34117         }
34118         
34119         
34120         
34121         
34122         if(this.hasPanel(panel)){
34123             this.showPanel(panel);
34124             return panel;
34125         }
34126         panel.setRegion(this);
34127         this.panels.add(panel);
34128        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34129             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34130             // and hide them... ???
34131             this.bodyEl.dom.appendChild(panel.getEl().dom);
34132             if(panel.background !== true){
34133                 this.setActivePanel(panel);
34134             }
34135             this.fireEvent("paneladded", this, panel);
34136             return panel;
34137         }
34138         */
34139         if(!this.tabs){
34140             this.initTabs();
34141         }else{
34142             this.initPanelAsTab(panel);
34143         }
34144         
34145         
34146         if(panel.background !== true){
34147             this.tabs.activate(panel.getEl().id);
34148         }
34149         this.fireEvent("paneladded", this, panel);
34150         return panel;
34151     },
34152
34153     /**
34154      * Hides the tab for the specified panel.
34155      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34156      */
34157     hidePanel : function(panel){
34158         if(this.tabs && (panel = this.getPanel(panel))){
34159             this.tabs.hideTab(panel.getEl().id);
34160         }
34161     },
34162
34163     /**
34164      * Unhides the tab for a previously hidden panel.
34165      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34166      */
34167     unhidePanel : function(panel){
34168         if(this.tabs && (panel = this.getPanel(panel))){
34169             this.tabs.unhideTab(panel.getEl().id);
34170         }
34171     },
34172
34173     clearPanels : function(){
34174         while(this.panels.getCount() > 0){
34175              this.remove(this.panels.first());
34176         }
34177     },
34178
34179     /**
34180      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34181      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34182      * @param {Boolean} preservePanel Overrides the config preservePanel option
34183      * @return {Roo.ContentPanel} The panel that was removed
34184      */
34185     remove : function(panel, preservePanel)
34186     {
34187         panel = this.getPanel(panel);
34188         if(!panel){
34189             return null;
34190         }
34191         var e = {};
34192         this.fireEvent("beforeremove", this, panel, e);
34193         if(e.cancel === true){
34194             return null;
34195         }
34196         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34197         var panelId = panel.getId();
34198         this.panels.removeKey(panelId);
34199         if(preservePanel){
34200             document.body.appendChild(panel.getEl().dom);
34201         }
34202         if(this.tabs){
34203             this.tabs.removeTab(panel.getEl().id);
34204         }else if (!preservePanel){
34205             this.bodyEl.dom.removeChild(panel.getEl().dom);
34206         }
34207         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34208             var p = this.panels.first();
34209             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34210             tempEl.appendChild(p.getEl().dom);
34211             this.bodyEl.update("");
34212             this.bodyEl.dom.appendChild(p.getEl().dom);
34213             tempEl = null;
34214             this.updateTitle(p.getTitle());
34215             this.tabs = null;
34216             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34217             this.setActivePanel(p);
34218         }
34219         panel.setRegion(null);
34220         if(this.activePanel == panel){
34221             this.activePanel = null;
34222         }
34223         if(this.config.autoDestroy !== false && preservePanel !== true){
34224             try{panel.destroy();}catch(e){}
34225         }
34226         this.fireEvent("panelremoved", this, panel);
34227         return panel;
34228     },
34229
34230     /**
34231      * Returns the TabPanel component used by this region
34232      * @return {Roo.TabPanel}
34233      */
34234     getTabs : function(){
34235         return this.tabs;
34236     },
34237
34238     createTool : function(parentEl, className){
34239         var btn = Roo.DomHelper.append(parentEl, {
34240             tag: "div",
34241             cls: "x-layout-tools-button",
34242             children: [ {
34243                 tag: "div",
34244                 cls: "roo-layout-tools-button-inner " + className,
34245                 html: "&#160;"
34246             }]
34247         }, true);
34248         btn.addClassOnOver("roo-layout-tools-button-over");
34249         return btn;
34250     }
34251 });/*
34252  * Based on:
34253  * Ext JS Library 1.1.1
34254  * Copyright(c) 2006-2007, Ext JS, LLC.
34255  *
34256  * Originally Released Under LGPL - original licence link has changed is not relivant.
34257  *
34258  * Fork - LGPL
34259  * <script type="text/javascript">
34260  */
34261  
34262
34263
34264 /**
34265  * @class Roo.SplitLayoutRegion
34266  * @extends Roo.LayoutRegion
34267  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34268  */
34269 Roo.bootstrap.layout.Split = function(config){
34270     this.cursor = config.cursor;
34271     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34272 };
34273
34274 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34275 {
34276     splitTip : "Drag to resize.",
34277     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34278     useSplitTips : false,
34279
34280     applyConfig : function(config){
34281         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34282     },
34283     
34284     onRender : function(ctr,pos) {
34285         
34286         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34287         if(!this.config.split){
34288             return;
34289         }
34290         if(!this.split){
34291             
34292             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34293                             tag: "div",
34294                             id: this.el.id + "-split",
34295                             cls: "roo-layout-split roo-layout-split-"+this.position,
34296                             html: "&#160;"
34297             });
34298             /** The SplitBar for this region 
34299             * @type Roo.SplitBar */
34300             // does not exist yet...
34301             Roo.log([this.position, this.orientation]);
34302             
34303             this.split = new Roo.bootstrap.SplitBar({
34304                 dragElement : splitEl,
34305                 resizingElement: this.el,
34306                 orientation : this.orientation
34307             });
34308             
34309             this.split.on("moved", this.onSplitMove, this);
34310             this.split.useShim = this.config.useShim === true;
34311             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34312             if(this.useSplitTips){
34313                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34314             }
34315             //if(config.collapsible){
34316             //    this.split.el.on("dblclick", this.collapse,  this);
34317             //}
34318         }
34319         if(typeof this.config.minSize != "undefined"){
34320             this.split.minSize = this.config.minSize;
34321         }
34322         if(typeof this.config.maxSize != "undefined"){
34323             this.split.maxSize = this.config.maxSize;
34324         }
34325         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34326             this.hideSplitter();
34327         }
34328         
34329     },
34330
34331     getHMaxSize : function(){
34332          var cmax = this.config.maxSize || 10000;
34333          var center = this.mgr.getRegion("center");
34334          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34335     },
34336
34337     getVMaxSize : function(){
34338          var cmax = this.config.maxSize || 10000;
34339          var center = this.mgr.getRegion("center");
34340          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34341     },
34342
34343     onSplitMove : function(split, newSize){
34344         this.fireEvent("resized", this, newSize);
34345     },
34346     
34347     /** 
34348      * Returns the {@link Roo.SplitBar} for this region.
34349      * @return {Roo.SplitBar}
34350      */
34351     getSplitBar : function(){
34352         return this.split;
34353     },
34354     
34355     hide : function(){
34356         this.hideSplitter();
34357         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34358     },
34359
34360     hideSplitter : function(){
34361         if(this.split){
34362             this.split.el.setLocation(-2000,-2000);
34363             this.split.el.hide();
34364         }
34365     },
34366
34367     show : function(){
34368         if(this.split){
34369             this.split.el.show();
34370         }
34371         Roo.bootstrap.layout.Split.superclass.show.call(this);
34372     },
34373     
34374     beforeSlide: function(){
34375         if(Roo.isGecko){// firefox overflow auto bug workaround
34376             this.bodyEl.clip();
34377             if(this.tabs) {
34378                 this.tabs.bodyEl.clip();
34379             }
34380             if(this.activePanel){
34381                 this.activePanel.getEl().clip();
34382                 
34383                 if(this.activePanel.beforeSlide){
34384                     this.activePanel.beforeSlide();
34385                 }
34386             }
34387         }
34388     },
34389     
34390     afterSlide : function(){
34391         if(Roo.isGecko){// firefox overflow auto bug workaround
34392             this.bodyEl.unclip();
34393             if(this.tabs) {
34394                 this.tabs.bodyEl.unclip();
34395             }
34396             if(this.activePanel){
34397                 this.activePanel.getEl().unclip();
34398                 if(this.activePanel.afterSlide){
34399                     this.activePanel.afterSlide();
34400                 }
34401             }
34402         }
34403     },
34404
34405     initAutoHide : function(){
34406         if(this.autoHide !== false){
34407             if(!this.autoHideHd){
34408                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34409                 this.autoHideHd = {
34410                     "mouseout": function(e){
34411                         if(!e.within(this.el, true)){
34412                             st.delay(500);
34413                         }
34414                     },
34415                     "mouseover" : function(e){
34416                         st.cancel();
34417                     },
34418                     scope : this
34419                 };
34420             }
34421             this.el.on(this.autoHideHd);
34422         }
34423     },
34424
34425     clearAutoHide : function(){
34426         if(this.autoHide !== false){
34427             this.el.un("mouseout", this.autoHideHd.mouseout);
34428             this.el.un("mouseover", this.autoHideHd.mouseover);
34429         }
34430     },
34431
34432     clearMonitor : function(){
34433         Roo.get(document).un("click", this.slideInIf, this);
34434     },
34435
34436     // these names are backwards but not changed for compat
34437     slideOut : function(){
34438         if(this.isSlid || this.el.hasActiveFx()){
34439             return;
34440         }
34441         this.isSlid = true;
34442         if(this.collapseBtn){
34443             this.collapseBtn.hide();
34444         }
34445         this.closeBtnState = this.closeBtn.getStyle('display');
34446         this.closeBtn.hide();
34447         if(this.stickBtn){
34448             this.stickBtn.show();
34449         }
34450         this.el.show();
34451         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34452         this.beforeSlide();
34453         this.el.setStyle("z-index", 10001);
34454         this.el.slideIn(this.getSlideAnchor(), {
34455             callback: function(){
34456                 this.afterSlide();
34457                 this.initAutoHide();
34458                 Roo.get(document).on("click", this.slideInIf, this);
34459                 this.fireEvent("slideshow", this);
34460             },
34461             scope: this,
34462             block: true
34463         });
34464     },
34465
34466     afterSlideIn : function(){
34467         this.clearAutoHide();
34468         this.isSlid = false;
34469         this.clearMonitor();
34470         this.el.setStyle("z-index", "");
34471         if(this.collapseBtn){
34472             this.collapseBtn.show();
34473         }
34474         this.closeBtn.setStyle('display', this.closeBtnState);
34475         if(this.stickBtn){
34476             this.stickBtn.hide();
34477         }
34478         this.fireEvent("slidehide", this);
34479     },
34480
34481     slideIn : function(cb){
34482         if(!this.isSlid || this.el.hasActiveFx()){
34483             Roo.callback(cb);
34484             return;
34485         }
34486         this.isSlid = false;
34487         this.beforeSlide();
34488         this.el.slideOut(this.getSlideAnchor(), {
34489             callback: function(){
34490                 this.el.setLeftTop(-10000, -10000);
34491                 this.afterSlide();
34492                 this.afterSlideIn();
34493                 Roo.callback(cb);
34494             },
34495             scope: this,
34496             block: true
34497         });
34498     },
34499     
34500     slideInIf : function(e){
34501         if(!e.within(this.el)){
34502             this.slideIn();
34503         }
34504     },
34505
34506     animateCollapse : function(){
34507         this.beforeSlide();
34508         this.el.setStyle("z-index", 20000);
34509         var anchor = this.getSlideAnchor();
34510         this.el.slideOut(anchor, {
34511             callback : function(){
34512                 this.el.setStyle("z-index", "");
34513                 this.collapsedEl.slideIn(anchor, {duration:.3});
34514                 this.afterSlide();
34515                 this.el.setLocation(-10000,-10000);
34516                 this.el.hide();
34517                 this.fireEvent("collapsed", this);
34518             },
34519             scope: this,
34520             block: true
34521         });
34522     },
34523
34524     animateExpand : function(){
34525         this.beforeSlide();
34526         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34527         this.el.setStyle("z-index", 20000);
34528         this.collapsedEl.hide({
34529             duration:.1
34530         });
34531         this.el.slideIn(this.getSlideAnchor(), {
34532             callback : function(){
34533                 this.el.setStyle("z-index", "");
34534                 this.afterSlide();
34535                 if(this.split){
34536                     this.split.el.show();
34537                 }
34538                 this.fireEvent("invalidated", this);
34539                 this.fireEvent("expanded", this);
34540             },
34541             scope: this,
34542             block: true
34543         });
34544     },
34545
34546     anchors : {
34547         "west" : "left",
34548         "east" : "right",
34549         "north" : "top",
34550         "south" : "bottom"
34551     },
34552
34553     sanchors : {
34554         "west" : "l",
34555         "east" : "r",
34556         "north" : "t",
34557         "south" : "b"
34558     },
34559
34560     canchors : {
34561         "west" : "tl-tr",
34562         "east" : "tr-tl",
34563         "north" : "tl-bl",
34564         "south" : "bl-tl"
34565     },
34566
34567     getAnchor : function(){
34568         return this.anchors[this.position];
34569     },
34570
34571     getCollapseAnchor : function(){
34572         return this.canchors[this.position];
34573     },
34574
34575     getSlideAnchor : function(){
34576         return this.sanchors[this.position];
34577     },
34578
34579     getAlignAdj : function(){
34580         var cm = this.cmargins;
34581         switch(this.position){
34582             case "west":
34583                 return [0, 0];
34584             break;
34585             case "east":
34586                 return [0, 0];
34587             break;
34588             case "north":
34589                 return [0, 0];
34590             break;
34591             case "south":
34592                 return [0, 0];
34593             break;
34594         }
34595     },
34596
34597     getExpandAdj : function(){
34598         var c = this.collapsedEl, cm = this.cmargins;
34599         switch(this.position){
34600             case "west":
34601                 return [-(cm.right+c.getWidth()+cm.left), 0];
34602             break;
34603             case "east":
34604                 return [cm.right+c.getWidth()+cm.left, 0];
34605             break;
34606             case "north":
34607                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34608             break;
34609             case "south":
34610                 return [0, cm.top+cm.bottom+c.getHeight()];
34611             break;
34612         }
34613     }
34614 });/*
34615  * Based on:
34616  * Ext JS Library 1.1.1
34617  * Copyright(c) 2006-2007, Ext JS, LLC.
34618  *
34619  * Originally Released Under LGPL - original licence link has changed is not relivant.
34620  *
34621  * Fork - LGPL
34622  * <script type="text/javascript">
34623  */
34624 /*
34625  * These classes are private internal classes
34626  */
34627 Roo.bootstrap.layout.Center = function(config){
34628     config.region = "center";
34629     Roo.bootstrap.layout.Region.call(this, config);
34630     this.visible = true;
34631     this.minWidth = config.minWidth || 20;
34632     this.minHeight = config.minHeight || 20;
34633 };
34634
34635 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34636     hide : function(){
34637         // center panel can't be hidden
34638     },
34639     
34640     show : function(){
34641         // center panel can't be hidden
34642     },
34643     
34644     getMinWidth: function(){
34645         return this.minWidth;
34646     },
34647     
34648     getMinHeight: function(){
34649         return this.minHeight;
34650     }
34651 });
34652
34653
34654
34655
34656  
34657
34658
34659
34660
34661
34662 Roo.bootstrap.layout.North = function(config)
34663 {
34664     config.region = 'north';
34665     config.cursor = 'n-resize';
34666     
34667     Roo.bootstrap.layout.Split.call(this, config);
34668     
34669     
34670     if(this.split){
34671         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34672         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34673         this.split.el.addClass("roo-layout-split-v");
34674     }
34675     var size = config.initialSize || config.height;
34676     if(typeof size != "undefined"){
34677         this.el.setHeight(size);
34678     }
34679 };
34680 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34681 {
34682     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34683     
34684     
34685     
34686     getBox : function(){
34687         if(this.collapsed){
34688             return this.collapsedEl.getBox();
34689         }
34690         var box = this.el.getBox();
34691         if(this.split){
34692             box.height += this.split.el.getHeight();
34693         }
34694         return box;
34695     },
34696     
34697     updateBox : function(box){
34698         if(this.split && !this.collapsed){
34699             box.height -= this.split.el.getHeight();
34700             this.split.el.setLeft(box.x);
34701             this.split.el.setTop(box.y+box.height);
34702             this.split.el.setWidth(box.width);
34703         }
34704         if(this.collapsed){
34705             this.updateBody(box.width, null);
34706         }
34707         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34708     }
34709 });
34710
34711
34712
34713
34714
34715 Roo.bootstrap.layout.South = function(config){
34716     config.region = 'south';
34717     config.cursor = 's-resize';
34718     Roo.bootstrap.layout.Split.call(this, config);
34719     if(this.split){
34720         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34721         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34722         this.split.el.addClass("roo-layout-split-v");
34723     }
34724     var size = config.initialSize || config.height;
34725     if(typeof size != "undefined"){
34726         this.el.setHeight(size);
34727     }
34728 };
34729
34730 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34731     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34732     getBox : function(){
34733         if(this.collapsed){
34734             return this.collapsedEl.getBox();
34735         }
34736         var box = this.el.getBox();
34737         if(this.split){
34738             var sh = this.split.el.getHeight();
34739             box.height += sh;
34740             box.y -= sh;
34741         }
34742         return box;
34743     },
34744     
34745     updateBox : function(box){
34746         if(this.split && !this.collapsed){
34747             var sh = this.split.el.getHeight();
34748             box.height -= sh;
34749             box.y += sh;
34750             this.split.el.setLeft(box.x);
34751             this.split.el.setTop(box.y-sh);
34752             this.split.el.setWidth(box.width);
34753         }
34754         if(this.collapsed){
34755             this.updateBody(box.width, null);
34756         }
34757         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34758     }
34759 });
34760
34761 Roo.bootstrap.layout.East = function(config){
34762     config.region = "east";
34763     config.cursor = "e-resize";
34764     Roo.bootstrap.layout.Split.call(this, config);
34765     if(this.split){
34766         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34767         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34768         this.split.el.addClass("roo-layout-split-h");
34769     }
34770     var size = config.initialSize || config.width;
34771     if(typeof size != "undefined"){
34772         this.el.setWidth(size);
34773     }
34774 };
34775 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34776     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34777     getBox : function(){
34778         if(this.collapsed){
34779             return this.collapsedEl.getBox();
34780         }
34781         var box = this.el.getBox();
34782         if(this.split){
34783             var sw = this.split.el.getWidth();
34784             box.width += sw;
34785             box.x -= sw;
34786         }
34787         return box;
34788     },
34789
34790     updateBox : function(box){
34791         if(this.split && !this.collapsed){
34792             var sw = this.split.el.getWidth();
34793             box.width -= sw;
34794             this.split.el.setLeft(box.x);
34795             this.split.el.setTop(box.y);
34796             this.split.el.setHeight(box.height);
34797             box.x += sw;
34798         }
34799         if(this.collapsed){
34800             this.updateBody(null, box.height);
34801         }
34802         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34803     }
34804 });
34805
34806 Roo.bootstrap.layout.West = function(config){
34807     config.region = "west";
34808     config.cursor = "w-resize";
34809     
34810     Roo.bootstrap.layout.Split.call(this, config);
34811     if(this.split){
34812         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34813         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34814         this.split.el.addClass("roo-layout-split-h");
34815     }
34816     
34817 };
34818 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34819     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34820     
34821     onRender: function(ctr, pos)
34822     {
34823         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34824         var size = this.config.initialSize || this.config.width;
34825         if(typeof size != "undefined"){
34826             this.el.setWidth(size);
34827         }
34828     },
34829     
34830     getBox : function(){
34831         if(this.collapsed){
34832             return this.collapsedEl.getBox();
34833         }
34834         var box = this.el.getBox();
34835         if(this.split){
34836             box.width += this.split.el.getWidth();
34837         }
34838         return box;
34839     },
34840     
34841     updateBox : function(box){
34842         if(this.split && !this.collapsed){
34843             var sw = this.split.el.getWidth();
34844             box.width -= sw;
34845             this.split.el.setLeft(box.x+box.width);
34846             this.split.el.setTop(box.y);
34847             this.split.el.setHeight(box.height);
34848         }
34849         if(this.collapsed){
34850             this.updateBody(null, box.height);
34851         }
34852         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34853     }
34854 });
34855 Roo.namespace("Roo.bootstrap.panel");/*
34856  * Based on:
34857  * Ext JS Library 1.1.1
34858  * Copyright(c) 2006-2007, Ext JS, LLC.
34859  *
34860  * Originally Released Under LGPL - original licence link has changed is not relivant.
34861  *
34862  * Fork - LGPL
34863  * <script type="text/javascript">
34864  */
34865 /**
34866  * @class Roo.ContentPanel
34867  * @extends Roo.util.Observable
34868  * A basic ContentPanel element.
34869  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34870  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34871  * @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
34872  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34873  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34874  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34875  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34876  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34877  * @cfg {String} title          The title for this panel
34878  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34879  * @cfg {String} url            Calls {@link #setUrl} with this value
34880  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34881  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34882  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34883  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34884  * @cfg {Boolean} badges render the badges
34885
34886  * @constructor
34887  * Create a new ContentPanel.
34888  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34889  * @param {String/Object} config A string to set only the title or a config object
34890  * @param {String} content (optional) Set the HTML content for this panel
34891  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34892  */
34893 Roo.bootstrap.panel.Content = function( config){
34894     
34895     this.tpl = config.tpl || false;
34896     
34897     var el = config.el;
34898     var content = config.content;
34899
34900     if(config.autoCreate){ // xtype is available if this is called from factory
34901         el = Roo.id();
34902     }
34903     this.el = Roo.get(el);
34904     if(!this.el && config && config.autoCreate){
34905         if(typeof config.autoCreate == "object"){
34906             if(!config.autoCreate.id){
34907                 config.autoCreate.id = config.id||el;
34908             }
34909             this.el = Roo.DomHelper.append(document.body,
34910                         config.autoCreate, true);
34911         }else{
34912             var elcfg =  {   tag: "div",
34913                             cls: "roo-layout-inactive-content",
34914                             id: config.id||el
34915                             };
34916             if (config.html) {
34917                 elcfg.html = config.html;
34918                 
34919             }
34920                         
34921             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34922         }
34923     } 
34924     this.closable = false;
34925     this.loaded = false;
34926     this.active = false;
34927    
34928       
34929     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34930         
34931         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34932         
34933         this.wrapEl = this.el; //this.el.wrap();
34934         var ti = [];
34935         if (config.toolbar.items) {
34936             ti = config.toolbar.items ;
34937             delete config.toolbar.items ;
34938         }
34939         
34940         var nitems = [];
34941         this.toolbar.render(this.wrapEl, 'before');
34942         for(var i =0;i < ti.length;i++) {
34943           //  Roo.log(['add child', items[i]]);
34944             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34945         }
34946         this.toolbar.items = nitems;
34947         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34948         delete config.toolbar;
34949         
34950     }
34951     /*
34952     // xtype created footer. - not sure if will work as we normally have to render first..
34953     if (this.footer && !this.footer.el && this.footer.xtype) {
34954         if (!this.wrapEl) {
34955             this.wrapEl = this.el.wrap();
34956         }
34957     
34958         this.footer.container = this.wrapEl.createChild();
34959          
34960         this.footer = Roo.factory(this.footer, Roo);
34961         
34962     }
34963     */
34964     
34965      if(typeof config == "string"){
34966         this.title = config;
34967     }else{
34968         Roo.apply(this, config);
34969     }
34970     
34971     if(this.resizeEl){
34972         this.resizeEl = Roo.get(this.resizeEl, true);
34973     }else{
34974         this.resizeEl = this.el;
34975     }
34976     // handle view.xtype
34977     
34978  
34979     
34980     
34981     this.addEvents({
34982         /**
34983          * @event activate
34984          * Fires when this panel is activated. 
34985          * @param {Roo.ContentPanel} this
34986          */
34987         "activate" : true,
34988         /**
34989          * @event deactivate
34990          * Fires when this panel is activated. 
34991          * @param {Roo.ContentPanel} this
34992          */
34993         "deactivate" : true,
34994
34995         /**
34996          * @event resize
34997          * Fires when this panel is resized if fitToFrame is true.
34998          * @param {Roo.ContentPanel} this
34999          * @param {Number} width The width after any component adjustments
35000          * @param {Number} height The height after any component adjustments
35001          */
35002         "resize" : true,
35003         
35004          /**
35005          * @event render
35006          * Fires when this tab is created
35007          * @param {Roo.ContentPanel} this
35008          */
35009         "render" : true
35010         
35011         
35012         
35013     });
35014     
35015
35016     
35017     
35018     if(this.autoScroll){
35019         this.resizeEl.setStyle("overflow", "auto");
35020     } else {
35021         // fix randome scrolling
35022         //this.el.on('scroll', function() {
35023         //    Roo.log('fix random scolling');
35024         //    this.scrollTo('top',0); 
35025         //});
35026     }
35027     content = content || this.content;
35028     if(content){
35029         this.setContent(content);
35030     }
35031     if(config && config.url){
35032         this.setUrl(this.url, this.params, this.loadOnce);
35033     }
35034     
35035     
35036     
35037     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35038     
35039     if (this.view && typeof(this.view.xtype) != 'undefined') {
35040         this.view.el = this.el.appendChild(document.createElement("div"));
35041         this.view = Roo.factory(this.view); 
35042         this.view.render  &&  this.view.render(false, '');  
35043     }
35044     
35045     
35046     this.fireEvent('render', this);
35047 };
35048
35049 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35050     
35051     tabTip : '',
35052     
35053     setRegion : function(region){
35054         this.region = region;
35055         this.setActiveClass(region && !this.background);
35056     },
35057     
35058     
35059     setActiveClass: function(state)
35060     {
35061         if(state){
35062            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35063            this.el.setStyle('position','relative');
35064         }else{
35065            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35066            this.el.setStyle('position', 'absolute');
35067         } 
35068     },
35069     
35070     /**
35071      * Returns the toolbar for this Panel if one was configured. 
35072      * @return {Roo.Toolbar} 
35073      */
35074     getToolbar : function(){
35075         return this.toolbar;
35076     },
35077     
35078     setActiveState : function(active)
35079     {
35080         this.active = active;
35081         this.setActiveClass(active);
35082         if(!active){
35083             this.fireEvent("deactivate", this);
35084         }else{
35085             this.fireEvent("activate", this);
35086         }
35087     },
35088     /**
35089      * Updates this panel's element
35090      * @param {String} content The new content
35091      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35092     */
35093     setContent : function(content, loadScripts){
35094         this.el.update(content, loadScripts);
35095     },
35096
35097     ignoreResize : function(w, h){
35098         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35099             return true;
35100         }else{
35101             this.lastSize = {width: w, height: h};
35102             return false;
35103         }
35104     },
35105     /**
35106      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35107      * @return {Roo.UpdateManager} The UpdateManager
35108      */
35109     getUpdateManager : function(){
35110         return this.el.getUpdateManager();
35111     },
35112      /**
35113      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35114      * @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:
35115 <pre><code>
35116 panel.load({
35117     url: "your-url.php",
35118     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35119     callback: yourFunction,
35120     scope: yourObject, //(optional scope)
35121     discardUrl: false,
35122     nocache: false,
35123     text: "Loading...",
35124     timeout: 30,
35125     scripts: false
35126 });
35127 </code></pre>
35128      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35129      * 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.
35130      * @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}
35131      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35132      * @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.
35133      * @return {Roo.ContentPanel} this
35134      */
35135     load : function(){
35136         var um = this.el.getUpdateManager();
35137         um.update.apply(um, arguments);
35138         return this;
35139     },
35140
35141
35142     /**
35143      * 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.
35144      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35145      * @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)
35146      * @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)
35147      * @return {Roo.UpdateManager} The UpdateManager
35148      */
35149     setUrl : function(url, params, loadOnce){
35150         if(this.refreshDelegate){
35151             this.removeListener("activate", this.refreshDelegate);
35152         }
35153         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35154         this.on("activate", this.refreshDelegate);
35155         return this.el.getUpdateManager();
35156     },
35157     
35158     _handleRefresh : function(url, params, loadOnce){
35159         if(!loadOnce || !this.loaded){
35160             var updater = this.el.getUpdateManager();
35161             updater.update(url, params, this._setLoaded.createDelegate(this));
35162         }
35163     },
35164     
35165     _setLoaded : function(){
35166         this.loaded = true;
35167     }, 
35168     
35169     /**
35170      * Returns this panel's id
35171      * @return {String} 
35172      */
35173     getId : function(){
35174         return this.el.id;
35175     },
35176     
35177     /** 
35178      * Returns this panel's element - used by regiosn to add.
35179      * @return {Roo.Element} 
35180      */
35181     getEl : function(){
35182         return this.wrapEl || this.el;
35183     },
35184     
35185    
35186     
35187     adjustForComponents : function(width, height)
35188     {
35189         //Roo.log('adjustForComponents ');
35190         if(this.resizeEl != this.el){
35191             width -= this.el.getFrameWidth('lr');
35192             height -= this.el.getFrameWidth('tb');
35193         }
35194         if(this.toolbar){
35195             var te = this.toolbar.getEl();
35196             height -= te.getHeight();
35197             te.setWidth(width);
35198         }
35199         if(this.footer){
35200             var te = this.footer.getEl();
35201             Roo.log("footer:" + te.getHeight());
35202             
35203             height -= te.getHeight();
35204             te.setWidth(width);
35205         }
35206         
35207         
35208         if(this.adjustments){
35209             width += this.adjustments[0];
35210             height += this.adjustments[1];
35211         }
35212         return {"width": width, "height": height};
35213     },
35214     
35215     setSize : function(width, height){
35216         if(this.fitToFrame && !this.ignoreResize(width, height)){
35217             if(this.fitContainer && this.resizeEl != this.el){
35218                 this.el.setSize(width, height);
35219             }
35220             var size = this.adjustForComponents(width, height);
35221             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35222             this.fireEvent('resize', this, size.width, size.height);
35223         }
35224     },
35225     
35226     /**
35227      * Returns this panel's title
35228      * @return {String} 
35229      */
35230     getTitle : function(){
35231         return this.title;
35232     },
35233     
35234     /**
35235      * Set this panel's title
35236      * @param {String} title
35237      */
35238     setTitle : function(title){
35239         this.title = title;
35240         if(this.region){
35241             this.region.updatePanelTitle(this, title);
35242         }
35243     },
35244     
35245     /**
35246      * Returns true is this panel was configured to be closable
35247      * @return {Boolean} 
35248      */
35249     isClosable : function(){
35250         return this.closable;
35251     },
35252     
35253     beforeSlide : function(){
35254         this.el.clip();
35255         this.resizeEl.clip();
35256     },
35257     
35258     afterSlide : function(){
35259         this.el.unclip();
35260         this.resizeEl.unclip();
35261     },
35262     
35263     /**
35264      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35265      *   Will fail silently if the {@link #setUrl} method has not been called.
35266      *   This does not activate the panel, just updates its content.
35267      */
35268     refresh : function(){
35269         if(this.refreshDelegate){
35270            this.loaded = false;
35271            this.refreshDelegate();
35272         }
35273     },
35274     
35275     /**
35276      * Destroys this panel
35277      */
35278     destroy : function(){
35279         this.el.removeAllListeners();
35280         var tempEl = document.createElement("span");
35281         tempEl.appendChild(this.el.dom);
35282         tempEl.innerHTML = "";
35283         this.el.remove();
35284         this.el = null;
35285     },
35286     
35287     /**
35288      * form - if the content panel contains a form - this is a reference to it.
35289      * @type {Roo.form.Form}
35290      */
35291     form : false,
35292     /**
35293      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35294      *    This contains a reference to it.
35295      * @type {Roo.View}
35296      */
35297     view : false,
35298     
35299       /**
35300      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35301      * <pre><code>
35302
35303 layout.addxtype({
35304        xtype : 'Form',
35305        items: [ .... ]
35306    }
35307 );
35308
35309 </code></pre>
35310      * @param {Object} cfg Xtype definition of item to add.
35311      */
35312     
35313     
35314     getChildContainer: function () {
35315         return this.getEl();
35316     }
35317     
35318     
35319     /*
35320         var  ret = new Roo.factory(cfg);
35321         return ret;
35322         
35323         
35324         // add form..
35325         if (cfg.xtype.match(/^Form$/)) {
35326             
35327             var el;
35328             //if (this.footer) {
35329             //    el = this.footer.container.insertSibling(false, 'before');
35330             //} else {
35331                 el = this.el.createChild();
35332             //}
35333
35334             this.form = new  Roo.form.Form(cfg);
35335             
35336             
35337             if ( this.form.allItems.length) {
35338                 this.form.render(el.dom);
35339             }
35340             return this.form;
35341         }
35342         // should only have one of theses..
35343         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35344             // views.. should not be just added - used named prop 'view''
35345             
35346             cfg.el = this.el.appendChild(document.createElement("div"));
35347             // factory?
35348             
35349             var ret = new Roo.factory(cfg);
35350              
35351              ret.render && ret.render(false, ''); // render blank..
35352             this.view = ret;
35353             return ret;
35354         }
35355         return false;
35356     }
35357     \*/
35358 });
35359  
35360 /**
35361  * @class Roo.bootstrap.panel.Grid
35362  * @extends Roo.bootstrap.panel.Content
35363  * @constructor
35364  * Create a new GridPanel.
35365  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35366  * @param {Object} config A the config object
35367   
35368  */
35369
35370
35371
35372 Roo.bootstrap.panel.Grid = function(config)
35373 {
35374     
35375       
35376     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35377         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35378
35379     config.el = this.wrapper;
35380     //this.el = this.wrapper;
35381     
35382       if (config.container) {
35383         // ctor'ed from a Border/panel.grid
35384         
35385         
35386         this.wrapper.setStyle("overflow", "hidden");
35387         this.wrapper.addClass('roo-grid-container');
35388
35389     }
35390     
35391     
35392     if(config.toolbar){
35393         var tool_el = this.wrapper.createChild();    
35394         this.toolbar = Roo.factory(config.toolbar);
35395         var ti = [];
35396         if (config.toolbar.items) {
35397             ti = config.toolbar.items ;
35398             delete config.toolbar.items ;
35399         }
35400         
35401         var nitems = [];
35402         this.toolbar.render(tool_el);
35403         for(var i =0;i < ti.length;i++) {
35404           //  Roo.log(['add child', items[i]]);
35405             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35406         }
35407         this.toolbar.items = nitems;
35408         
35409         delete config.toolbar;
35410     }
35411     
35412     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35413     config.grid.scrollBody = true;;
35414     config.grid.monitorWindowResize = false; // turn off autosizing
35415     config.grid.autoHeight = false;
35416     config.grid.autoWidth = false;
35417     
35418     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35419     
35420     if (config.background) {
35421         // render grid on panel activation (if panel background)
35422         this.on('activate', function(gp) {
35423             if (!gp.grid.rendered) {
35424                 gp.grid.render(this.wrapper);
35425                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35426             }
35427         });
35428             
35429     } else {
35430         this.grid.render(this.wrapper);
35431         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35432
35433     }
35434     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35435     // ??? needed ??? config.el = this.wrapper;
35436     
35437     
35438     
35439   
35440     // xtype created footer. - not sure if will work as we normally have to render first..
35441     if (this.footer && !this.footer.el && this.footer.xtype) {
35442         
35443         var ctr = this.grid.getView().getFooterPanel(true);
35444         this.footer.dataSource = this.grid.dataSource;
35445         this.footer = Roo.factory(this.footer, Roo);
35446         this.footer.render(ctr);
35447         
35448     }
35449     
35450     
35451     
35452     
35453      
35454 };
35455
35456 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35457     getId : function(){
35458         return this.grid.id;
35459     },
35460     
35461     /**
35462      * Returns the grid for this panel
35463      * @return {Roo.bootstrap.Table} 
35464      */
35465     getGrid : function(){
35466         return this.grid;    
35467     },
35468     
35469     setSize : function(width, height){
35470         if(!this.ignoreResize(width, height)){
35471             var grid = this.grid;
35472             var size = this.adjustForComponents(width, height);
35473             var gridel = grid.getGridEl();
35474             gridel.setSize(size.width, size.height);
35475             /*
35476             var thd = grid.getGridEl().select('thead',true).first();
35477             var tbd = grid.getGridEl().select('tbody', true).first();
35478             if (tbd) {
35479                 tbd.setSize(width, height - thd.getHeight());
35480             }
35481             */
35482             grid.autoSize();
35483         }
35484     },
35485      
35486     
35487     
35488     beforeSlide : function(){
35489         this.grid.getView().scroller.clip();
35490     },
35491     
35492     afterSlide : function(){
35493         this.grid.getView().scroller.unclip();
35494     },
35495     
35496     destroy : function(){
35497         this.grid.destroy();
35498         delete this.grid;
35499         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35500     }
35501 });
35502
35503 /**
35504  * @class Roo.bootstrap.panel.Nest
35505  * @extends Roo.bootstrap.panel.Content
35506  * @constructor
35507  * Create a new Panel, that can contain a layout.Border.
35508  * 
35509  * 
35510  * @param {Roo.BorderLayout} layout The layout for this panel
35511  * @param {String/Object} config A string to set only the title or a config object
35512  */
35513 Roo.bootstrap.panel.Nest = function(config)
35514 {
35515     // construct with only one argument..
35516     /* FIXME - implement nicer consturctors
35517     if (layout.layout) {
35518         config = layout;
35519         layout = config.layout;
35520         delete config.layout;
35521     }
35522     if (layout.xtype && !layout.getEl) {
35523         // then layout needs constructing..
35524         layout = Roo.factory(layout, Roo);
35525     }
35526     */
35527     
35528     config.el =  config.layout.getEl();
35529     
35530     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35531     
35532     config.layout.monitorWindowResize = false; // turn off autosizing
35533     this.layout = config.layout;
35534     this.layout.getEl().addClass("roo-layout-nested-layout");
35535     
35536     
35537     
35538     
35539 };
35540
35541 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35542
35543     setSize : function(width, height){
35544         if(!this.ignoreResize(width, height)){
35545             var size = this.adjustForComponents(width, height);
35546             var el = this.layout.getEl();
35547             if (size.height < 1) {
35548                 el.setWidth(size.width);   
35549             } else {
35550                 el.setSize(size.width, size.height);
35551             }
35552             var touch = el.dom.offsetWidth;
35553             this.layout.layout();
35554             // ie requires a double layout on the first pass
35555             if(Roo.isIE && !this.initialized){
35556                 this.initialized = true;
35557                 this.layout.layout();
35558             }
35559         }
35560     },
35561     
35562     // activate all subpanels if not currently active..
35563     
35564     setActiveState : function(active){
35565         this.active = active;
35566         this.setActiveClass(active);
35567         
35568         if(!active){
35569             this.fireEvent("deactivate", this);
35570             return;
35571         }
35572         
35573         this.fireEvent("activate", this);
35574         // not sure if this should happen before or after..
35575         if (!this.layout) {
35576             return; // should not happen..
35577         }
35578         var reg = false;
35579         for (var r in this.layout.regions) {
35580             reg = this.layout.getRegion(r);
35581             if (reg.getActivePanel()) {
35582                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35583                 reg.setActivePanel(reg.getActivePanel());
35584                 continue;
35585             }
35586             if (!reg.panels.length) {
35587                 continue;
35588             }
35589             reg.showPanel(reg.getPanel(0));
35590         }
35591         
35592         
35593         
35594         
35595     },
35596     
35597     /**
35598      * Returns the nested BorderLayout for this panel
35599      * @return {Roo.BorderLayout} 
35600      */
35601     getLayout : function(){
35602         return this.layout;
35603     },
35604     
35605      /**
35606      * Adds a xtype elements to the layout of the nested panel
35607      * <pre><code>
35608
35609 panel.addxtype({
35610        xtype : 'ContentPanel',
35611        region: 'west',
35612        items: [ .... ]
35613    }
35614 );
35615
35616 panel.addxtype({
35617         xtype : 'NestedLayoutPanel',
35618         region: 'west',
35619         layout: {
35620            center: { },
35621            west: { }   
35622         },
35623         items : [ ... list of content panels or nested layout panels.. ]
35624    }
35625 );
35626 </code></pre>
35627      * @param {Object} cfg Xtype definition of item to add.
35628      */
35629     addxtype : function(cfg) {
35630         return this.layout.addxtype(cfg);
35631     
35632     }
35633 });        /*
35634  * Based on:
35635  * Ext JS Library 1.1.1
35636  * Copyright(c) 2006-2007, Ext JS, LLC.
35637  *
35638  * Originally Released Under LGPL - original licence link has changed is not relivant.
35639  *
35640  * Fork - LGPL
35641  * <script type="text/javascript">
35642  */
35643 /**
35644  * @class Roo.TabPanel
35645  * @extends Roo.util.Observable
35646  * A lightweight tab container.
35647  * <br><br>
35648  * Usage:
35649  * <pre><code>
35650 // basic tabs 1, built from existing content
35651 var tabs = new Roo.TabPanel("tabs1");
35652 tabs.addTab("script", "View Script");
35653 tabs.addTab("markup", "View Markup");
35654 tabs.activate("script");
35655
35656 // more advanced tabs, built from javascript
35657 var jtabs = new Roo.TabPanel("jtabs");
35658 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35659
35660 // set up the UpdateManager
35661 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35662 var updater = tab2.getUpdateManager();
35663 updater.setDefaultUrl("ajax1.htm");
35664 tab2.on('activate', updater.refresh, updater, true);
35665
35666 // Use setUrl for Ajax loading
35667 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35668 tab3.setUrl("ajax2.htm", null, true);
35669
35670 // Disabled tab
35671 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35672 tab4.disable();
35673
35674 jtabs.activate("jtabs-1");
35675  * </code></pre>
35676  * @constructor
35677  * Create a new TabPanel.
35678  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35679  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35680  */
35681 Roo.bootstrap.panel.Tabs = function(config){
35682     /**
35683     * The container element for this TabPanel.
35684     * @type Roo.Element
35685     */
35686     this.el = Roo.get(config.el);
35687     delete config.el;
35688     if(config){
35689         if(typeof config == "boolean"){
35690             this.tabPosition = config ? "bottom" : "top";
35691         }else{
35692             Roo.apply(this, config);
35693         }
35694     }
35695     
35696     if(this.tabPosition == "bottom"){
35697         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35698         this.el.addClass("roo-tabs-bottom");
35699     }
35700     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35701     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35702     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35703     if(Roo.isIE){
35704         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35705     }
35706     if(this.tabPosition != "bottom"){
35707         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35708          * @type Roo.Element
35709          */
35710         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35711         this.el.addClass("roo-tabs-top");
35712     }
35713     this.items = [];
35714
35715     this.bodyEl.setStyle("position", "relative");
35716
35717     this.active = null;
35718     this.activateDelegate = this.activate.createDelegate(this);
35719
35720     this.addEvents({
35721         /**
35722          * @event tabchange
35723          * Fires when the active tab changes
35724          * @param {Roo.TabPanel} this
35725          * @param {Roo.TabPanelItem} activePanel The new active tab
35726          */
35727         "tabchange": true,
35728         /**
35729          * @event beforetabchange
35730          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35731          * @param {Roo.TabPanel} this
35732          * @param {Object} e Set cancel to true on this object to cancel the tab change
35733          * @param {Roo.TabPanelItem} tab The tab being changed to
35734          */
35735         "beforetabchange" : true
35736     });
35737
35738     Roo.EventManager.onWindowResize(this.onResize, this);
35739     this.cpad = this.el.getPadding("lr");
35740     this.hiddenCount = 0;
35741
35742
35743     // toolbar on the tabbar support...
35744     if (this.toolbar) {
35745         alert("no toolbar support yet");
35746         this.toolbar  = false;
35747         /*
35748         var tcfg = this.toolbar;
35749         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35750         this.toolbar = new Roo.Toolbar(tcfg);
35751         if (Roo.isSafari) {
35752             var tbl = tcfg.container.child('table', true);
35753             tbl.setAttribute('width', '100%');
35754         }
35755         */
35756         
35757     }
35758    
35759
35760
35761     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35762 };
35763
35764 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35765     /*
35766      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35767      */
35768     tabPosition : "top",
35769     /*
35770      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35771      */
35772     currentTabWidth : 0,
35773     /*
35774      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35775      */
35776     minTabWidth : 40,
35777     /*
35778      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35779      */
35780     maxTabWidth : 250,
35781     /*
35782      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35783      */
35784     preferredTabWidth : 175,
35785     /*
35786      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35787      */
35788     resizeTabs : false,
35789     /*
35790      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35791      */
35792     monitorResize : true,
35793     /*
35794      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35795      */
35796     toolbar : false,
35797
35798     /**
35799      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35800      * @param {String} id The id of the div to use <b>or create</b>
35801      * @param {String} text The text for the tab
35802      * @param {String} content (optional) Content to put in the TabPanelItem body
35803      * @param {Boolean} closable (optional) True to create a close icon on the tab
35804      * @return {Roo.TabPanelItem} The created TabPanelItem
35805      */
35806     addTab : function(id, text, content, closable, tpl)
35807     {
35808         var item = new Roo.bootstrap.panel.TabItem({
35809             panel: this,
35810             id : id,
35811             text : text,
35812             closable : closable,
35813             tpl : tpl
35814         });
35815         this.addTabItem(item);
35816         if(content){
35817             item.setContent(content);
35818         }
35819         return item;
35820     },
35821
35822     /**
35823      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35824      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35825      * @return {Roo.TabPanelItem}
35826      */
35827     getTab : function(id){
35828         return this.items[id];
35829     },
35830
35831     /**
35832      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35833      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35834      */
35835     hideTab : function(id){
35836         var t = this.items[id];
35837         if(!t.isHidden()){
35838            t.setHidden(true);
35839            this.hiddenCount++;
35840            this.autoSizeTabs();
35841         }
35842     },
35843
35844     /**
35845      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35846      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35847      */
35848     unhideTab : function(id){
35849         var t = this.items[id];
35850         if(t.isHidden()){
35851            t.setHidden(false);
35852            this.hiddenCount--;
35853            this.autoSizeTabs();
35854         }
35855     },
35856
35857     /**
35858      * Adds an existing {@link Roo.TabPanelItem}.
35859      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35860      */
35861     addTabItem : function(item){
35862         this.items[item.id] = item;
35863         this.items.push(item);
35864       //  if(this.resizeTabs){
35865     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35866   //         this.autoSizeTabs();
35867 //        }else{
35868 //            item.autoSize();
35869        // }
35870     },
35871
35872     /**
35873      * Removes a {@link Roo.TabPanelItem}.
35874      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35875      */
35876     removeTab : function(id){
35877         var items = this.items;
35878         var tab = items[id];
35879         if(!tab) { return; }
35880         var index = items.indexOf(tab);
35881         if(this.active == tab && items.length > 1){
35882             var newTab = this.getNextAvailable(index);
35883             if(newTab) {
35884                 newTab.activate();
35885             }
35886         }
35887         this.stripEl.dom.removeChild(tab.pnode.dom);
35888         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35889             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35890         }
35891         items.splice(index, 1);
35892         delete this.items[tab.id];
35893         tab.fireEvent("close", tab);
35894         tab.purgeListeners();
35895         this.autoSizeTabs();
35896     },
35897
35898     getNextAvailable : function(start){
35899         var items = this.items;
35900         var index = start;
35901         // look for a next tab that will slide over to
35902         // replace the one being removed
35903         while(index < items.length){
35904             var item = items[++index];
35905             if(item && !item.isHidden()){
35906                 return item;
35907             }
35908         }
35909         // if one isn't found select the previous tab (on the left)
35910         index = start;
35911         while(index >= 0){
35912             var item = items[--index];
35913             if(item && !item.isHidden()){
35914                 return item;
35915             }
35916         }
35917         return null;
35918     },
35919
35920     /**
35921      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35922      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35923      */
35924     disableTab : function(id){
35925         var tab = this.items[id];
35926         if(tab && this.active != tab){
35927             tab.disable();
35928         }
35929     },
35930
35931     /**
35932      * Enables a {@link Roo.TabPanelItem} that is disabled.
35933      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35934      */
35935     enableTab : function(id){
35936         var tab = this.items[id];
35937         tab.enable();
35938     },
35939
35940     /**
35941      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35942      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35943      * @return {Roo.TabPanelItem} The TabPanelItem.
35944      */
35945     activate : function(id){
35946         var tab = this.items[id];
35947         if(!tab){
35948             return null;
35949         }
35950         if(tab == this.active || tab.disabled){
35951             return tab;
35952         }
35953         var e = {};
35954         this.fireEvent("beforetabchange", this, e, tab);
35955         if(e.cancel !== true && !tab.disabled){
35956             if(this.active){
35957                 this.active.hide();
35958             }
35959             this.active = this.items[id];
35960             this.active.show();
35961             this.fireEvent("tabchange", this, this.active);
35962         }
35963         return tab;
35964     },
35965
35966     /**
35967      * Gets the active {@link Roo.TabPanelItem}.
35968      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35969      */
35970     getActiveTab : function(){
35971         return this.active;
35972     },
35973
35974     /**
35975      * Updates the tab body element to fit the height of the container element
35976      * for overflow scrolling
35977      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35978      */
35979     syncHeight : function(targetHeight){
35980         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35981         var bm = this.bodyEl.getMargins();
35982         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35983         this.bodyEl.setHeight(newHeight);
35984         return newHeight;
35985     },
35986
35987     onResize : function(){
35988         if(this.monitorResize){
35989             this.autoSizeTabs();
35990         }
35991     },
35992
35993     /**
35994      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35995      */
35996     beginUpdate : function(){
35997         this.updating = true;
35998     },
35999
36000     /**
36001      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36002      */
36003     endUpdate : function(){
36004         this.updating = false;
36005         this.autoSizeTabs();
36006     },
36007
36008     /**
36009      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36010      */
36011     autoSizeTabs : function(){
36012         var count = this.items.length;
36013         var vcount = count - this.hiddenCount;
36014         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36015             return;
36016         }
36017         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36018         var availWidth = Math.floor(w / vcount);
36019         var b = this.stripBody;
36020         if(b.getWidth() > w){
36021             var tabs = this.items;
36022             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36023             if(availWidth < this.minTabWidth){
36024                 /*if(!this.sleft){    // incomplete scrolling code
36025                     this.createScrollButtons();
36026                 }
36027                 this.showScroll();
36028                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36029             }
36030         }else{
36031             if(this.currentTabWidth < this.preferredTabWidth){
36032                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36033             }
36034         }
36035     },
36036
36037     /**
36038      * Returns the number of tabs in this TabPanel.
36039      * @return {Number}
36040      */
36041      getCount : function(){
36042          return this.items.length;
36043      },
36044
36045     /**
36046      * Resizes all the tabs to the passed width
36047      * @param {Number} The new width
36048      */
36049     setTabWidth : function(width){
36050         this.currentTabWidth = width;
36051         for(var i = 0, len = this.items.length; i < len; i++) {
36052                 if(!this.items[i].isHidden()) {
36053                 this.items[i].setWidth(width);
36054             }
36055         }
36056     },
36057
36058     /**
36059      * Destroys this TabPanel
36060      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36061      */
36062     destroy : function(removeEl){
36063         Roo.EventManager.removeResizeListener(this.onResize, this);
36064         for(var i = 0, len = this.items.length; i < len; i++){
36065             this.items[i].purgeListeners();
36066         }
36067         if(removeEl === true){
36068             this.el.update("");
36069             this.el.remove();
36070         }
36071     },
36072     
36073     createStrip : function(container)
36074     {
36075         var strip = document.createElement("nav");
36076         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36077         container.appendChild(strip);
36078         return strip;
36079     },
36080     
36081     createStripList : function(strip)
36082     {
36083         // div wrapper for retard IE
36084         // returns the "tr" element.
36085         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36086         //'<div class="x-tabs-strip-wrap">'+
36087           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36088           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36089         return strip.firstChild; //.firstChild.firstChild.firstChild;
36090     },
36091     createBody : function(container)
36092     {
36093         var body = document.createElement("div");
36094         Roo.id(body, "tab-body");
36095         //Roo.fly(body).addClass("x-tabs-body");
36096         Roo.fly(body).addClass("tab-content");
36097         container.appendChild(body);
36098         return body;
36099     },
36100     createItemBody :function(bodyEl, id){
36101         var body = Roo.getDom(id);
36102         if(!body){
36103             body = document.createElement("div");
36104             body.id = id;
36105         }
36106         //Roo.fly(body).addClass("x-tabs-item-body");
36107         Roo.fly(body).addClass("tab-pane");
36108          bodyEl.insertBefore(body, bodyEl.firstChild);
36109         return body;
36110     },
36111     /** @private */
36112     createStripElements :  function(stripEl, text, closable, tpl)
36113     {
36114         var td = document.createElement("li"); // was td..
36115         
36116         
36117         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36118         
36119         
36120         stripEl.appendChild(td);
36121         /*if(closable){
36122             td.className = "x-tabs-closable";
36123             if(!this.closeTpl){
36124                 this.closeTpl = new Roo.Template(
36125                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36126                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36127                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36128                 );
36129             }
36130             var el = this.closeTpl.overwrite(td, {"text": text});
36131             var close = el.getElementsByTagName("div")[0];
36132             var inner = el.getElementsByTagName("em")[0];
36133             return {"el": el, "close": close, "inner": inner};
36134         } else {
36135         */
36136         // not sure what this is..
36137 //            if(!this.tabTpl){
36138                 //this.tabTpl = new Roo.Template(
36139                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36140                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36141                 //);
36142 //                this.tabTpl = new Roo.Template(
36143 //                   '<a href="#">' +
36144 //                   '<span unselectable="on"' +
36145 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36146 //                            ' >{text}</span></a>'
36147 //                );
36148 //                
36149 //            }
36150
36151
36152             var template = tpl || this.tabTpl || false;
36153             
36154             if(!template){
36155                 
36156                 template = new Roo.Template(
36157                    '<a href="#">' +
36158                    '<span unselectable="on"' +
36159                             (this.disableTooltips ? '' : ' title="{text}"') +
36160                             ' >{text}</span></a>'
36161                 );
36162             }
36163             
36164             switch (typeof(template)) {
36165                 case 'object' :
36166                     break;
36167                 case 'string' :
36168                     template = new Roo.Template(template);
36169                     break;
36170                 default :
36171                     break;
36172             }
36173             
36174             var el = template.overwrite(td, {"text": text});
36175             
36176             var inner = el.getElementsByTagName("span")[0];
36177             
36178             return {"el": el, "inner": inner};
36179             
36180     }
36181         
36182     
36183 });
36184
36185 /**
36186  * @class Roo.TabPanelItem
36187  * @extends Roo.util.Observable
36188  * Represents an individual item (tab plus body) in a TabPanel.
36189  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36190  * @param {String} id The id of this TabPanelItem
36191  * @param {String} text The text for the tab of this TabPanelItem
36192  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36193  */
36194 Roo.bootstrap.panel.TabItem = function(config){
36195     /**
36196      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36197      * @type Roo.TabPanel
36198      */
36199     this.tabPanel = config.panel;
36200     /**
36201      * The id for this TabPanelItem
36202      * @type String
36203      */
36204     this.id = config.id;
36205     /** @private */
36206     this.disabled = false;
36207     /** @private */
36208     this.text = config.text;
36209     /** @private */
36210     this.loaded = false;
36211     this.closable = config.closable;
36212
36213     /**
36214      * The body element for this TabPanelItem.
36215      * @type Roo.Element
36216      */
36217     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36218     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36219     this.bodyEl.setStyle("display", "block");
36220     this.bodyEl.setStyle("zoom", "1");
36221     //this.hideAction();
36222
36223     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36224     /** @private */
36225     this.el = Roo.get(els.el);
36226     this.inner = Roo.get(els.inner, true);
36227     this.textEl = Roo.get(this.el.dom.firstChild, true);
36228     this.pnode = Roo.get(els.el.parentNode, true);
36229     this.el.on("mousedown", this.onTabMouseDown, this);
36230     this.el.on("click", this.onTabClick, this);
36231     /** @private */
36232     if(config.closable){
36233         var c = Roo.get(els.close, true);
36234         c.dom.title = this.closeText;
36235         c.addClassOnOver("close-over");
36236         c.on("click", this.closeClick, this);
36237      }
36238
36239     this.addEvents({
36240          /**
36241          * @event activate
36242          * Fires when this tab becomes the active tab.
36243          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36244          * @param {Roo.TabPanelItem} this
36245          */
36246         "activate": true,
36247         /**
36248          * @event beforeclose
36249          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36250          * @param {Roo.TabPanelItem} this
36251          * @param {Object} e Set cancel to true on this object to cancel the close.
36252          */
36253         "beforeclose": true,
36254         /**
36255          * @event close
36256          * Fires when this tab is closed.
36257          * @param {Roo.TabPanelItem} this
36258          */
36259          "close": true,
36260         /**
36261          * @event deactivate
36262          * Fires when this tab is no longer the active tab.
36263          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36264          * @param {Roo.TabPanelItem} this
36265          */
36266          "deactivate" : true
36267     });
36268     this.hidden = false;
36269
36270     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36271 };
36272
36273 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36274            {
36275     purgeListeners : function(){
36276        Roo.util.Observable.prototype.purgeListeners.call(this);
36277        this.el.removeAllListeners();
36278     },
36279     /**
36280      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36281      */
36282     show : function(){
36283         this.pnode.addClass("active");
36284         this.showAction();
36285         if(Roo.isOpera){
36286             this.tabPanel.stripWrap.repaint();
36287         }
36288         this.fireEvent("activate", this.tabPanel, this);
36289     },
36290
36291     /**
36292      * Returns true if this tab is the active tab.
36293      * @return {Boolean}
36294      */
36295     isActive : function(){
36296         return this.tabPanel.getActiveTab() == this;
36297     },
36298
36299     /**
36300      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36301      */
36302     hide : function(){
36303         this.pnode.removeClass("active");
36304         this.hideAction();
36305         this.fireEvent("deactivate", this.tabPanel, this);
36306     },
36307
36308     hideAction : function(){
36309         this.bodyEl.hide();
36310         this.bodyEl.setStyle("position", "absolute");
36311         this.bodyEl.setLeft("-20000px");
36312         this.bodyEl.setTop("-20000px");
36313     },
36314
36315     showAction : function(){
36316         this.bodyEl.setStyle("position", "relative");
36317         this.bodyEl.setTop("");
36318         this.bodyEl.setLeft("");
36319         this.bodyEl.show();
36320     },
36321
36322     /**
36323      * Set the tooltip for the tab.
36324      * @param {String} tooltip The tab's tooltip
36325      */
36326     setTooltip : function(text){
36327         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36328             this.textEl.dom.qtip = text;
36329             this.textEl.dom.removeAttribute('title');
36330         }else{
36331             this.textEl.dom.title = text;
36332         }
36333     },
36334
36335     onTabClick : function(e){
36336         e.preventDefault();
36337         this.tabPanel.activate(this.id);
36338     },
36339
36340     onTabMouseDown : function(e){
36341         e.preventDefault();
36342         this.tabPanel.activate(this.id);
36343     },
36344 /*
36345     getWidth : function(){
36346         return this.inner.getWidth();
36347     },
36348
36349     setWidth : function(width){
36350         var iwidth = width - this.pnode.getPadding("lr");
36351         this.inner.setWidth(iwidth);
36352         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36353         this.pnode.setWidth(width);
36354     },
36355 */
36356     /**
36357      * Show or hide the tab
36358      * @param {Boolean} hidden True to hide or false to show.
36359      */
36360     setHidden : function(hidden){
36361         this.hidden = hidden;
36362         this.pnode.setStyle("display", hidden ? "none" : "");
36363     },
36364
36365     /**
36366      * Returns true if this tab is "hidden"
36367      * @return {Boolean}
36368      */
36369     isHidden : function(){
36370         return this.hidden;
36371     },
36372
36373     /**
36374      * Returns the text for this tab
36375      * @return {String}
36376      */
36377     getText : function(){
36378         return this.text;
36379     },
36380     /*
36381     autoSize : function(){
36382         //this.el.beginMeasure();
36383         this.textEl.setWidth(1);
36384         /*
36385          *  #2804 [new] Tabs in Roojs
36386          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36387          */
36388         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36389         //this.el.endMeasure();
36390     //},
36391
36392     /**
36393      * Sets the text for the tab (Note: this also sets the tooltip text)
36394      * @param {String} text The tab's text and tooltip
36395      */
36396     setText : function(text){
36397         this.text = text;
36398         this.textEl.update(text);
36399         this.setTooltip(text);
36400         //if(!this.tabPanel.resizeTabs){
36401         //    this.autoSize();
36402         //}
36403     },
36404     /**
36405      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36406      */
36407     activate : function(){
36408         this.tabPanel.activate(this.id);
36409     },
36410
36411     /**
36412      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36413      */
36414     disable : function(){
36415         if(this.tabPanel.active != this){
36416             this.disabled = true;
36417             this.pnode.addClass("disabled");
36418         }
36419     },
36420
36421     /**
36422      * Enables this TabPanelItem if it was previously disabled.
36423      */
36424     enable : function(){
36425         this.disabled = false;
36426         this.pnode.removeClass("disabled");
36427     },
36428
36429     /**
36430      * Sets the content for this TabPanelItem.
36431      * @param {String} content The content
36432      * @param {Boolean} loadScripts true to look for and load scripts
36433      */
36434     setContent : function(content, loadScripts){
36435         this.bodyEl.update(content, loadScripts);
36436     },
36437
36438     /**
36439      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36440      * @return {Roo.UpdateManager} The UpdateManager
36441      */
36442     getUpdateManager : function(){
36443         return this.bodyEl.getUpdateManager();
36444     },
36445
36446     /**
36447      * Set a URL to be used to load the content for this TabPanelItem.
36448      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36449      * @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)
36450      * @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)
36451      * @return {Roo.UpdateManager} The UpdateManager
36452      */
36453     setUrl : function(url, params, loadOnce){
36454         if(this.refreshDelegate){
36455             this.un('activate', this.refreshDelegate);
36456         }
36457         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36458         this.on("activate", this.refreshDelegate);
36459         return this.bodyEl.getUpdateManager();
36460     },
36461
36462     /** @private */
36463     _handleRefresh : function(url, params, loadOnce){
36464         if(!loadOnce || !this.loaded){
36465             var updater = this.bodyEl.getUpdateManager();
36466             updater.update(url, params, this._setLoaded.createDelegate(this));
36467         }
36468     },
36469
36470     /**
36471      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36472      *   Will fail silently if the setUrl method has not been called.
36473      *   This does not activate the panel, just updates its content.
36474      */
36475     refresh : function(){
36476         if(this.refreshDelegate){
36477            this.loaded = false;
36478            this.refreshDelegate();
36479         }
36480     },
36481
36482     /** @private */
36483     _setLoaded : function(){
36484         this.loaded = true;
36485     },
36486
36487     /** @private */
36488     closeClick : function(e){
36489         var o = {};
36490         e.stopEvent();
36491         this.fireEvent("beforeclose", this, o);
36492         if(o.cancel !== true){
36493             this.tabPanel.removeTab(this.id);
36494         }
36495     },
36496     /**
36497      * The text displayed in the tooltip for the close icon.
36498      * @type String
36499      */
36500     closeText : "Close this tab"
36501 });