css-bootstrap/secure-pass.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         
1528         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529             this.el.dom.src =  url;
1530             return;
1531         }
1532         
1533         this.el.select('img', true).first().dom.src =  url;
1534     }
1535     
1536     
1537    
1538 });
1539
1540  /*
1541  * - LGPL
1542  *
1543  * image
1544  * 
1545  */
1546
1547
1548 /**
1549  * @class Roo.bootstrap.Link
1550  * @extends Roo.bootstrap.Component
1551  * Bootstrap Link Class
1552  * @cfg {String} alt image alternative text
1553  * @cfg {String} href a tag href
1554  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555  * @cfg {String} html the content of the link.
1556  * @cfg {String} anchor name for the anchor link
1557  * @cfg {String} fa - favicon
1558
1559  * @cfg {Boolean} preventDefault (true | false) default false
1560
1561  * 
1562  * @constructor
1563  * Create a new Input
1564  * @param {Object} config The config object
1565  */
1566
1567 Roo.bootstrap.Link = function(config){
1568     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1569     
1570     this.addEvents({
1571         // img events
1572         /**
1573          * @event click
1574          * The img click event for the img.
1575          * @param {Roo.EventObject} e
1576          */
1577         "click" : true
1578     });
1579 };
1580
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1582     
1583     href: false,
1584     target: false,
1585     preventDefault: false,
1586     anchor : false,
1587     alt : false,
1588     fa: false,
1589
1590
1591     getAutoCreate : function()
1592     {
1593         var html = this.html || '';
1594         
1595         if (this.fa !== false) {
1596             html = '<i class="fa fa-' + this.fa + '"></i>';
1597         }
1598         var cfg = {
1599             tag: 'a'
1600         };
1601         // anchor's do not require html/href...
1602         if (this.anchor === false) {
1603             cfg.html = html;
1604             cfg.href = this.href || '#';
1605         } else {
1606             cfg.name = this.anchor;
1607             if (this.html !== false || this.fa !== false) {
1608                 cfg.html = html;
1609             }
1610             if (this.href !== false) {
1611                 cfg.href = this.href;
1612             }
1613         }
1614         
1615         if(this.alt !== false){
1616             cfg.alt = this.alt;
1617         }
1618         
1619         
1620         if(this.target !== false) {
1621             cfg.target = this.target;
1622         }
1623         
1624         return cfg;
1625     },
1626     
1627     initEvents: function() {
1628         
1629         if(!this.href || this.preventDefault){
1630             this.el.on('click', this.onClick, this);
1631         }
1632     },
1633     
1634     onClick : function(e)
1635     {
1636         if(this.preventDefault){
1637             e.preventDefault();
1638         }
1639         //Roo.log('img onclick');
1640         this.fireEvent('click', this, e);
1641     }
1642    
1643 });
1644
1645  /*
1646  * - LGPL
1647  *
1648  * header
1649  * 
1650  */
1651
1652 /**
1653  * @class Roo.bootstrap.Header
1654  * @extends Roo.bootstrap.Component
1655  * Bootstrap Header class
1656  * @cfg {String} html content of header
1657  * @cfg {Number} level (1|2|3|4|5|6) default 1
1658  * 
1659  * @constructor
1660  * Create a new Header
1661  * @param {Object} config The config object
1662  */
1663
1664
1665 Roo.bootstrap.Header  = function(config){
1666     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1667 };
1668
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1670     
1671     //href : false,
1672     html : false,
1673     level : 1,
1674     
1675     
1676     
1677     getAutoCreate : function(){
1678         
1679         
1680         
1681         var cfg = {
1682             tag: 'h' + (1 *this.level),
1683             html: this.html || ''
1684         } ;
1685         
1686         return cfg;
1687     }
1688    
1689 });
1690
1691  
1692
1693  /*
1694  * Based on:
1695  * Ext JS Library 1.1.1
1696  * Copyright(c) 2006-2007, Ext JS, LLC.
1697  *
1698  * Originally Released Under LGPL - original licence link has changed is not relivant.
1699  *
1700  * Fork - LGPL
1701  * <script type="text/javascript">
1702  */
1703  
1704 /**
1705  * @class Roo.bootstrap.MenuMgr
1706  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1707  * @singleton
1708  */
1709 Roo.bootstrap.MenuMgr = function(){
1710    var menus, active, groups = {}, attached = false, lastShow = new Date();
1711
1712    // private - called when first menu is created
1713    function init(){
1714        menus = {};
1715        active = new Roo.util.MixedCollection();
1716        Roo.get(document).addKeyListener(27, function(){
1717            if(active.length > 0){
1718                hideAll();
1719            }
1720        });
1721    }
1722
1723    // private
1724    function hideAll(){
1725        if(active && active.length > 0){
1726            var c = active.clone();
1727            c.each(function(m){
1728                m.hide();
1729            });
1730        }
1731    }
1732
1733    // private
1734    function onHide(m){
1735        active.remove(m);
1736        if(active.length < 1){
1737            Roo.get(document).un("mouseup", onMouseDown);
1738             
1739            attached = false;
1740        }
1741    }
1742
1743    // private
1744    function onShow(m){
1745        var last = active.last();
1746        lastShow = new Date();
1747        active.add(m);
1748        if(!attached){
1749           Roo.get(document).on("mouseup", onMouseDown);
1750            
1751            attached = true;
1752        }
1753        if(m.parentMenu){
1754           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755           m.parentMenu.activeChild = m;
1756        }else if(last && last.isVisible()){
1757           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1758        }
1759    }
1760
1761    // private
1762    function onBeforeHide(m){
1763        if(m.activeChild){
1764            m.activeChild.hide();
1765        }
1766        if(m.autoHideTimer){
1767            clearTimeout(m.autoHideTimer);
1768            delete m.autoHideTimer;
1769        }
1770    }
1771
1772    // private
1773    function onBeforeShow(m){
1774        var pm = m.parentMenu;
1775        if(!pm && !m.allowOtherMenus){
1776            hideAll();
1777        }else if(pm && pm.activeChild && active != m){
1778            pm.activeChild.hide();
1779        }
1780    }
1781
1782    // private this should really trigger on mouseup..
1783    function onMouseDown(e){
1784         Roo.log("on Mouse Up");
1785         
1786         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787             Roo.log("MenuManager hideAll");
1788             hideAll();
1789             e.stopEvent();
1790         }
1791         
1792         
1793    }
1794
1795    // private
1796    function onBeforeCheck(mi, state){
1797        if(state){
1798            var g = groups[mi.group];
1799            for(var i = 0, l = g.length; i < l; i++){
1800                if(g[i] != mi){
1801                    g[i].setChecked(false);
1802                }
1803            }
1804        }
1805    }
1806
1807    return {
1808
1809        /**
1810         * Hides all menus that are currently visible
1811         */
1812        hideAll : function(){
1813             hideAll();  
1814        },
1815
1816        // private
1817        register : function(menu){
1818            if(!menus){
1819                init();
1820            }
1821            menus[menu.id] = menu;
1822            menu.on("beforehide", onBeforeHide);
1823            menu.on("hide", onHide);
1824            menu.on("beforeshow", onBeforeShow);
1825            menu.on("show", onShow);
1826            var g = menu.group;
1827            if(g && menu.events["checkchange"]){
1828                if(!groups[g]){
1829                    groups[g] = [];
1830                }
1831                groups[g].push(menu);
1832                menu.on("checkchange", onCheck);
1833            }
1834        },
1835
1836         /**
1837          * Returns a {@link Roo.menu.Menu} object
1838          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839          * be used to generate and return a new Menu instance.
1840          */
1841        get : function(menu){
1842            if(typeof menu == "string"){ // menu id
1843                return menus[menu];
1844            }else if(menu.events){  // menu instance
1845                return menu;
1846            }
1847            /*else if(typeof menu.length == 'number'){ // array of menu items?
1848                return new Roo.bootstrap.Menu({items:menu});
1849            }else{ // otherwise, must be a config
1850                return new Roo.bootstrap.Menu(menu);
1851            }
1852            */
1853            return false;
1854        },
1855
1856        // private
1857        unregister : function(menu){
1858            delete menus[menu.id];
1859            menu.un("beforehide", onBeforeHide);
1860            menu.un("hide", onHide);
1861            menu.un("beforeshow", onBeforeShow);
1862            menu.un("show", onShow);
1863            var g = menu.group;
1864            if(g && menu.events["checkchange"]){
1865                groups[g].remove(menu);
1866                menu.un("checkchange", onCheck);
1867            }
1868        },
1869
1870        // private
1871        registerCheckable : function(menuItem){
1872            var g = menuItem.group;
1873            if(g){
1874                if(!groups[g]){
1875                    groups[g] = [];
1876                }
1877                groups[g].push(menuItem);
1878                menuItem.on("beforecheckchange", onBeforeCheck);
1879            }
1880        },
1881
1882        // private
1883        unregisterCheckable : function(menuItem){
1884            var g = menuItem.group;
1885            if(g){
1886                groups[g].remove(menuItem);
1887                menuItem.un("beforecheckchange", onBeforeCheck);
1888            }
1889        }
1890    };
1891 }();/*
1892  * - LGPL
1893  *
1894  * menu
1895  * 
1896  */
1897
1898 /**
1899  * @class Roo.bootstrap.Menu
1900  * @extends Roo.bootstrap.Component
1901  * Bootstrap Menu class - container for MenuItems
1902  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1904  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1905  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1906  * 
1907  * @constructor
1908  * Create a new Menu
1909  * @param {Object} config The config object
1910  */
1911
1912
1913 Roo.bootstrap.Menu = function(config){
1914     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915     if (this.registerMenu && this.type != 'treeview')  {
1916         Roo.bootstrap.MenuMgr.register(this);
1917     }
1918     this.addEvents({
1919         /**
1920          * @event beforeshow
1921          * Fires before this menu is displayed
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforeshow : true,
1925         /**
1926          * @event beforehide
1927          * Fires before this menu is hidden
1928          * @param {Roo.menu.Menu} this
1929          */
1930         beforehide : true,
1931         /**
1932          * @event show
1933          * Fires after this menu is displayed
1934          * @param {Roo.menu.Menu} this
1935          */
1936         show : true,
1937         /**
1938          * @event hide
1939          * Fires after this menu is hidden
1940          * @param {Roo.menu.Menu} this
1941          */
1942         hide : true,
1943         /**
1944          * @event click
1945          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946          * @param {Roo.menu.Menu} this
1947          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948          * @param {Roo.EventObject} e
1949          */
1950         click : true,
1951         /**
1952          * @event mouseover
1953          * Fires when the mouse is hovering over this menu
1954          * @param {Roo.menu.Menu} this
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          */
1958         mouseover : true,
1959         /**
1960          * @event mouseout
1961          * Fires when the mouse exits this menu
1962          * @param {Roo.menu.Menu} this
1963          * @param {Roo.EventObject} e
1964          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1965          */
1966         mouseout : true,
1967         /**
1968          * @event itemclick
1969          * Fires when a menu item contained in this menu is clicked
1970          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971          * @param {Roo.EventObject} e
1972          */
1973         itemclick: true
1974     });
1975     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1976 };
1977
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1979     
1980    /// html : false,
1981     //align : '',
1982     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1983     type: false,
1984     /**
1985      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1986      */
1987     registerMenu : true,
1988     
1989     menuItems :false, // stores the menu items..
1990     
1991     hidden:true,
1992         
1993     parentMenu : false,
1994     
1995     stopEvent : true,
1996     
1997     isLink : false,
1998     
1999     getChildContainer : function() {
2000         return this.el;  
2001     },
2002     
2003     getAutoCreate : function(){
2004          
2005         //if (['right'].indexOf(this.align)!==-1) {
2006         //    cfg.cn[1].cls += ' pull-right'
2007         //}
2008         
2009         
2010         var cfg = {
2011             tag : 'ul',
2012             cls : 'dropdown-menu' ,
2013             style : 'z-index:1000'
2014             
2015         };
2016         
2017         if (this.type === 'submenu') {
2018             cfg.cls = 'submenu active';
2019         }
2020         if (this.type === 'treeview') {
2021             cfg.cls = 'treeview-menu';
2022         }
2023         
2024         return cfg;
2025     },
2026     initEvents : function() {
2027         
2028        // Roo.log("ADD event");
2029        // Roo.log(this.triggerEl.dom);
2030         
2031         this.triggerEl.on('click', this.onTriggerClick, this);
2032         
2033         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2034         
2035         this.triggerEl.addClass('dropdown-toggle');
2036         
2037         if (Roo.isTouch) {
2038             this.el.on('touchstart'  , this.onTouch, this);
2039         }
2040         this.el.on('click' , this.onClick, this);
2041
2042         this.el.on("mouseover", this.onMouseOver, this);
2043         this.el.on("mouseout", this.onMouseOut, this);
2044         
2045     },
2046     
2047     findTargetItem : function(e)
2048     {
2049         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2050         if(!t){
2051             return false;
2052         }
2053         //Roo.log(t);         Roo.log(t.id);
2054         if(t && t.id){
2055             //Roo.log(this.menuitems);
2056             return this.menuitems.get(t.id);
2057             
2058             //return this.items.get(t.menuItemId);
2059         }
2060         
2061         return false;
2062     },
2063     
2064     onTouch : function(e) 
2065     {
2066         Roo.log("menu.onTouch");
2067         //e.stopEvent(); this make the user popdown broken
2068         this.onClick(e);
2069     },
2070     
2071     onClick : function(e)
2072     {
2073         Roo.log("menu.onClick");
2074         
2075         var t = this.findTargetItem(e);
2076         if(!t || t.isContainer){
2077             return;
2078         }
2079         Roo.log(e);
2080         /*
2081         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2082             if(t == this.activeItem && t.shouldDeactivate(e)){
2083                 this.activeItem.deactivate();
2084                 delete this.activeItem;
2085                 return;
2086             }
2087             if(t.canActivate){
2088                 this.setActiveItem(t, true);
2089             }
2090             return;
2091             
2092             
2093         }
2094         */
2095        
2096         Roo.log('pass click event');
2097         
2098         t.onClick(e);
2099         
2100         this.fireEvent("click", this, t, e);
2101         
2102         var _this = this;
2103         
2104         (function() { _this.hide(); }).defer(100);
2105     },
2106     
2107     onMouseOver : function(e){
2108         var t  = this.findTargetItem(e);
2109         //Roo.log(t);
2110         //if(t){
2111         //    if(t.canActivate && !t.disabled){
2112         //        this.setActiveItem(t, true);
2113         //    }
2114         //}
2115         
2116         this.fireEvent("mouseover", this, e, t);
2117     },
2118     isVisible : function(){
2119         return !this.hidden;
2120     },
2121      onMouseOut : function(e){
2122         var t  = this.findTargetItem(e);
2123         
2124         //if(t ){
2125         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2126         //        this.activeItem.deactivate();
2127         //        delete this.activeItem;
2128         //    }
2129         //}
2130         this.fireEvent("mouseout", this, e, t);
2131     },
2132     
2133     
2134     /**
2135      * Displays this menu relative to another element
2136      * @param {String/HTMLElement/Roo.Element} element The element to align to
2137      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138      * the element (defaults to this.defaultAlign)
2139      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2140      */
2141     show : function(el, pos, parentMenu){
2142         this.parentMenu = parentMenu;
2143         if(!this.el){
2144             this.render();
2145         }
2146         this.fireEvent("beforeshow", this);
2147         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2148     },
2149      /**
2150      * Displays this menu at a specific xy position
2151      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2153      */
2154     showAt : function(xy, parentMenu, /* private: */_e){
2155         this.parentMenu = parentMenu;
2156         if(!this.el){
2157             this.render();
2158         }
2159         if(_e !== false){
2160             this.fireEvent("beforeshow", this);
2161             //xy = this.el.adjustForConstraints(xy);
2162         }
2163         
2164         //this.el.show();
2165         this.hideMenuItems();
2166         this.hidden = false;
2167         this.triggerEl.addClass('open');
2168         
2169         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2171         }
2172         
2173         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2174             this.el.setXY(xy);
2175         }
2176         
2177         this.focus();
2178         this.fireEvent("show", this);
2179     },
2180     
2181     focus : function(){
2182         return;
2183         if(!this.hidden){
2184             this.doFocus.defer(50, this);
2185         }
2186     },
2187
2188     doFocus : function(){
2189         if(!this.hidden){
2190             this.focusEl.focus();
2191         }
2192     },
2193
2194     /**
2195      * Hides this menu and optionally all parent menus
2196      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2197      */
2198     hide : function(deep)
2199     {
2200         
2201         this.hideMenuItems();
2202         if(this.el && this.isVisible()){
2203             this.fireEvent("beforehide", this);
2204             if(this.activeItem){
2205                 this.activeItem.deactivate();
2206                 this.activeItem = null;
2207             }
2208             this.triggerEl.removeClass('open');;
2209             this.hidden = true;
2210             this.fireEvent("hide", this);
2211         }
2212         if(deep === true && this.parentMenu){
2213             this.parentMenu.hide(true);
2214         }
2215     },
2216     
2217     onTriggerClick : function(e)
2218     {
2219         Roo.log('trigger click');
2220         
2221         var target = e.getTarget();
2222         
2223         Roo.log(target.nodeName.toLowerCase());
2224         
2225         if(target.nodeName.toLowerCase() === 'i'){
2226             e.preventDefault();
2227         }
2228         
2229     },
2230     
2231     onTriggerPress  : function(e)
2232     {
2233         Roo.log('trigger press');
2234         //Roo.log(e.getTarget());
2235        // Roo.log(this.triggerEl.dom);
2236        
2237         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238         var pel = Roo.get(e.getTarget());
2239         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240             Roo.log('is treeview or dropdown?');
2241             return;
2242         }
2243         
2244         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2245             return;
2246         }
2247         
2248         if (this.isVisible()) {
2249             Roo.log('hide');
2250             this.hide();
2251         } else {
2252             Roo.log('show');
2253             this.show(this.triggerEl, false, false);
2254         }
2255         
2256         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2257             e.stopEvent();
2258         }
2259         
2260     },
2261        
2262     
2263     hideMenuItems : function()
2264     {
2265         Roo.log("hide Menu Items");
2266         if (!this.el) { 
2267             return;
2268         }
2269         //$(backdrop).remove()
2270         this.el.select('.open',true).each(function(aa) {
2271             
2272             aa.removeClass('open');
2273           //var parent = getParent($(this))
2274           //var relatedTarget = { relatedTarget: this }
2275           
2276            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277           //if (e.isDefaultPrevented()) return
2278            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2279         });
2280     },
2281     addxtypeChild : function (tree, cntr) {
2282         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2283           
2284         this.menuitems.add(comp);
2285         return comp;
2286
2287     },
2288     getEl : function()
2289     {
2290         Roo.log(this.el);
2291         return this.el;
2292     }
2293 });
2294
2295  
2296  /*
2297  * - LGPL
2298  *
2299  * menu item
2300  * 
2301  */
2302
2303
2304 /**
2305  * @class Roo.bootstrap.MenuItem
2306  * @extends Roo.bootstrap.Component
2307  * Bootstrap MenuItem class
2308  * @cfg {String} html the menu label
2309  * @cfg {String} href the link
2310  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2313  * @cfg {String} fa favicon to show on left of menu item.
2314  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2315  * 
2316  * 
2317  * @constructor
2318  * Create a new MenuItem
2319  * @param {Object} config The config object
2320  */
2321
2322
2323 Roo.bootstrap.MenuItem = function(config){
2324     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2325     this.addEvents({
2326         // raw events
2327         /**
2328          * @event click
2329          * The raw click event for the entire grid.
2330          * @param {Roo.bootstrap.MenuItem} this
2331          * @param {Roo.EventObject} e
2332          */
2333         "click" : true
2334     });
2335 };
2336
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2338     
2339     href : false,
2340     html : false,
2341     preventDefault: false,
2342     isContainer : false,
2343     active : false,
2344     fa: false,
2345     
2346     getAutoCreate : function(){
2347         
2348         if(this.isContainer){
2349             return {
2350                 tag: 'li',
2351                 cls: 'dropdown-menu-item'
2352             };
2353         }
2354         var ctag = {
2355             tag: 'span',
2356             html: 'Link'
2357         };
2358         
2359         var anc = {
2360             tag : 'a',
2361             href : '#',
2362             cn : [  ]
2363         };
2364         
2365         if (this.fa !== false) {
2366             anc.cn.push({
2367                 tag : 'i',
2368                 cls : 'fa fa-' + this.fa
2369             });
2370         }
2371         
2372         anc.cn.push(ctag);
2373         
2374         
2375         var cfg= {
2376             tag: 'li',
2377             cls: 'dropdown-menu-item',
2378             cn: [ anc ]
2379         };
2380         if (this.parent().type == 'treeview') {
2381             cfg.cls = 'treeview-menu';
2382         }
2383         if (this.active) {
2384             cfg.cls += ' active';
2385         }
2386         
2387         
2388         
2389         anc.href = this.href || cfg.cn[0].href ;
2390         ctag.html = this.html || cfg.cn[0].html ;
2391         return cfg;
2392     },
2393     
2394     initEvents: function()
2395     {
2396         if (this.parent().type == 'treeview') {
2397             this.el.select('a').on('click', this.onClick, this);
2398         }
2399         if (this.menu) {
2400             this.menu.parentType = this.xtype;
2401             this.menu.triggerEl = this.el;
2402             this.menu = this.addxtype(Roo.apply({}, this.menu));
2403         }
2404         
2405     },
2406     onClick : function(e)
2407     {
2408         Roo.log('item on click ');
2409         
2410         if(this.preventDefault){
2411             e.preventDefault();
2412         }
2413         //this.parent().hideMenuItems();
2414         
2415         this.fireEvent('click', this, e);
2416     },
2417     getEl : function()
2418     {
2419         return this.el;
2420     } 
2421 });
2422
2423  
2424
2425  /*
2426  * - LGPL
2427  *
2428  * menu separator
2429  * 
2430  */
2431
2432
2433 /**
2434  * @class Roo.bootstrap.MenuSeparator
2435  * @extends Roo.bootstrap.Component
2436  * Bootstrap MenuSeparator class
2437  * 
2438  * @constructor
2439  * Create a new MenuItem
2440  * @param {Object} config The config object
2441  */
2442
2443
2444 Roo.bootstrap.MenuSeparator = function(config){
2445     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2446 };
2447
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2449     
2450     getAutoCreate : function(){
2451         var cfg = {
2452             cls: 'divider',
2453             tag : 'li'
2454         };
2455         
2456         return cfg;
2457     }
2458    
2459 });
2460
2461  
2462
2463  
2464 /*
2465 * Licence: LGPL
2466 */
2467
2468 /**
2469  * @class Roo.bootstrap.Modal
2470  * @extends Roo.bootstrap.Component
2471  * Bootstrap Modal class
2472  * @cfg {String} title Title of dialog
2473  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2475  * @cfg {Boolean} specificTitle default false
2476  * @cfg {Array} buttons Array of buttons or standard button set..
2477  * @cfg {String} buttonPosition (left|right|center) default right
2478  * @cfg {Boolean} animate default true
2479  * @cfg {Boolean} allow_close default true
2480  * @cfg {Boolean} fitwindow default false
2481  * @cfg {String} size (sm|lg) default empty
2482  *
2483  *
2484  * @constructor
2485  * Create a new Modal Dialog
2486  * @param {Object} config The config object
2487  */
2488
2489 Roo.bootstrap.Modal = function(config){
2490     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event btnclick
2495          * The raw btnclick event for the button
2496          * @param {Roo.EventObject} e
2497          */
2498         "btnclick" : true
2499     });
2500     this.buttons = this.buttons || [];
2501
2502     if (this.tmpl) {
2503         this.tmpl = Roo.factory(this.tmpl);
2504     }
2505
2506 };
2507
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2509
2510     title : 'test dialog',
2511
2512     buttons : false,
2513
2514     // set on load...
2515
2516     html: false,
2517
2518     tmp: false,
2519
2520     specificTitle: false,
2521
2522     buttonPosition: 'right',
2523
2524     allow_close : true,
2525
2526     animate : true,
2527
2528     fitwindow: false,
2529
2530
2531      // private
2532     dialogEl: false,
2533     bodyEl:  false,
2534     footerEl:  false,
2535     titleEl:  false,
2536     closeEl:  false,
2537
2538     size: '',
2539
2540
2541     onRender : function(ct, position)
2542     {
2543         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2544
2545         if(!this.el){
2546             var cfg = Roo.apply({},  this.getAutoCreate());
2547             cfg.id = Roo.id();
2548             //if(!cfg.name){
2549             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2550             //}
2551             //if (!cfg.name.length) {
2552             //    delete cfg.name;
2553            // }
2554             if (this.cls) {
2555                 cfg.cls += ' ' + this.cls;
2556             }
2557             if (this.style) {
2558                 cfg.style = this.style;
2559             }
2560             this.el = Roo.get(document.body).createChild(cfg, position);
2561         }
2562         //var type = this.el.dom.type;
2563
2564
2565         if(this.tabIndex !== undefined){
2566             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2567         }
2568
2569         this.dialogEl = this.el.select('.modal-dialog',true).first();
2570         this.bodyEl = this.el.select('.modal-body',true).first();
2571         this.closeEl = this.el.select('.modal-header .close', true).first();
2572         this.headerEl = this.el.select('.modal-header',true).first();
2573         this.titleEl = this.el.select('.modal-title',true).first();
2574         this.footerEl = this.el.select('.modal-footer',true).first();
2575
2576         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577         this.maskEl.enableDisplayMode("block");
2578         this.maskEl.hide();
2579         //this.el.addClass("x-dlg-modal");
2580
2581         if (this.buttons.length) {
2582             Roo.each(this.buttons, function(bb) {
2583                 var b = Roo.apply({}, bb);
2584                 b.xns = b.xns || Roo.bootstrap;
2585                 b.xtype = b.xtype || 'Button';
2586                 if (typeof(b.listeners) == 'undefined') {
2587                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2588                 }
2589
2590                 var btn = Roo.factory(b);
2591
2592                 btn.render(this.el.select('.modal-footer div').first());
2593
2594             },this);
2595         }
2596         // render the children.
2597         var nitems = [];
2598
2599         if(typeof(this.items) != 'undefined'){
2600             var items = this.items;
2601             delete this.items;
2602
2603             for(var i =0;i < items.length;i++) {
2604                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2605             }
2606         }
2607
2608         this.items = nitems;
2609
2610         // where are these used - they used to be body/close/footer
2611
2612
2613         this.initEvents();
2614         //this.el.addClass([this.fieldClass, this.cls]);
2615
2616     },
2617
2618     getAutoCreate : function(){
2619
2620
2621         var bdy = {
2622                 cls : 'modal-body',
2623                 html : this.html || ''
2624         };
2625
2626         var title = {
2627             tag: 'h4',
2628             cls : 'modal-title',
2629             html : this.title
2630         };
2631
2632         if(this.specificTitle){
2633             title = this.title;
2634
2635         };
2636
2637         var header = [];
2638         if (this.allow_close) {
2639             header.push({
2640                 tag: 'button',
2641                 cls : 'close',
2642                 html : '&times'
2643             });
2644         }
2645
2646         header.push(title);
2647
2648         var size = '';
2649
2650         if(this.size.length){
2651             size = 'modal-' + this.size;
2652         }
2653
2654         var modal = {
2655             cls: "modal",
2656             style : 'display: none',
2657             cn : [
2658                 {
2659                     cls: "modal-dialog " + size,
2660                     cn : [
2661                         {
2662                             cls : "modal-content",
2663                             cn : [
2664                                 {
2665                                     cls : 'modal-header',
2666                                     cn : header
2667                                 },
2668                                 bdy,
2669                                 {
2670                                     cls : 'modal-footer',
2671                                     cn : [
2672                                         {
2673                                             tag: 'div',
2674                                             cls: 'btn-' + this.buttonPosition
2675                                         }
2676                                     ]
2677
2678                                 }
2679
2680
2681                             ]
2682
2683                         }
2684                     ]
2685
2686                 }
2687             ]
2688         };
2689
2690         if(this.animate){
2691             modal.cls += ' fade';
2692         }
2693
2694         return modal;
2695
2696     },
2697     getChildContainer : function() {
2698
2699          return this.bodyEl;
2700
2701     },
2702     getButtonContainer : function() {
2703          return this.el.select('.modal-footer div',true).first();
2704
2705     },
2706     initEvents : function()
2707     {
2708         if (this.allow_close) {
2709             this.closeEl.on('click', this.hide, this);
2710         }
2711         Roo.EventManager.onWindowResize(this.resize, this, true);
2712
2713
2714     },
2715
2716     resize : function()
2717     {
2718         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2719         if (this.fitwindow) {
2720             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2722             this.setSize(w,h);
2723         }
2724     },
2725
2726     setSize : function(w,h)
2727     {
2728         if (!w && !h) {
2729             return;
2730         }
2731         this.resizeTo(w,h);
2732     },
2733
2734     show : function() {
2735
2736         if (!this.rendered) {
2737             this.render();
2738         }
2739
2740         this.el.setStyle('display', 'block');
2741
2742         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2743             var _this = this;
2744             (function(){
2745                 this.el.addClass('in');
2746             }).defer(50, this);
2747         }else{
2748             this.el.addClass('in');
2749
2750         }
2751
2752         // not sure how we can show data in here..
2753         //if (this.tmpl) {
2754         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2755         //}
2756
2757         Roo.get(document.body).addClass("x-body-masked");
2758         
2759         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2760         this.maskEl.setStyle('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     
7446     Roo.bootstrap.Form.popover.apply();
7447     
7448     this.addEvents({
7449         /**
7450          * @event clientvalidation
7451          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7452          * @param {Form} this
7453          * @param {Boolean} valid true if the form has passed client-side validation
7454          */
7455         clientvalidation: true,
7456         /**
7457          * @event beforeaction
7458          * Fires before any action is performed. Return false to cancel the action.
7459          * @param {Form} this
7460          * @param {Action} action The action to be performed
7461          */
7462         beforeaction: true,
7463         /**
7464          * @event actionfailed
7465          * Fires when an action fails.
7466          * @param {Form} this
7467          * @param {Action} action The action that failed
7468          */
7469         actionfailed : true,
7470         /**
7471          * @event actioncomplete
7472          * Fires when an action is completed.
7473          * @param {Form} this
7474          * @param {Action} action The action that completed
7475          */
7476         actioncomplete : true
7477     });
7478
7479 };
7480
7481 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7482
7483      /**
7484      * @cfg {String} method
7485      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7486      */
7487     method : 'POST',
7488     /**
7489      * @cfg {String} url
7490      * The URL to use for form actions if one isn't supplied in the action options.
7491      */
7492     /**
7493      * @cfg {Boolean} fileUpload
7494      * Set to true if this form is a file upload.
7495      */
7496
7497     /**
7498      * @cfg {Object} baseParams
7499      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7500      */
7501
7502     /**
7503      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7504      */
7505     timeout: 30,
7506     /**
7507      * @cfg {Sting} align (left|right) for navbar forms
7508      */
7509     align : 'left',
7510
7511     // private
7512     activeAction : null,
7513
7514     /**
7515      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7516      * element by passing it or its id or mask the form itself by passing in true.
7517      * @type Mixed
7518      */
7519     waitMsgTarget : false,
7520
7521     loadMask : true,
7522     
7523     /**
7524      * @cfg {Boolean} errPopover (true|false) default false
7525      */
7526     errPopover : false,
7527
7528     getAutoCreate : function(){
7529
7530         var cfg = {
7531             tag: 'form',
7532             method : this.method || 'POST',
7533             id : this.id || Roo.id(),
7534             cls : ''
7535         };
7536         if (this.parent().xtype.match(/^Nav/)) {
7537             cfg.cls = 'navbar-form navbar-' + this.align;
7538
7539         }
7540
7541         if (this.labelAlign == 'left' ) {
7542             cfg.cls += ' form-horizontal';
7543         }
7544
7545
7546         return cfg;
7547     },
7548     initEvents : function()
7549     {
7550         this.el.on('submit', this.onSubmit, this);
7551         // this was added as random key presses on the form where triggering form submit.
7552         this.el.on('keypress', function(e) {
7553             if (e.getCharCode() != 13) {
7554                 return true;
7555             }
7556             // we might need to allow it for textareas.. and some other items.
7557             // check e.getTarget().
7558
7559             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7560                 return true;
7561             }
7562
7563             Roo.log("keypress blocked");
7564
7565             e.preventDefault();
7566             return false;
7567         });
7568         
7569     },
7570     // private
7571     onSubmit : function(e){
7572         e.stopEvent();
7573     },
7574
7575      /**
7576      * Returns true if client-side validation on the form is successful.
7577      * @return Boolean
7578      */
7579     isValid : function(){
7580         var items = this.getItems();
7581         var valid = true;
7582         var target = false;
7583         
7584         items.each(function(f){
7585             
7586             if(f.validate()){
7587                 return;
7588             }
7589             
7590             valid = false;
7591
7592             if(!target && f.el.isVisible(true)){
7593                 target = f;
7594             }
7595            
7596         });
7597         
7598         if(this.errPopover && !valid){
7599             Roo.bootstrap.Form.popover.mask(this, target);
7600         }
7601         
7602         return valid;
7603     },
7604     
7605     /**
7606      * Returns true if any fields in this form have changed since their original load.
7607      * @return Boolean
7608      */
7609     isDirty : function(){
7610         var dirty = false;
7611         var items = this.getItems();
7612         items.each(function(f){
7613            if(f.isDirty()){
7614                dirty = true;
7615                return false;
7616            }
7617            return true;
7618         });
7619         return dirty;
7620     },
7621      /**
7622      * Performs a predefined action (submit or load) or custom actions you define on this form.
7623      * @param {String} actionName The name of the action type
7624      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7625      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7626      * accept other config options):
7627      * <pre>
7628 Property          Type             Description
7629 ----------------  ---------------  ----------------------------------------------------------------------------------
7630 url               String           The url for the action (defaults to the form's url)
7631 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7632 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7633 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7634                                    validate the form on the client (defaults to false)
7635      * </pre>
7636      * @return {BasicForm} this
7637      */
7638     doAction : function(action, options){
7639         if(typeof action == 'string'){
7640             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7641         }
7642         if(this.fireEvent('beforeaction', this, action) !== false){
7643             this.beforeAction(action);
7644             action.run.defer(100, action);
7645         }
7646         return this;
7647     },
7648
7649     // private
7650     beforeAction : function(action){
7651         var o = action.options;
7652
7653         if(this.loadMask){
7654             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7655         }
7656         // not really supported yet.. ??
7657
7658         //if(this.waitMsgTarget === true){
7659         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7660         //}else if(this.waitMsgTarget){
7661         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7662         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7663         //}else {
7664         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7665        // }
7666
7667     },
7668
7669     // private
7670     afterAction : function(action, success){
7671         this.activeAction = null;
7672         var o = action.options;
7673
7674         //if(this.waitMsgTarget === true){
7675             this.el.unmask();
7676         //}else if(this.waitMsgTarget){
7677         //    this.waitMsgTarget.unmask();
7678         //}else{
7679         //    Roo.MessageBox.updateProgress(1);
7680         //    Roo.MessageBox.hide();
7681        // }
7682         //
7683         if(success){
7684             if(o.reset){
7685                 this.reset();
7686             }
7687             Roo.callback(o.success, o.scope, [this, action]);
7688             this.fireEvent('actioncomplete', this, action);
7689
7690         }else{
7691
7692             // failure condition..
7693             // we have a scenario where updates need confirming.
7694             // eg. if a locking scenario exists..
7695             // we look for { errors : { needs_confirm : true }} in the response.
7696             if (
7697                 (typeof(action.result) != 'undefined')  &&
7698                 (typeof(action.result.errors) != 'undefined')  &&
7699                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7700            ){
7701                 var _t = this;
7702                 Roo.log("not supported yet");
7703                  /*
7704
7705                 Roo.MessageBox.confirm(
7706                     "Change requires confirmation",
7707                     action.result.errorMsg,
7708                     function(r) {
7709                         if (r != 'yes') {
7710                             return;
7711                         }
7712                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7713                     }
7714
7715                 );
7716                 */
7717
7718
7719                 return;
7720             }
7721
7722             Roo.callback(o.failure, o.scope, [this, action]);
7723             // show an error message if no failed handler is set..
7724             if (!this.hasListener('actionfailed')) {
7725                 Roo.log("need to add dialog support");
7726                 /*
7727                 Roo.MessageBox.alert("Error",
7728                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7729                         action.result.errorMsg :
7730                         "Saving Failed, please check your entries or try again"
7731                 );
7732                 */
7733             }
7734
7735             this.fireEvent('actionfailed', this, action);
7736         }
7737
7738     },
7739     /**
7740      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7741      * @param {String} id The value to search for
7742      * @return Field
7743      */
7744     findField : function(id){
7745         var items = this.getItems();
7746         var field = items.get(id);
7747         if(!field){
7748              items.each(function(f){
7749                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7750                     field = f;
7751                     return false;
7752                 }
7753                 return true;
7754             });
7755         }
7756         return field || null;
7757     },
7758      /**
7759      * Mark fields in this form invalid in bulk.
7760      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7761      * @return {BasicForm} this
7762      */
7763     markInvalid : function(errors){
7764         if(errors instanceof Array){
7765             for(var i = 0, len = errors.length; i < len; i++){
7766                 var fieldError = errors[i];
7767                 var f = this.findField(fieldError.id);
7768                 if(f){
7769                     f.markInvalid(fieldError.msg);
7770                 }
7771             }
7772         }else{
7773             var field, id;
7774             for(id in errors){
7775                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7776                     field.markInvalid(errors[id]);
7777                 }
7778             }
7779         }
7780         //Roo.each(this.childForms || [], function (f) {
7781         //    f.markInvalid(errors);
7782         //});
7783
7784         return this;
7785     },
7786
7787     /**
7788      * Set values for fields in this form in bulk.
7789      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7790      * @return {BasicForm} this
7791      */
7792     setValues : function(values){
7793         if(values instanceof Array){ // array of objects
7794             for(var i = 0, len = values.length; i < len; i++){
7795                 var v = values[i];
7796                 var f = this.findField(v.id);
7797                 if(f){
7798                     f.setValue(v.value);
7799                     if(this.trackResetOnLoad){
7800                         f.originalValue = f.getValue();
7801                     }
7802                 }
7803             }
7804         }else{ // object hash
7805             var field, id;
7806             for(id in values){
7807                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7808
7809                     if (field.setFromData &&
7810                         field.valueField &&
7811                         field.displayField &&
7812                         // combos' with local stores can
7813                         // be queried via setValue()
7814                         // to set their value..
7815                         (field.store && !field.store.isLocal)
7816                         ) {
7817                         // it's a combo
7818                         var sd = { };
7819                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7820                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7821                         field.setFromData(sd);
7822
7823                     } else {
7824                         field.setValue(values[id]);
7825                     }
7826
7827
7828                     if(this.trackResetOnLoad){
7829                         field.originalValue = field.getValue();
7830                     }
7831                 }
7832             }
7833         }
7834
7835         //Roo.each(this.childForms || [], function (f) {
7836         //    f.setValues(values);
7837         //});
7838
7839         return this;
7840     },
7841
7842     /**
7843      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7844      * they are returned as an array.
7845      * @param {Boolean} asString
7846      * @return {Object}
7847      */
7848     getValues : function(asString){
7849         //if (this.childForms) {
7850             // copy values from the child forms
7851         //    Roo.each(this.childForms, function (f) {
7852         //        this.setValues(f.getValues());
7853         //    }, this);
7854         //}
7855
7856
7857
7858         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7859         if(asString === true){
7860             return fs;
7861         }
7862         return Roo.urlDecode(fs);
7863     },
7864
7865     /**
7866      * Returns the fields in this form as an object with key/value pairs.
7867      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7868      * @return {Object}
7869      */
7870     getFieldValues : function(with_hidden)
7871     {
7872         var items = this.getItems();
7873         var ret = {};
7874         items.each(function(f){
7875             if (!f.getName()) {
7876                 return;
7877             }
7878             var v = f.getValue();
7879             if (f.inputType =='radio') {
7880                 if (typeof(ret[f.getName()]) == 'undefined') {
7881                     ret[f.getName()] = ''; // empty..
7882                 }
7883
7884                 if (!f.el.dom.checked) {
7885                     return;
7886
7887                 }
7888                 v = f.el.dom.value;
7889
7890             }
7891
7892             // not sure if this supported any more..
7893             if ((typeof(v) == 'object') && f.getRawValue) {
7894                 v = f.getRawValue() ; // dates..
7895             }
7896             // combo boxes where name != hiddenName...
7897             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7898                 ret[f.name] = f.getRawValue();
7899             }
7900             ret[f.getName()] = v;
7901         });
7902
7903         return ret;
7904     },
7905
7906     /**
7907      * Clears all invalid messages in this form.
7908      * @return {BasicForm} this
7909      */
7910     clearInvalid : function(){
7911         var items = this.getItems();
7912
7913         items.each(function(f){
7914            f.clearInvalid();
7915         });
7916
7917
7918
7919         return this;
7920     },
7921
7922     /**
7923      * Resets this form.
7924      * @return {BasicForm} this
7925      */
7926     reset : function(){
7927         var items = this.getItems();
7928         items.each(function(f){
7929             f.reset();
7930         });
7931
7932         Roo.each(this.childForms || [], function (f) {
7933             f.reset();
7934         });
7935
7936
7937         return this;
7938     },
7939     getItems : function()
7940     {
7941         var r=new Roo.util.MixedCollection(false, function(o){
7942             return o.id || (o.id = Roo.id());
7943         });
7944         var iter = function(el) {
7945             if (el.inputEl) {
7946                 r.add(el);
7947             }
7948             if (!el.items) {
7949                 return;
7950             }
7951             Roo.each(el.items,function(e) {
7952                 iter(e);
7953             });
7954
7955
7956         };
7957
7958         iter(this);
7959         return r;
7960
7961
7962
7963
7964     }
7965
7966 });
7967
7968 Roo.apply(Roo.bootstrap.Form, {
7969     
7970     popover : {
7971         
7972         isApplied : false,
7973         
7974         isMasked : false,
7975         
7976         form : false,
7977         
7978         target : false,
7979         
7980         oIndex : false,
7981         
7982         toolTip : false,
7983         
7984         intervalID : false,
7985     
7986         apply : function()
7987         {
7988             if(this.isApplied){
7989                 return;
7990             }
7991             
7992             this.toolTip = new Roo.bootstrap.Tooltip({
7993                 cls : 'roo-form-error-popover',
7994                 alignment : {
7995                     'left' : ['r-l', [-2,0], 'right'],
7996                     'right' : ['l-r', [2,0], 'left'],
7997                     'bottom' : ['tl-bl', [0,2], 'top'],
7998                     'top' : [ 'bl-tl', [0,-2], 'bottom']
7999                 }
8000             });
8001             
8002             this.toolTip.render(Roo.get(document.body));
8003
8004             this.toolTip.el.setVisibilityMode(Roo.Element.DISPLAY);
8005             
8006             Roo.get(document.body).on('click', function(){
8007                 this.unmask();
8008             }, this);
8009             
8010             this.isApplied = true
8011         },
8012         
8013         mask : function(form, target)
8014         {
8015             this.form = form;
8016             
8017             this.target = target;
8018             
8019             if(!this.form.errPopover){
8020                 return;
8021             }
8022
8023             this.oIndex = target.el.getStyle('z-index');
8024             
8025             this.target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8026         
8027             this.target.el.addClass('roo-invalid-outline');
8028             
8029             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8030             
8031             var scrolled = scrollable.getScroll();
8032             
8033             var ot = this.target.el.calcOffsetsTo(scrollable);
8034             
8035             var scrollTo = 0;
8036             
8037             if(ot[1] <= scrolled.top){
8038                 scrollTo = ot[1] - 100;
8039             } else {
8040                 scrollTo = ot[1] + Roo.lib.Dom.getViewHeight() - 100;
8041             }
8042             
8043             scrollable.scrollTo('top', scrollTo);
8044             
8045             this.toolTip.bindEl = this.target.el;
8046         
8047             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8048
8049             var tip = this.target.blankText;
8050
8051             if(this.target.getValue() !== '' && this.target.regexText.length){
8052                 tip = this.target.regexText;
8053             }
8054
8055             this.toolTip.show(tip);
8056             
8057             this.intervalID = window.setInterval(function() {
8058                 Roo.bootstrap.Form.popover.unmask();
8059             }, 10000);
8060
8061             window.onwheel = function(){ return false;};
8062             
8063             (function(){ this.isMasked = true; }).defer(500, this);
8064             
8065         },
8066         
8067         unmask : function()
8068         {
8069             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errPopover){
8070                 return;
8071             }
8072             
8073             if(this.oIndex){
8074                 this.target.el.setStyle('z-index', this.oIndex);
8075             }
8076             
8077             this.target.el.removeClass('roo-invalid-outline');
8078             
8079             this.toolTip.hide();
8080             
8081             this.toolTip.el.hide();
8082             
8083             window.onwheel = function(){ return true;};
8084             
8085             if(this.intervalID){
8086                 window.clearInterval(this.intervalID);
8087                 this.intervalID = false;
8088             }
8089             
8090             this.isMasked = false;
8091             
8092         }
8093         
8094     }
8095     
8096 });
8097
8098 /*
8099  * Based on:
8100  * Ext JS Library 1.1.1
8101  * Copyright(c) 2006-2007, Ext JS, LLC.
8102  *
8103  * Originally Released Under LGPL - original licence link has changed is not relivant.
8104  *
8105  * Fork - LGPL
8106  * <script type="text/javascript">
8107  */
8108 /**
8109  * @class Roo.form.VTypes
8110  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8111  * @singleton
8112  */
8113 Roo.form.VTypes = function(){
8114     // closure these in so they are only created once.
8115     var alpha = /^[a-zA-Z_]+$/;
8116     var alphanum = /^[a-zA-Z0-9_]+$/;
8117     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8118     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8119
8120     // All these messages and functions are configurable
8121     return {
8122         /**
8123          * The function used to validate email addresses
8124          * @param {String} value The email address
8125          */
8126         'email' : function(v){
8127             return email.test(v);
8128         },
8129         /**
8130          * The error text to display when the email validation function returns false
8131          * @type String
8132          */
8133         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8134         /**
8135          * The keystroke filter mask to be applied on email input
8136          * @type RegExp
8137          */
8138         'emailMask' : /[a-z0-9_\.\-@]/i,
8139
8140         /**
8141          * The function used to validate URLs
8142          * @param {String} value The URL
8143          */
8144         'url' : function(v){
8145             return url.test(v);
8146         },
8147         /**
8148          * The error text to display when the url validation function returns false
8149          * @type String
8150          */
8151         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8152         
8153         /**
8154          * The function used to validate alpha values
8155          * @param {String} value The value
8156          */
8157         'alpha' : function(v){
8158             return alpha.test(v);
8159         },
8160         /**
8161          * The error text to display when the alpha validation function returns false
8162          * @type String
8163          */
8164         'alphaText' : 'This field should only contain letters and _',
8165         /**
8166          * The keystroke filter mask to be applied on alpha input
8167          * @type RegExp
8168          */
8169         'alphaMask' : /[a-z_]/i,
8170
8171         /**
8172          * The function used to validate alphanumeric values
8173          * @param {String} value The value
8174          */
8175         'alphanum' : function(v){
8176             return alphanum.test(v);
8177         },
8178         /**
8179          * The error text to display when the alphanumeric validation function returns false
8180          * @type String
8181          */
8182         'alphanumText' : 'This field should only contain letters, numbers and _',
8183         /**
8184          * The keystroke filter mask to be applied on alphanumeric input
8185          * @type RegExp
8186          */
8187         'alphanumMask' : /[a-z0-9_]/i
8188     };
8189 }();/*
8190  * - LGPL
8191  *
8192  * Input
8193  * 
8194  */
8195
8196 /**
8197  * @class Roo.bootstrap.Input
8198  * @extends Roo.bootstrap.Component
8199  * Bootstrap Input class
8200  * @cfg {Boolean} disabled is it disabled
8201  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8202  * @cfg {String} name name of the input
8203  * @cfg {string} fieldLabel - the label associated
8204  * @cfg {string} placeholder - placeholder to put in text.
8205  * @cfg {string}  before - input group add on before
8206  * @cfg {string} after - input group add on after
8207  * @cfg {string} size - (lg|sm) or leave empty..
8208  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8209  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8210  * @cfg {Number} md colspan out of 12 for computer-sized screens
8211  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8212  * @cfg {string} value default value of the input
8213  * @cfg {Number} labelWidth set the width of label (0-12)
8214  * @cfg {String} labelAlign (top|left)
8215  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8216  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8217  * @cfg {String} indicatorpos (left|right) default left
8218
8219  * @cfg {String} align (left|center|right) Default left
8220  * @cfg {Boolean} forceFeedback (true|false) Default false
8221  * 
8222  * 
8223  * 
8224  * 
8225  * @constructor
8226  * Create a new Input
8227  * @param {Object} config The config object
8228  */
8229
8230 Roo.bootstrap.Input = function(config){
8231     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8232    
8233         this.addEvents({
8234             /**
8235              * @event focus
8236              * Fires when this field receives input focus.
8237              * @param {Roo.form.Field} this
8238              */
8239             focus : true,
8240             /**
8241              * @event blur
8242              * Fires when this field loses input focus.
8243              * @param {Roo.form.Field} this
8244              */
8245             blur : true,
8246             /**
8247              * @event specialkey
8248              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8249              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8250              * @param {Roo.form.Field} this
8251              * @param {Roo.EventObject} e The event object
8252              */
8253             specialkey : true,
8254             /**
8255              * @event change
8256              * Fires just before the field blurs if the field value has changed.
8257              * @param {Roo.form.Field} this
8258              * @param {Mixed} newValue The new value
8259              * @param {Mixed} oldValue The original value
8260              */
8261             change : true,
8262             /**
8263              * @event invalid
8264              * Fires after the field has been marked as invalid.
8265              * @param {Roo.form.Field} this
8266              * @param {String} msg The validation message
8267              */
8268             invalid : true,
8269             /**
8270              * @event valid
8271              * Fires after the field has been validated with no errors.
8272              * @param {Roo.form.Field} this
8273              */
8274             valid : true,
8275              /**
8276              * @event keyup
8277              * Fires after the key up
8278              * @param {Roo.form.Field} this
8279              * @param {Roo.EventObject}  e The event Object
8280              */
8281             keyup : true
8282         });
8283 };
8284
8285 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8286      /**
8287      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8288       automatic validation (defaults to "keyup").
8289      */
8290     validationEvent : "keyup",
8291      /**
8292      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8293      */
8294     validateOnBlur : true,
8295     /**
8296      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8297      */
8298     validationDelay : 250,
8299      /**
8300      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8301      */
8302     focusClass : "x-form-focus",  // not needed???
8303     
8304        
8305     /**
8306      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8307      */
8308     invalidClass : "has-warning",
8309     
8310     /**
8311      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8312      */
8313     validClass : "has-success",
8314     
8315     /**
8316      * @cfg {Boolean} hasFeedback (true|false) default true
8317      */
8318     hasFeedback : true,
8319     
8320     /**
8321      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8322      */
8323     invalidFeedbackClass : "glyphicon-warning-sign",
8324     
8325     /**
8326      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8327      */
8328     validFeedbackClass : "glyphicon-ok",
8329     
8330     /**
8331      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8332      */
8333     selectOnFocus : false,
8334     
8335      /**
8336      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8337      */
8338     maskRe : null,
8339        /**
8340      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8341      */
8342     vtype : null,
8343     
8344       /**
8345      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8346      */
8347     disableKeyFilter : false,
8348     
8349        /**
8350      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8351      */
8352     disabled : false,
8353      /**
8354      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8355      */
8356     allowBlank : true,
8357     /**
8358      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8359      */
8360     blankText : "This field is required",
8361     
8362      /**
8363      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8364      */
8365     minLength : 0,
8366     /**
8367      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8368      */
8369     maxLength : Number.MAX_VALUE,
8370     /**
8371      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8372      */
8373     minLengthText : "The minimum length for this field is {0}",
8374     /**
8375      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8376      */
8377     maxLengthText : "The maximum length for this field is {0}",
8378   
8379     
8380     /**
8381      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8382      * If available, this function will be called only after the basic validators all return true, and will be passed the
8383      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8384      */
8385     validator : null,
8386     /**
8387      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8388      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8389      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8390      */
8391     regex : null,
8392     /**
8393      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8394      */
8395     regexText : "",
8396     
8397     autocomplete: false,
8398     
8399     
8400     fieldLabel : '',
8401     inputType : 'text',
8402     
8403     name : false,
8404     placeholder: false,
8405     before : false,
8406     after : false,
8407     size : false,
8408     hasFocus : false,
8409     preventMark: false,
8410     isFormField : true,
8411     value : '',
8412     labelWidth : 2,
8413     labelAlign : false,
8414     readOnly : false,
8415     align : false,
8416     formatedValue : false,
8417     forceFeedback : false,
8418     
8419     indicatorpos : 'left',
8420     
8421     parentLabelAlign : function()
8422     {
8423         var parent = this;
8424         while (parent.parent()) {
8425             parent = parent.parent();
8426             if (typeof(parent.labelAlign) !='undefined') {
8427                 return parent.labelAlign;
8428             }
8429         }
8430         return 'left';
8431         
8432     },
8433     
8434     getAutoCreate : function()
8435     {
8436         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8437         
8438         var id = Roo.id();
8439         
8440         var cfg = {};
8441         
8442         if(this.inputType != 'hidden'){
8443             cfg.cls = 'form-group' //input-group
8444         }
8445         
8446         var input =  {
8447             tag: 'input',
8448             id : id,
8449             type : this.inputType,
8450             value : this.value,
8451             cls : 'form-control',
8452             placeholder : this.placeholder || '',
8453             autocomplete : this.autocomplete || 'new-password'
8454         };
8455         
8456         if(this.align){
8457             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8458         }
8459         
8460         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8461             input.maxLength = this.maxLength;
8462         }
8463         
8464         if (this.disabled) {
8465             input.disabled=true;
8466         }
8467         
8468         if (this.readOnly) {
8469             input.readonly=true;
8470         }
8471         
8472         if (this.name) {
8473             input.name = this.name;
8474         }
8475         
8476         if (this.size) {
8477             input.cls += ' input-' + this.size;
8478         }
8479         
8480         var settings=this;
8481         ['xs','sm','md','lg'].map(function(size){
8482             if (settings[size]) {
8483                 cfg.cls += ' col-' + size + '-' + settings[size];
8484             }
8485         });
8486         
8487         var inputblock = input;
8488         
8489         var feedback = {
8490             tag: 'span',
8491             cls: 'glyphicon form-control-feedback'
8492         };
8493             
8494         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8495             
8496             inputblock = {
8497                 cls : 'has-feedback',
8498                 cn :  [
8499                     input,
8500                     feedback
8501                 ] 
8502             };  
8503         }
8504         
8505         if (this.before || this.after) {
8506             
8507             inputblock = {
8508                 cls : 'input-group',
8509                 cn :  [] 
8510             };
8511             
8512             if (this.before && typeof(this.before) == 'string') {
8513                 
8514                 inputblock.cn.push({
8515                     tag :'span',
8516                     cls : 'roo-input-before input-group-addon',
8517                     html : this.before
8518                 });
8519             }
8520             if (this.before && typeof(this.before) == 'object') {
8521                 this.before = Roo.factory(this.before);
8522                 
8523                 inputblock.cn.push({
8524                     tag :'span',
8525                     cls : 'roo-input-before input-group-' +
8526                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8527                 });
8528             }
8529             
8530             inputblock.cn.push(input);
8531             
8532             if (this.after && typeof(this.after) == 'string') {
8533                 inputblock.cn.push({
8534                     tag :'span',
8535                     cls : 'roo-input-after input-group-addon',
8536                     html : this.after
8537                 });
8538             }
8539             if (this.after && typeof(this.after) == 'object') {
8540                 this.after = Roo.factory(this.after);
8541                 
8542                 inputblock.cn.push({
8543                     tag :'span',
8544                     cls : 'roo-input-after input-group-' +
8545                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8546                 });
8547             }
8548             
8549             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8550                 inputblock.cls += ' has-feedback';
8551                 inputblock.cn.push(feedback);
8552             }
8553         };
8554         
8555         if (align ==='left' && this.fieldLabel.length) {
8556             
8557             cfg.cn = [
8558                 {
8559                     tag : 'i',
8560                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8561                     tooltip : 'This field is required'
8562                 },
8563                 {
8564                     tag: 'label',
8565                     'for' :  id,
8566                     cls : 'control-label col-sm-' + this.labelWidth,
8567                     html : this.fieldLabel
8568
8569                 },
8570                 {
8571                     cls : "col-sm-" + (12 - this.labelWidth), 
8572                     cn: [
8573                         inputblock
8574                     ]
8575                 }
8576
8577             ];
8578             
8579             if(this.indicatorpos == 'right'){
8580                 cfg.cn = [
8581                     {
8582                         tag: 'label',
8583                         'for' :  id,
8584                         cls : 'control-label col-sm-' + this.labelWidth,
8585                         html : this.fieldLabel
8586
8587                     },
8588                     {
8589                         tag : 'i',
8590                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8591                         tooltip : 'This field is required'
8592                     },
8593                     {
8594                         cls : "col-sm-" + (12 - this.labelWidth), 
8595                         cn: [
8596                             inputblock
8597                         ]
8598                     }
8599
8600                 ];
8601             }
8602             
8603         } else if ( this.fieldLabel.length) {
8604                 
8605             cfg.cn = [
8606                 {
8607                     tag : 'i',
8608                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8609                     tooltip : 'This field is required'
8610                 },
8611                 {
8612                     tag: 'label',
8613                    //cls : 'input-group-addon',
8614                     html : this.fieldLabel
8615
8616                 },
8617
8618                inputblock
8619
8620            ];
8621            
8622            if(this.indicatorpos == 'right'){
8623                 
8624                 cfg.cn = [
8625                     {
8626                         tag: 'label',
8627                        //cls : 'input-group-addon',
8628                         html : this.fieldLabel
8629
8630                     },
8631                     {
8632                         tag : 'i',
8633                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8634                         tooltip : 'This field is required'
8635                     },
8636
8637                    inputblock
8638
8639                ];
8640
8641             }
8642
8643         } else {
8644             
8645             cfg.cn = [
8646
8647                     inputblock
8648
8649             ];
8650                 
8651                 
8652         };
8653         
8654         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8655            cfg.cls += ' navbar-form';
8656         }
8657         
8658         if (this.parentType === 'NavGroup') {
8659            cfg.cls += ' navbar-form';
8660            cfg.tag = 'li';
8661         }
8662         
8663         return cfg;
8664         
8665     },
8666     /**
8667      * return the real input element.
8668      */
8669     inputEl: function ()
8670     {
8671         return this.el.select('input.form-control',true).first();
8672     },
8673     
8674     tooltipEl : function()
8675     {
8676         return this.inputEl();
8677     },
8678     
8679     indicatorEl : function()
8680     {
8681         var indicator = this.el.select('i.roo-required-indicator',true).first();
8682         
8683         if(!indicator){
8684             return false;
8685         }
8686         
8687         return indicator;
8688         
8689     },
8690     
8691     setDisabled : function(v)
8692     {
8693         var i  = this.inputEl().dom;
8694         if (!v) {
8695             i.removeAttribute('disabled');
8696             return;
8697             
8698         }
8699         i.setAttribute('disabled','true');
8700     },
8701     initEvents : function()
8702     {
8703           
8704         this.inputEl().on("keydown" , this.fireKey,  this);
8705         this.inputEl().on("focus", this.onFocus,  this);
8706         this.inputEl().on("blur", this.onBlur,  this);
8707         
8708         this.inputEl().relayEvent('keyup', this);
8709         
8710         this.indicator = this.indicatorEl();
8711         
8712         if(this.indicator){
8713             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8714             this.indicator.hide();
8715         }
8716  
8717         // reference to original value for reset
8718         this.originalValue = this.getValue();
8719         //Roo.form.TextField.superclass.initEvents.call(this);
8720         if(this.validationEvent == 'keyup'){
8721             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8722             this.inputEl().on('keyup', this.filterValidation, this);
8723         }
8724         else if(this.validationEvent !== false){
8725             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8726         }
8727         
8728         if(this.selectOnFocus){
8729             this.on("focus", this.preFocus, this);
8730             
8731         }
8732         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8733             this.inputEl().on("keypress", this.filterKeys, this);
8734         } else {
8735             this.inputEl().relayEvent('keypress', this);
8736         }
8737        /* if(this.grow){
8738             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8739             this.el.on("click", this.autoSize,  this);
8740         }
8741         */
8742         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8743             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8744         }
8745         
8746         if (typeof(this.before) == 'object') {
8747             this.before.render(this.el.select('.roo-input-before',true).first());
8748         }
8749         if (typeof(this.after) == 'object') {
8750             this.after.render(this.el.select('.roo-input-after',true).first());
8751         }
8752         
8753         
8754     },
8755     filterValidation : function(e){
8756         if(!e.isNavKeyPress()){
8757             this.validationTask.delay(this.validationDelay);
8758         }
8759     },
8760      /**
8761      * Validates the field value
8762      * @return {Boolean} True if the value is valid, else false
8763      */
8764     validate : function(){
8765         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8766         if(this.disabled || this.validateValue(this.getRawValue())){
8767             this.markValid();
8768             return true;
8769         }
8770         
8771         this.markInvalid();
8772         return false;
8773     },
8774     
8775     
8776     /**
8777      * Validates a value according to the field's validation rules and marks the field as invalid
8778      * if the validation fails
8779      * @param {Mixed} value The value to validate
8780      * @return {Boolean} True if the value is valid, else false
8781      */
8782     validateValue : function(value){
8783         if(value.length < 1)  { // if it's blank
8784             if(this.allowBlank){
8785                 return true;
8786             }
8787             return false;
8788         }
8789         
8790         if(value.length < this.minLength){
8791             return false;
8792         }
8793         if(value.length > this.maxLength){
8794             return false;
8795         }
8796         if(this.vtype){
8797             var vt = Roo.form.VTypes;
8798             if(!vt[this.vtype](value, this)){
8799                 return false;
8800             }
8801         }
8802         if(typeof this.validator == "function"){
8803             var msg = this.validator(value);
8804             if(msg !== true){
8805                 return false;
8806             }
8807         }
8808         
8809         if(this.regex && !this.regex.test(value)){
8810             return false;
8811         }
8812         
8813         return true;
8814     },
8815
8816     
8817     
8818      // private
8819     fireKey : function(e){
8820         //Roo.log('field ' + e.getKey());
8821         if(e.isNavKeyPress()){
8822             this.fireEvent("specialkey", this, e);
8823         }
8824     },
8825     focus : function (selectText){
8826         if(this.rendered){
8827             this.inputEl().focus();
8828             if(selectText === true){
8829                 this.inputEl().dom.select();
8830             }
8831         }
8832         return this;
8833     } ,
8834     
8835     onFocus : function(){
8836         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8837            // this.el.addClass(this.focusClass);
8838         }
8839         if(!this.hasFocus){
8840             this.hasFocus = true;
8841             this.startValue = this.getValue();
8842             this.fireEvent("focus", this);
8843         }
8844     },
8845     
8846     beforeBlur : Roo.emptyFn,
8847
8848     
8849     // private
8850     onBlur : function(){
8851         this.beforeBlur();
8852         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8853             //this.el.removeClass(this.focusClass);
8854         }
8855         this.hasFocus = false;
8856         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8857             this.validate();
8858         }
8859         var v = this.getValue();
8860         if(String(v) !== String(this.startValue)){
8861             this.fireEvent('change', this, v, this.startValue);
8862         }
8863         this.fireEvent("blur", this);
8864     },
8865     
8866     /**
8867      * Resets the current field value to the originally loaded value and clears any validation messages
8868      */
8869     reset : function(){
8870         this.setValue(this.originalValue);
8871         this.validate();
8872     },
8873      /**
8874      * Returns the name of the field
8875      * @return {Mixed} name The name field
8876      */
8877     getName: function(){
8878         return this.name;
8879     },
8880      /**
8881      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8882      * @return {Mixed} value The field value
8883      */
8884     getValue : function(){
8885         
8886         var v = this.inputEl().getValue();
8887         
8888         return v;
8889     },
8890     /**
8891      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8892      * @return {Mixed} value The field value
8893      */
8894     getRawValue : function(){
8895         var v = this.inputEl().getValue();
8896         
8897         return v;
8898     },
8899     
8900     /**
8901      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8902      * @param {Mixed} value The value to set
8903      */
8904     setRawValue : function(v){
8905         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8906     },
8907     
8908     selectText : function(start, end){
8909         var v = this.getRawValue();
8910         if(v.length > 0){
8911             start = start === undefined ? 0 : start;
8912             end = end === undefined ? v.length : end;
8913             var d = this.inputEl().dom;
8914             if(d.setSelectionRange){
8915                 d.setSelectionRange(start, end);
8916             }else if(d.createTextRange){
8917                 var range = d.createTextRange();
8918                 range.moveStart("character", start);
8919                 range.moveEnd("character", v.length-end);
8920                 range.select();
8921             }
8922         }
8923     },
8924     
8925     /**
8926      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8927      * @param {Mixed} value The value to set
8928      */
8929     setValue : function(v){
8930         this.value = v;
8931         if(this.rendered){
8932             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8933             this.validate();
8934         }
8935     },
8936     
8937     /*
8938     processValue : function(value){
8939         if(this.stripCharsRe){
8940             var newValue = value.replace(this.stripCharsRe, '');
8941             if(newValue !== value){
8942                 this.setRawValue(newValue);
8943                 return newValue;
8944             }
8945         }
8946         return value;
8947     },
8948   */
8949     preFocus : function(){
8950         
8951         if(this.selectOnFocus){
8952             this.inputEl().dom.select();
8953         }
8954     },
8955     filterKeys : function(e){
8956         var k = e.getKey();
8957         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8958             return;
8959         }
8960         var c = e.getCharCode(), cc = String.fromCharCode(c);
8961         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8962             return;
8963         }
8964         if(!this.maskRe.test(cc)){
8965             e.stopEvent();
8966         }
8967     },
8968      /**
8969      * Clear any invalid styles/messages for this field
8970      */
8971     clearInvalid : function(){
8972         
8973         if(!this.el || this.preventMark){ // not rendered
8974             return;
8975         }
8976         
8977         if(this.indicator){
8978             this.indicator.hide();
8979         }
8980         
8981         this.el.removeClass(this.invalidClass);
8982         
8983         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8984             
8985             var feedback = this.el.select('.form-control-feedback', true).first();
8986             
8987             if(feedback){
8988                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8989             }
8990             
8991         }
8992         
8993         this.fireEvent('valid', this);
8994     },
8995     
8996      /**
8997      * Mark this field as valid
8998      */
8999     markValid : function()
9000     {
9001         if(!this.el  || this.preventMark){ // not rendered
9002             return;
9003         }
9004         
9005         this.el.removeClass([this.invalidClass, this.validClass]);
9006         
9007         var feedback = this.el.select('.form-control-feedback', true).first();
9008             
9009         if(feedback){
9010             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9011         }
9012
9013         if(this.disabled){
9014             return;
9015         }
9016         
9017         if(this.allowBlank && !this.getRawValue().length){
9018             return;
9019         }
9020         
9021         if(this.indicator){
9022             this.indicator.hide();
9023         }
9024         
9025         this.el.addClass(this.validClass);
9026         
9027         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9028             
9029             var feedback = this.el.select('.form-control-feedback', true).first();
9030             
9031             if(feedback){
9032                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9033                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9034             }
9035             
9036         }
9037         
9038         this.fireEvent('valid', this);
9039     },
9040     
9041      /**
9042      * Mark this field as invalid
9043      * @param {String} msg The validation message
9044      */
9045     markInvalid : function(msg)
9046     {
9047         if(!this.el  || this.preventMark){ // not rendered
9048             return;
9049         }
9050         
9051         this.el.removeClass([this.invalidClass, this.validClass]);
9052         
9053         var feedback = this.el.select('.form-control-feedback', true).first();
9054             
9055         if(feedback){
9056             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9057         }
9058
9059         if(this.disabled){
9060             return;
9061         }
9062         
9063         if(this.allowBlank && !this.getRawValue().length){
9064             return;
9065         }
9066         
9067         if(this.indicator){
9068             this.indicator.show();
9069         }
9070         
9071         this.el.addClass(this.invalidClass);
9072         
9073         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9074             
9075             var feedback = this.el.select('.form-control-feedback', true).first();
9076             
9077             if(feedback){
9078                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9079                 
9080                 if(this.getValue().length || this.forceFeedback){
9081                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9082                 }
9083                 
9084             }
9085             
9086         }
9087         
9088         this.fireEvent('invalid', this, msg);
9089     },
9090     // private
9091     SafariOnKeyDown : function(event)
9092     {
9093         // this is a workaround for a password hang bug on chrome/ webkit.
9094         if (this.inputEl().dom.type != 'password') {
9095             return;
9096         }
9097         
9098         var isSelectAll = false;
9099         
9100         if(this.inputEl().dom.selectionEnd > 0){
9101             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9102         }
9103         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9104             event.preventDefault();
9105             this.setValue('');
9106             return;
9107         }
9108         
9109         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9110             
9111             event.preventDefault();
9112             // this is very hacky as keydown always get's upper case.
9113             //
9114             var cc = String.fromCharCode(event.getCharCode());
9115             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9116             
9117         }
9118     },
9119     adjustWidth : function(tag, w){
9120         tag = tag.toLowerCase();
9121         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9122             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9123                 if(tag == 'input'){
9124                     return w + 2;
9125                 }
9126                 if(tag == 'textarea'){
9127                     return w-2;
9128                 }
9129             }else if(Roo.isOpera){
9130                 if(tag == 'input'){
9131                     return w + 2;
9132                 }
9133                 if(tag == 'textarea'){
9134                     return w-2;
9135                 }
9136             }
9137         }
9138         return w;
9139     }
9140     
9141 });
9142
9143  
9144 /*
9145  * - LGPL
9146  *
9147  * Input
9148  * 
9149  */
9150
9151 /**
9152  * @class Roo.bootstrap.TextArea
9153  * @extends Roo.bootstrap.Input
9154  * Bootstrap TextArea class
9155  * @cfg {Number} cols Specifies the visible width of a text area
9156  * @cfg {Number} rows Specifies the visible number of lines in a text area
9157  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9158  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9159  * @cfg {string} html text
9160  * 
9161  * @constructor
9162  * Create a new TextArea
9163  * @param {Object} config The config object
9164  */
9165
9166 Roo.bootstrap.TextArea = function(config){
9167     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9168    
9169 };
9170
9171 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9172      
9173     cols : false,
9174     rows : 5,
9175     readOnly : false,
9176     warp : 'soft',
9177     resize : false,
9178     value: false,
9179     html: false,
9180     
9181     getAutoCreate : function(){
9182         
9183         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9184         
9185         var id = Roo.id();
9186         
9187         var cfg = {};
9188         
9189         var input =  {
9190             tag: 'textarea',
9191             id : id,
9192             warp : this.warp,
9193             rows : this.rows,
9194             value : this.value || '',
9195             html: this.html || '',
9196             cls : 'form-control',
9197             placeholder : this.placeholder || '' 
9198             
9199         };
9200         
9201         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9202             input.maxLength = this.maxLength;
9203         }
9204         
9205         if(this.resize){
9206             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9207         }
9208         
9209         if(this.cols){
9210             input.cols = this.cols;
9211         }
9212         
9213         if (this.readOnly) {
9214             input.readonly = true;
9215         }
9216         
9217         if (this.name) {
9218             input.name = this.name;
9219         }
9220         
9221         if (this.size) {
9222             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9223         }
9224         
9225         var settings=this;
9226         ['xs','sm','md','lg'].map(function(size){
9227             if (settings[size]) {
9228                 cfg.cls += ' col-' + size + '-' + settings[size];
9229             }
9230         });
9231         
9232         var inputblock = input;
9233         
9234         if(this.hasFeedback && !this.allowBlank){
9235             
9236             var feedback = {
9237                 tag: 'span',
9238                 cls: 'glyphicon form-control-feedback'
9239             };
9240
9241             inputblock = {
9242                 cls : 'has-feedback',
9243                 cn :  [
9244                     input,
9245                     feedback
9246                 ] 
9247             };  
9248         }
9249         
9250         
9251         if (this.before || this.after) {
9252             
9253             inputblock = {
9254                 cls : 'input-group',
9255                 cn :  [] 
9256             };
9257             if (this.before) {
9258                 inputblock.cn.push({
9259                     tag :'span',
9260                     cls : 'input-group-addon',
9261                     html : this.before
9262                 });
9263             }
9264             
9265             inputblock.cn.push(input);
9266             
9267             if(this.hasFeedback && !this.allowBlank){
9268                 inputblock.cls += ' has-feedback';
9269                 inputblock.cn.push(feedback);
9270             }
9271             
9272             if (this.after) {
9273                 inputblock.cn.push({
9274                     tag :'span',
9275                     cls : 'input-group-addon',
9276                     html : this.after
9277                 });
9278             }
9279             
9280         }
9281         
9282         if (align ==='left' && this.fieldLabel.length) {
9283 //                Roo.log("left and has label");
9284                 cfg.cn = [
9285                     
9286                     {
9287                         tag: 'label',
9288                         'for' :  id,
9289                         cls : 'control-label col-sm-' + this.labelWidth,
9290                         html : this.fieldLabel
9291                         
9292                     },
9293                     {
9294                         cls : "col-sm-" + (12 - this.labelWidth), 
9295                         cn: [
9296                             inputblock
9297                         ]
9298                     }
9299                     
9300                 ];
9301         } else if ( this.fieldLabel.length) {
9302 //                Roo.log(" label");
9303                  cfg.cn = [
9304                    
9305                     {
9306                         tag: 'label',
9307                         //cls : 'input-group-addon',
9308                         html : this.fieldLabel
9309                         
9310                     },
9311                     
9312                     inputblock
9313                     
9314                 ];
9315
9316         } else {
9317             
9318 //                   Roo.log(" no label && no align");
9319                 cfg.cn = [
9320                     
9321                         inputblock
9322                     
9323                 ];
9324                 
9325                 
9326         }
9327         
9328         if (this.disabled) {
9329             input.disabled=true;
9330         }
9331         
9332         return cfg;
9333         
9334     },
9335     /**
9336      * return the real textarea element.
9337      */
9338     inputEl: function ()
9339     {
9340         return this.el.select('textarea.form-control',true).first();
9341     },
9342     
9343     /**
9344      * Clear any invalid styles/messages for this field
9345      */
9346     clearInvalid : function()
9347     {
9348         
9349         if(!this.el || this.preventMark){ // not rendered
9350             return;
9351         }
9352         
9353         var label = this.el.select('label', true).first();
9354         var icon = this.el.select('i.fa-star', true).first();
9355         
9356         if(label && icon){
9357             icon.remove();
9358         }
9359         
9360         this.el.removeClass(this.invalidClass);
9361         
9362         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9363             
9364             var feedback = this.el.select('.form-control-feedback', true).first();
9365             
9366             if(feedback){
9367                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9368             }
9369             
9370         }
9371         
9372         this.fireEvent('valid', this);
9373     },
9374     
9375      /**
9376      * Mark this field as valid
9377      */
9378     markValid : function()
9379     {
9380         if(!this.el  || this.preventMark){ // not rendered
9381             return;
9382         }
9383         
9384         this.el.removeClass([this.invalidClass, this.validClass]);
9385         
9386         var feedback = this.el.select('.form-control-feedback', true).first();
9387             
9388         if(feedback){
9389             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9390         }
9391
9392         if(this.disabled || this.allowBlank){
9393             return;
9394         }
9395         
9396         var label = this.el.select('label', true).first();
9397         var icon = this.el.select('i.fa-star', true).first();
9398         
9399         if(label && icon){
9400             icon.remove();
9401         }
9402         
9403         this.el.addClass(this.validClass);
9404         
9405         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9406             
9407             var feedback = this.el.select('.form-control-feedback', true).first();
9408             
9409             if(feedback){
9410                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9411                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9412             }
9413             
9414         }
9415         
9416         this.fireEvent('valid', this);
9417     },
9418     
9419      /**
9420      * Mark this field as invalid
9421      * @param {String} msg The validation message
9422      */
9423     markInvalid : function(msg)
9424     {
9425         if(!this.el  || this.preventMark){ // not rendered
9426             return;
9427         }
9428         
9429         this.el.removeClass([this.invalidClass, this.validClass]);
9430         
9431         var feedback = this.el.select('.form-control-feedback', true).first();
9432             
9433         if(feedback){
9434             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9435         }
9436
9437         if(this.disabled || this.allowBlank){
9438             return;
9439         }
9440         
9441         var label = this.el.select('label', true).first();
9442         var icon = this.el.select('i.fa-star', true).first();
9443         
9444         if(!this.getValue().length && label && !icon){
9445             this.el.createChild({
9446                 tag : 'i',
9447                 cls : 'text-danger fa fa-lg fa-star',
9448                 tooltip : 'This field is required',
9449                 style : 'margin-right:5px;'
9450             }, label, true);
9451         }
9452
9453         this.el.addClass(this.invalidClass);
9454         
9455         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9456             
9457             var feedback = this.el.select('.form-control-feedback', true).first();
9458             
9459             if(feedback){
9460                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9461                 
9462                 if(this.getValue().length || this.forceFeedback){
9463                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9464                 }
9465                 
9466             }
9467             
9468         }
9469         
9470         this.fireEvent('invalid', this, msg);
9471     }
9472 });
9473
9474  
9475 /*
9476  * - LGPL
9477  *
9478  * trigger field - base class for combo..
9479  * 
9480  */
9481  
9482 /**
9483  * @class Roo.bootstrap.TriggerField
9484  * @extends Roo.bootstrap.Input
9485  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9486  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9487  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9488  * for which you can provide a custom implementation.  For example:
9489  * <pre><code>
9490 var trigger = new Roo.bootstrap.TriggerField();
9491 trigger.onTriggerClick = myTriggerFn;
9492 trigger.applyTo('my-field');
9493 </code></pre>
9494  *
9495  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9496  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9497  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9498  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9499  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9500
9501  * @constructor
9502  * Create a new TriggerField.
9503  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9504  * to the base TextField)
9505  */
9506 Roo.bootstrap.TriggerField = function(config){
9507     this.mimicing = false;
9508     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9509 };
9510
9511 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9512     /**
9513      * @cfg {String} triggerClass A CSS class to apply to the trigger
9514      */
9515      /**
9516      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9517      */
9518     hideTrigger:false,
9519
9520     /**
9521      * @cfg {Boolean} removable (true|false) special filter default false
9522      */
9523     removable : false,
9524     
9525     /** @cfg {Boolean} grow @hide */
9526     /** @cfg {Number} growMin @hide */
9527     /** @cfg {Number} growMax @hide */
9528
9529     /**
9530      * @hide 
9531      * @method
9532      */
9533     autoSize: Roo.emptyFn,
9534     // private
9535     monitorTab : true,
9536     // private
9537     deferHeight : true,
9538
9539     
9540     actionMode : 'wrap',
9541     
9542     caret : false,
9543     
9544     
9545     getAutoCreate : function(){
9546        
9547         var align = this.labelAlign || this.parentLabelAlign();
9548         
9549         var id = Roo.id();
9550         
9551         var cfg = {
9552             cls: 'form-group' //input-group
9553         };
9554         
9555         
9556         var input =  {
9557             tag: 'input',
9558             id : id,
9559             type : this.inputType,
9560             cls : 'form-control',
9561             autocomplete: 'new-password',
9562             placeholder : this.placeholder || '' 
9563             
9564         };
9565         if (this.name) {
9566             input.name = this.name;
9567         }
9568         if (this.size) {
9569             input.cls += ' input-' + this.size;
9570         }
9571         
9572         if (this.disabled) {
9573             input.disabled=true;
9574         }
9575         
9576         var inputblock = input;
9577         
9578         if(this.hasFeedback && !this.allowBlank){
9579             
9580             var feedback = {
9581                 tag: 'span',
9582                 cls: 'glyphicon form-control-feedback'
9583             };
9584             
9585             if(this.removable && !this.editable && !this.tickable){
9586                 inputblock = {
9587                     cls : 'has-feedback',
9588                     cn :  [
9589                         inputblock,
9590                         {
9591                             tag: 'button',
9592                             html : 'x',
9593                             cls : 'roo-combo-removable-btn close'
9594                         },
9595                         feedback
9596                     ] 
9597                 };
9598             } else {
9599                 inputblock = {
9600                     cls : 'has-feedback',
9601                     cn :  [
9602                         inputblock,
9603                         feedback
9604                     ] 
9605                 };
9606             }
9607
9608         } else {
9609             if(this.removable && !this.editable && !this.tickable){
9610                 inputblock = {
9611                     cls : 'roo-removable',
9612                     cn :  [
9613                         inputblock,
9614                         {
9615                             tag: 'button',
9616                             html : 'x',
9617                             cls : 'roo-combo-removable-btn close'
9618                         }
9619                     ] 
9620                 };
9621             }
9622         }
9623         
9624         if (this.before || this.after) {
9625             
9626             inputblock = {
9627                 cls : 'input-group',
9628                 cn :  [] 
9629             };
9630             if (this.before) {
9631                 inputblock.cn.push({
9632                     tag :'span',
9633                     cls : 'input-group-addon',
9634                     html : this.before
9635                 });
9636             }
9637             
9638             inputblock.cn.push(input);
9639             
9640             if(this.hasFeedback && !this.allowBlank){
9641                 inputblock.cls += ' has-feedback';
9642                 inputblock.cn.push(feedback);
9643             }
9644             
9645             if (this.after) {
9646                 inputblock.cn.push({
9647                     tag :'span',
9648                     cls : 'input-group-addon',
9649                     html : this.after
9650                 });
9651             }
9652             
9653         };
9654         
9655         var box = {
9656             tag: 'div',
9657             cn: [
9658                 {
9659                     tag: 'input',
9660                     type : 'hidden',
9661                     cls: 'form-hidden-field'
9662                 },
9663                 inputblock
9664             ]
9665             
9666         };
9667         
9668         if(this.multiple){
9669             box = {
9670                 tag: 'div',
9671                 cn: [
9672                     {
9673                         tag: 'input',
9674                         type : 'hidden',
9675                         cls: 'form-hidden-field'
9676                     },
9677                     {
9678                         tag: 'ul',
9679                         cls: 'roo-select2-choices',
9680                         cn:[
9681                             {
9682                                 tag: 'li',
9683                                 cls: 'roo-select2-search-field',
9684                                 cn: [
9685
9686                                     inputblock
9687                                 ]
9688                             }
9689                         ]
9690                     }
9691                 ]
9692             }
9693         };
9694         
9695         var combobox = {
9696             cls: 'roo-select2-container input-group',
9697             cn: [
9698                 box
9699 //                {
9700 //                    tag: 'ul',
9701 //                    cls: 'typeahead typeahead-long dropdown-menu',
9702 //                    style: 'display:none'
9703 //                }
9704             ]
9705         };
9706         
9707         if(!this.multiple && this.showToggleBtn){
9708             
9709             var caret = {
9710                         tag: 'span',
9711                         cls: 'caret'
9712              };
9713             if (this.caret != false) {
9714                 caret = {
9715                      tag: 'i',
9716                      cls: 'fa fa-' + this.caret
9717                 };
9718                 
9719             }
9720             
9721             combobox.cn.push({
9722                 tag :'span',
9723                 cls : 'input-group-addon btn dropdown-toggle',
9724                 cn : [
9725                     caret,
9726                     {
9727                         tag: 'span',
9728                         cls: 'combobox-clear',
9729                         cn  : [
9730                             {
9731                                 tag : 'i',
9732                                 cls: 'icon-remove'
9733                             }
9734                         ]
9735                     }
9736                 ]
9737
9738             })
9739         }
9740         
9741         if(this.multiple){
9742             combobox.cls += ' roo-select2-container-multi';
9743         }
9744         
9745         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9746             
9747 //                Roo.log("left and has label");
9748             cfg.cn = [
9749                 {
9750                     tag : 'i',
9751                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9752                     tooltip : 'This field is required'
9753                 },
9754                 {
9755                     tag: 'label',
9756                     'for' :  id,
9757                     cls : 'control-label col-sm-' + this.labelWidth,
9758                     html : this.fieldLabel
9759
9760                 },
9761                 {
9762                     cls : "col-sm-" + (12 - this.labelWidth), 
9763                     cn: [
9764                         combobox
9765                     ]
9766                 }
9767
9768             ];
9769             
9770             if(this.indicatorpos == 'right'){
9771                 cfg.cn = [
9772                     {
9773                         tag: 'label',
9774                         'for' :  id,
9775                         cls : 'control-label col-sm-' + this.labelWidth,
9776                         html : this.fieldLabel
9777
9778                     },
9779                     {
9780                         tag : 'i',
9781                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9782                         tooltip : 'This field is required'
9783                     },
9784                     {
9785                         cls : "col-sm-" + (12 - this.labelWidth), 
9786                         cn: [
9787                             combobox
9788                         ]
9789                     }
9790
9791                 ];
9792             }
9793             
9794         } else if ( this.fieldLabel.length) {
9795 //                Roo.log(" label");
9796             cfg.cn = [
9797                 {
9798                    tag : 'i',
9799                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9800                    tooltip : 'This field is required'
9801                },
9802                {
9803                    tag: 'label',
9804                    //cls : 'input-group-addon',
9805                    html : this.fieldLabel
9806
9807                },
9808
9809                combobox
9810
9811             ];
9812             
9813             if(this.indicatorpos == 'right'){
9814                 
9815                 cfg.cn = [
9816                     {
9817                        tag: 'label',
9818                        //cls : 'input-group-addon',
9819                        html : this.fieldLabel
9820
9821                     },
9822                     {
9823                        tag : 'i',
9824                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9825                        tooltip : 'This field is required'
9826                     },
9827                     
9828                     combobox
9829
9830                 ];
9831
9832             }
9833
9834         } else {
9835             
9836 //                Roo.log(" no label && no align");
9837                 cfg = combobox
9838                      
9839                 
9840         }
9841          
9842         var settings=this;
9843         ['xs','sm','md','lg'].map(function(size){
9844             if (settings[size]) {
9845                 cfg.cls += ' col-' + size + '-' + settings[size];
9846             }
9847         });
9848         
9849         return cfg;
9850         
9851     },
9852     
9853     
9854     
9855     // private
9856     onResize : function(w, h){
9857 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9858 //        if(typeof w == 'number'){
9859 //            var x = w - this.trigger.getWidth();
9860 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9861 //            this.trigger.setStyle('left', x+'px');
9862 //        }
9863     },
9864
9865     // private
9866     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9867
9868     // private
9869     getResizeEl : function(){
9870         return this.inputEl();
9871     },
9872
9873     // private
9874     getPositionEl : function(){
9875         return this.inputEl();
9876     },
9877
9878     // private
9879     alignErrorIcon : function(){
9880         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9881     },
9882
9883     // private
9884     initEvents : function(){
9885         
9886         this.createList();
9887         
9888         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9889         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9890         if(!this.multiple && this.showToggleBtn){
9891             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9892             if(this.hideTrigger){
9893                 this.trigger.setDisplayed(false);
9894             }
9895             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9896         }
9897         
9898         if(this.multiple){
9899             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9900         }
9901         
9902         if(this.removable && !this.editable && !this.tickable){
9903             var close = this.closeTriggerEl();
9904             
9905             if(close){
9906                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9907                 close.on('click', this.removeBtnClick, this, close);
9908             }
9909         }
9910         
9911         //this.trigger.addClassOnOver('x-form-trigger-over');
9912         //this.trigger.addClassOnClick('x-form-trigger-click');
9913         
9914         //if(!this.width){
9915         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9916         //}
9917     },
9918     
9919     closeTriggerEl : function()
9920     {
9921         var close = this.el.select('.roo-combo-removable-btn', true).first();
9922         return close ? close : false;
9923     },
9924     
9925     removeBtnClick : function(e, h, el)
9926     {
9927         e.preventDefault();
9928         
9929         if(this.fireEvent("remove", this) !== false){
9930             this.reset();
9931             this.fireEvent("afterremove", this)
9932         }
9933     },
9934     
9935     createList : function()
9936     {
9937         this.list = Roo.get(document.body).createChild({
9938             tag: 'ul',
9939             cls: 'typeahead typeahead-long dropdown-menu',
9940             style: 'display:none'
9941         });
9942         
9943         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9944         
9945     },
9946
9947     // private
9948     initTrigger : function(){
9949        
9950     },
9951
9952     // private
9953     onDestroy : function(){
9954         if(this.trigger){
9955             this.trigger.removeAllListeners();
9956           //  this.trigger.remove();
9957         }
9958         //if(this.wrap){
9959         //    this.wrap.remove();
9960         //}
9961         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9962     },
9963
9964     // private
9965     onFocus : function(){
9966         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9967         /*
9968         if(!this.mimicing){
9969             this.wrap.addClass('x-trigger-wrap-focus');
9970             this.mimicing = true;
9971             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9972             if(this.monitorTab){
9973                 this.el.on("keydown", this.checkTab, this);
9974             }
9975         }
9976         */
9977     },
9978
9979     // private
9980     checkTab : function(e){
9981         if(e.getKey() == e.TAB){
9982             this.triggerBlur();
9983         }
9984     },
9985
9986     // private
9987     onBlur : function(){
9988         // do nothing
9989     },
9990
9991     // private
9992     mimicBlur : function(e, t){
9993         /*
9994         if(!this.wrap.contains(t) && this.validateBlur()){
9995             this.triggerBlur();
9996         }
9997         */
9998     },
9999
10000     // private
10001     triggerBlur : function(){
10002         this.mimicing = false;
10003         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10004         if(this.monitorTab){
10005             this.el.un("keydown", this.checkTab, this);
10006         }
10007         //this.wrap.removeClass('x-trigger-wrap-focus');
10008         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10009     },
10010
10011     // private
10012     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10013     validateBlur : function(e, t){
10014         return true;
10015     },
10016
10017     // private
10018     onDisable : function(){
10019         this.inputEl().dom.disabled = true;
10020         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10021         //if(this.wrap){
10022         //    this.wrap.addClass('x-item-disabled');
10023         //}
10024     },
10025
10026     // private
10027     onEnable : function(){
10028         this.inputEl().dom.disabled = false;
10029         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10030         //if(this.wrap){
10031         //    this.el.removeClass('x-item-disabled');
10032         //}
10033     },
10034
10035     // private
10036     onShow : function(){
10037         var ae = this.getActionEl();
10038         
10039         if(ae){
10040             ae.dom.style.display = '';
10041             ae.dom.style.visibility = 'visible';
10042         }
10043     },
10044
10045     // private
10046     
10047     onHide : function(){
10048         var ae = this.getActionEl();
10049         ae.dom.style.display = 'none';
10050     },
10051
10052     /**
10053      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10054      * by an implementing function.
10055      * @method
10056      * @param {EventObject} e
10057      */
10058     onTriggerClick : Roo.emptyFn
10059 });
10060  /*
10061  * Based on:
10062  * Ext JS Library 1.1.1
10063  * Copyright(c) 2006-2007, Ext JS, LLC.
10064  *
10065  * Originally Released Under LGPL - original licence link has changed is not relivant.
10066  *
10067  * Fork - LGPL
10068  * <script type="text/javascript">
10069  */
10070
10071
10072 /**
10073  * @class Roo.data.SortTypes
10074  * @singleton
10075  * Defines the default sorting (casting?) comparison functions used when sorting data.
10076  */
10077 Roo.data.SortTypes = {
10078     /**
10079      * Default sort that does nothing
10080      * @param {Mixed} s The value being converted
10081      * @return {Mixed} The comparison value
10082      */
10083     none : function(s){
10084         return s;
10085     },
10086     
10087     /**
10088      * The regular expression used to strip tags
10089      * @type {RegExp}
10090      * @property
10091      */
10092     stripTagsRE : /<\/?[^>]+>/gi,
10093     
10094     /**
10095      * Strips all HTML tags to sort on text only
10096      * @param {Mixed} s The value being converted
10097      * @return {String} The comparison value
10098      */
10099     asText : function(s){
10100         return String(s).replace(this.stripTagsRE, "");
10101     },
10102     
10103     /**
10104      * Strips all HTML tags to sort on text only - Case insensitive
10105      * @param {Mixed} s The value being converted
10106      * @return {String} The comparison value
10107      */
10108     asUCText : function(s){
10109         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10110     },
10111     
10112     /**
10113      * Case insensitive string
10114      * @param {Mixed} s The value being converted
10115      * @return {String} The comparison value
10116      */
10117     asUCString : function(s) {
10118         return String(s).toUpperCase();
10119     },
10120     
10121     /**
10122      * Date sorting
10123      * @param {Mixed} s The value being converted
10124      * @return {Number} The comparison value
10125      */
10126     asDate : function(s) {
10127         if(!s){
10128             return 0;
10129         }
10130         if(s instanceof Date){
10131             return s.getTime();
10132         }
10133         return Date.parse(String(s));
10134     },
10135     
10136     /**
10137      * Float sorting
10138      * @param {Mixed} s The value being converted
10139      * @return {Float} The comparison value
10140      */
10141     asFloat : function(s) {
10142         var val = parseFloat(String(s).replace(/,/g, ""));
10143         if(isNaN(val)) {
10144             val = 0;
10145         }
10146         return val;
10147     },
10148     
10149     /**
10150      * Integer sorting
10151      * @param {Mixed} s The value being converted
10152      * @return {Number} The comparison value
10153      */
10154     asInt : function(s) {
10155         var val = parseInt(String(s).replace(/,/g, ""));
10156         if(isNaN(val)) {
10157             val = 0;
10158         }
10159         return val;
10160     }
10161 };/*
10162  * Based on:
10163  * Ext JS Library 1.1.1
10164  * Copyright(c) 2006-2007, Ext JS, LLC.
10165  *
10166  * Originally Released Under LGPL - original licence link has changed is not relivant.
10167  *
10168  * Fork - LGPL
10169  * <script type="text/javascript">
10170  */
10171
10172 /**
10173 * @class Roo.data.Record
10174  * Instances of this class encapsulate both record <em>definition</em> information, and record
10175  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10176  * to access Records cached in an {@link Roo.data.Store} object.<br>
10177  * <p>
10178  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10179  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10180  * objects.<br>
10181  * <p>
10182  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10183  * @constructor
10184  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10185  * {@link #create}. The parameters are the same.
10186  * @param {Array} data An associative Array of data values keyed by the field name.
10187  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10188  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10189  * not specified an integer id is generated.
10190  */
10191 Roo.data.Record = function(data, id){
10192     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10193     this.data = data;
10194 };
10195
10196 /**
10197  * Generate a constructor for a specific record layout.
10198  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10199  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10200  * Each field definition object may contain the following properties: <ul>
10201  * <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,
10202  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10203  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10204  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10205  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10206  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10207  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10208  * this may be omitted.</p></li>
10209  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10210  * <ul><li>auto (Default, implies no conversion)</li>
10211  * <li>string</li>
10212  * <li>int</li>
10213  * <li>float</li>
10214  * <li>boolean</li>
10215  * <li>date</li></ul></p></li>
10216  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10217  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10218  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10219  * by the Reader into an object that will be stored in the Record. It is passed the
10220  * following parameters:<ul>
10221  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10222  * </ul></p></li>
10223  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10224  * </ul>
10225  * <br>usage:<br><pre><code>
10226 var TopicRecord = Roo.data.Record.create(
10227     {name: 'title', mapping: 'topic_title'},
10228     {name: 'author', mapping: 'username'},
10229     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10230     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10231     {name: 'lastPoster', mapping: 'user2'},
10232     {name: 'excerpt', mapping: 'post_text'}
10233 );
10234
10235 var myNewRecord = new TopicRecord({
10236     title: 'Do my job please',
10237     author: 'noobie',
10238     totalPosts: 1,
10239     lastPost: new Date(),
10240     lastPoster: 'Animal',
10241     excerpt: 'No way dude!'
10242 });
10243 myStore.add(myNewRecord);
10244 </code></pre>
10245  * @method create
10246  * @static
10247  */
10248 Roo.data.Record.create = function(o){
10249     var f = function(){
10250         f.superclass.constructor.apply(this, arguments);
10251     };
10252     Roo.extend(f, Roo.data.Record);
10253     var p = f.prototype;
10254     p.fields = new Roo.util.MixedCollection(false, function(field){
10255         return field.name;
10256     });
10257     for(var i = 0, len = o.length; i < len; i++){
10258         p.fields.add(new Roo.data.Field(o[i]));
10259     }
10260     f.getField = function(name){
10261         return p.fields.get(name);  
10262     };
10263     return f;
10264 };
10265
10266 Roo.data.Record.AUTO_ID = 1000;
10267 Roo.data.Record.EDIT = 'edit';
10268 Roo.data.Record.REJECT = 'reject';
10269 Roo.data.Record.COMMIT = 'commit';
10270
10271 Roo.data.Record.prototype = {
10272     /**
10273      * Readonly flag - true if this record has been modified.
10274      * @type Boolean
10275      */
10276     dirty : false,
10277     editing : false,
10278     error: null,
10279     modified: null,
10280
10281     // private
10282     join : function(store){
10283         this.store = store;
10284     },
10285
10286     /**
10287      * Set the named field to the specified value.
10288      * @param {String} name The name of the field to set.
10289      * @param {Object} value The value to set the field to.
10290      */
10291     set : function(name, value){
10292         if(this.data[name] == value){
10293             return;
10294         }
10295         this.dirty = true;
10296         if(!this.modified){
10297             this.modified = {};
10298         }
10299         if(typeof this.modified[name] == 'undefined'){
10300             this.modified[name] = this.data[name];
10301         }
10302         this.data[name] = value;
10303         if(!this.editing && this.store){
10304             this.store.afterEdit(this);
10305         }       
10306     },
10307
10308     /**
10309      * Get the value of the named field.
10310      * @param {String} name The name of the field to get the value of.
10311      * @return {Object} The value of the field.
10312      */
10313     get : function(name){
10314         return this.data[name]; 
10315     },
10316
10317     // private
10318     beginEdit : function(){
10319         this.editing = true;
10320         this.modified = {}; 
10321     },
10322
10323     // private
10324     cancelEdit : function(){
10325         this.editing = false;
10326         delete this.modified;
10327     },
10328
10329     // private
10330     endEdit : function(){
10331         this.editing = false;
10332         if(this.dirty && this.store){
10333             this.store.afterEdit(this);
10334         }
10335     },
10336
10337     /**
10338      * Usually called by the {@link Roo.data.Store} which owns the Record.
10339      * Rejects all changes made to the Record since either creation, or the last commit operation.
10340      * Modified fields are reverted to their original values.
10341      * <p>
10342      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10343      * of reject operations.
10344      */
10345     reject : function(){
10346         var m = this.modified;
10347         for(var n in m){
10348             if(typeof m[n] != "function"){
10349                 this.data[n] = m[n];
10350             }
10351         }
10352         this.dirty = false;
10353         delete this.modified;
10354         this.editing = false;
10355         if(this.store){
10356             this.store.afterReject(this);
10357         }
10358     },
10359
10360     /**
10361      * Usually called by the {@link Roo.data.Store} which owns the Record.
10362      * Commits all changes made to the Record since either creation, or the last commit operation.
10363      * <p>
10364      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10365      * of commit operations.
10366      */
10367     commit : function(){
10368         this.dirty = false;
10369         delete this.modified;
10370         this.editing = false;
10371         if(this.store){
10372             this.store.afterCommit(this);
10373         }
10374     },
10375
10376     // private
10377     hasError : function(){
10378         return this.error != null;
10379     },
10380
10381     // private
10382     clearError : function(){
10383         this.error = null;
10384     },
10385
10386     /**
10387      * Creates a copy of this record.
10388      * @param {String} id (optional) A new record id if you don't want to use this record's id
10389      * @return {Record}
10390      */
10391     copy : function(newId) {
10392         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10393     }
10394 };/*
10395  * Based on:
10396  * Ext JS Library 1.1.1
10397  * Copyright(c) 2006-2007, Ext JS, LLC.
10398  *
10399  * Originally Released Under LGPL - original licence link has changed is not relivant.
10400  *
10401  * Fork - LGPL
10402  * <script type="text/javascript">
10403  */
10404
10405
10406
10407 /**
10408  * @class Roo.data.Store
10409  * @extends Roo.util.Observable
10410  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10411  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10412  * <p>
10413  * 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
10414  * has no knowledge of the format of the data returned by the Proxy.<br>
10415  * <p>
10416  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10417  * instances from the data object. These records are cached and made available through accessor functions.
10418  * @constructor
10419  * Creates a new Store.
10420  * @param {Object} config A config object containing the objects needed for the Store to access data,
10421  * and read the data into Records.
10422  */
10423 Roo.data.Store = function(config){
10424     this.data = new Roo.util.MixedCollection(false);
10425     this.data.getKey = function(o){
10426         return o.id;
10427     };
10428     this.baseParams = {};
10429     // private
10430     this.paramNames = {
10431         "start" : "start",
10432         "limit" : "limit",
10433         "sort" : "sort",
10434         "dir" : "dir",
10435         "multisort" : "_multisort"
10436     };
10437
10438     if(config && config.data){
10439         this.inlineData = config.data;
10440         delete config.data;
10441     }
10442
10443     Roo.apply(this, config);
10444     
10445     if(this.reader){ // reader passed
10446         this.reader = Roo.factory(this.reader, Roo.data);
10447         this.reader.xmodule = this.xmodule || false;
10448         if(!this.recordType){
10449             this.recordType = this.reader.recordType;
10450         }
10451         if(this.reader.onMetaChange){
10452             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10453         }
10454     }
10455
10456     if(this.recordType){
10457         this.fields = this.recordType.prototype.fields;
10458     }
10459     this.modified = [];
10460
10461     this.addEvents({
10462         /**
10463          * @event datachanged
10464          * Fires when the data cache has changed, and a widget which is using this Store
10465          * as a Record cache should refresh its view.
10466          * @param {Store} this
10467          */
10468         datachanged : true,
10469         /**
10470          * @event metachange
10471          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10472          * @param {Store} this
10473          * @param {Object} meta The JSON metadata
10474          */
10475         metachange : true,
10476         /**
10477          * @event add
10478          * Fires when Records have been added to the Store
10479          * @param {Store} this
10480          * @param {Roo.data.Record[]} records The array of Records added
10481          * @param {Number} index The index at which the record(s) were added
10482          */
10483         add : true,
10484         /**
10485          * @event remove
10486          * Fires when a Record has been removed from the Store
10487          * @param {Store} this
10488          * @param {Roo.data.Record} record The Record that was removed
10489          * @param {Number} index The index at which the record was removed
10490          */
10491         remove : true,
10492         /**
10493          * @event update
10494          * Fires when a Record has been updated
10495          * @param {Store} this
10496          * @param {Roo.data.Record} record The Record that was updated
10497          * @param {String} operation The update operation being performed.  Value may be one of:
10498          * <pre><code>
10499  Roo.data.Record.EDIT
10500  Roo.data.Record.REJECT
10501  Roo.data.Record.COMMIT
10502          * </code></pre>
10503          */
10504         update : true,
10505         /**
10506          * @event clear
10507          * Fires when the data cache has been cleared.
10508          * @param {Store} this
10509          */
10510         clear : true,
10511         /**
10512          * @event beforeload
10513          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10514          * the load action will be canceled.
10515          * @param {Store} this
10516          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10517          */
10518         beforeload : true,
10519         /**
10520          * @event beforeloadadd
10521          * Fires after a new set of Records has been loaded.
10522          * @param {Store} this
10523          * @param {Roo.data.Record[]} records The Records that were loaded
10524          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10525          */
10526         beforeloadadd : true,
10527         /**
10528          * @event load
10529          * Fires after a new set of Records has been loaded, before they are added to the store.
10530          * @param {Store} this
10531          * @param {Roo.data.Record[]} records The Records that were loaded
10532          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10533          * @params {Object} return from reader
10534          */
10535         load : true,
10536         /**
10537          * @event loadexception
10538          * Fires if an exception occurs in the Proxy during loading.
10539          * Called with the signature of the Proxy's "loadexception" event.
10540          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10541          * 
10542          * @param {Proxy} 
10543          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10544          * @param {Object} load options 
10545          * @param {Object} jsonData from your request (normally this contains the Exception)
10546          */
10547         loadexception : true
10548     });
10549     
10550     if(this.proxy){
10551         this.proxy = Roo.factory(this.proxy, Roo.data);
10552         this.proxy.xmodule = this.xmodule || false;
10553         this.relayEvents(this.proxy,  ["loadexception"]);
10554     }
10555     this.sortToggle = {};
10556     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10557
10558     Roo.data.Store.superclass.constructor.call(this);
10559
10560     if(this.inlineData){
10561         this.loadData(this.inlineData);
10562         delete this.inlineData;
10563     }
10564 };
10565
10566 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10567      /**
10568     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10569     * without a remote query - used by combo/forms at present.
10570     */
10571     
10572     /**
10573     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10574     */
10575     /**
10576     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10577     */
10578     /**
10579     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10580     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10581     */
10582     /**
10583     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10584     * on any HTTP request
10585     */
10586     /**
10587     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10588     */
10589     /**
10590     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10591     */
10592     multiSort: false,
10593     /**
10594     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10595     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10596     */
10597     remoteSort : false,
10598
10599     /**
10600     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10601      * loaded or when a record is removed. (defaults to false).
10602     */
10603     pruneModifiedRecords : false,
10604
10605     // private
10606     lastOptions : null,
10607
10608     /**
10609      * Add Records to the Store and fires the add event.
10610      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10611      */
10612     add : function(records){
10613         records = [].concat(records);
10614         for(var i = 0, len = records.length; i < len; i++){
10615             records[i].join(this);
10616         }
10617         var index = this.data.length;
10618         this.data.addAll(records);
10619         this.fireEvent("add", this, records, index);
10620     },
10621
10622     /**
10623      * Remove a Record from the Store and fires the remove event.
10624      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10625      */
10626     remove : function(record){
10627         var index = this.data.indexOf(record);
10628         this.data.removeAt(index);
10629         if(this.pruneModifiedRecords){
10630             this.modified.remove(record);
10631         }
10632         this.fireEvent("remove", this, record, index);
10633     },
10634
10635     /**
10636      * Remove all Records from the Store and fires the clear event.
10637      */
10638     removeAll : function(){
10639         this.data.clear();
10640         if(this.pruneModifiedRecords){
10641             this.modified = [];
10642         }
10643         this.fireEvent("clear", this);
10644     },
10645
10646     /**
10647      * Inserts Records to the Store at the given index and fires the add event.
10648      * @param {Number} index The start index at which to insert the passed Records.
10649      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10650      */
10651     insert : function(index, records){
10652         records = [].concat(records);
10653         for(var i = 0, len = records.length; i < len; i++){
10654             this.data.insert(index, records[i]);
10655             records[i].join(this);
10656         }
10657         this.fireEvent("add", this, records, index);
10658     },
10659
10660     /**
10661      * Get the index within the cache of the passed Record.
10662      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10663      * @return {Number} The index of the passed Record. Returns -1 if not found.
10664      */
10665     indexOf : function(record){
10666         return this.data.indexOf(record);
10667     },
10668
10669     /**
10670      * Get the index within the cache of the Record with the passed id.
10671      * @param {String} id The id of the Record to find.
10672      * @return {Number} The index of the Record. Returns -1 if not found.
10673      */
10674     indexOfId : function(id){
10675         return this.data.indexOfKey(id);
10676     },
10677
10678     /**
10679      * Get the Record with the specified id.
10680      * @param {String} id The id of the Record to find.
10681      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10682      */
10683     getById : function(id){
10684         return this.data.key(id);
10685     },
10686
10687     /**
10688      * Get the Record at the specified index.
10689      * @param {Number} index The index of the Record to find.
10690      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10691      */
10692     getAt : function(index){
10693         return this.data.itemAt(index);
10694     },
10695
10696     /**
10697      * Returns a range of Records between specified indices.
10698      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10699      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10700      * @return {Roo.data.Record[]} An array of Records
10701      */
10702     getRange : function(start, end){
10703         return this.data.getRange(start, end);
10704     },
10705
10706     // private
10707     storeOptions : function(o){
10708         o = Roo.apply({}, o);
10709         delete o.callback;
10710         delete o.scope;
10711         this.lastOptions = o;
10712     },
10713
10714     /**
10715      * Loads the Record cache from the configured Proxy using the configured Reader.
10716      * <p>
10717      * If using remote paging, then the first load call must specify the <em>start</em>
10718      * and <em>limit</em> properties in the options.params property to establish the initial
10719      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10720      * <p>
10721      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10722      * and this call will return before the new data has been loaded. Perform any post-processing
10723      * in a callback function, or in a "load" event handler.</strong>
10724      * <p>
10725      * @param {Object} options An object containing properties which control loading options:<ul>
10726      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10727      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10728      * passed the following arguments:<ul>
10729      * <li>r : Roo.data.Record[]</li>
10730      * <li>options: Options object from the load call</li>
10731      * <li>success: Boolean success indicator</li></ul></li>
10732      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10733      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10734      * </ul>
10735      */
10736     load : function(options){
10737         options = options || {};
10738         if(this.fireEvent("beforeload", this, options) !== false){
10739             this.storeOptions(options);
10740             var p = Roo.apply(options.params || {}, this.baseParams);
10741             // if meta was not loaded from remote source.. try requesting it.
10742             if (!this.reader.metaFromRemote) {
10743                 p._requestMeta = 1;
10744             }
10745             if(this.sortInfo && this.remoteSort){
10746                 var pn = this.paramNames;
10747                 p[pn["sort"]] = this.sortInfo.field;
10748                 p[pn["dir"]] = this.sortInfo.direction;
10749             }
10750             if (this.multiSort) {
10751                 var pn = this.paramNames;
10752                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10753             }
10754             
10755             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10756         }
10757     },
10758
10759     /**
10760      * Reloads the Record cache from the configured Proxy using the configured Reader and
10761      * the options from the last load operation performed.
10762      * @param {Object} options (optional) An object containing properties which may override the options
10763      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10764      * the most recently used options are reused).
10765      */
10766     reload : function(options){
10767         this.load(Roo.applyIf(options||{}, this.lastOptions));
10768     },
10769
10770     // private
10771     // Called as a callback by the Reader during a load operation.
10772     loadRecords : function(o, options, success){
10773         if(!o || success === false){
10774             if(success !== false){
10775                 this.fireEvent("load", this, [], options, o);
10776             }
10777             if(options.callback){
10778                 options.callback.call(options.scope || this, [], options, false);
10779             }
10780             return;
10781         }
10782         // if data returned failure - throw an exception.
10783         if (o.success === false) {
10784             // show a message if no listener is registered.
10785             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10786                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10787             }
10788             // loadmask wil be hooked into this..
10789             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10790             return;
10791         }
10792         var r = o.records, t = o.totalRecords || r.length;
10793         
10794         this.fireEvent("beforeloadadd", this, r, options, o);
10795         
10796         if(!options || options.add !== true){
10797             if(this.pruneModifiedRecords){
10798                 this.modified = [];
10799             }
10800             for(var i = 0, len = r.length; i < len; i++){
10801                 r[i].join(this);
10802             }
10803             if(this.snapshot){
10804                 this.data = this.snapshot;
10805                 delete this.snapshot;
10806             }
10807             this.data.clear();
10808             this.data.addAll(r);
10809             this.totalLength = t;
10810             this.applySort();
10811             this.fireEvent("datachanged", this);
10812         }else{
10813             this.totalLength = Math.max(t, this.data.length+r.length);
10814             this.add(r);
10815         }
10816         this.fireEvent("load", this, r, options, o);
10817         if(options.callback){
10818             options.callback.call(options.scope || this, r, options, true);
10819         }
10820     },
10821
10822
10823     /**
10824      * Loads data from a passed data block. A Reader which understands the format of the data
10825      * must have been configured in the constructor.
10826      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10827      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10828      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10829      */
10830     loadData : function(o, append){
10831         var r = this.reader.readRecords(o);
10832         this.loadRecords(r, {add: append}, true);
10833     },
10834
10835     /**
10836      * Gets the number of cached records.
10837      * <p>
10838      * <em>If using paging, this may not be the total size of the dataset. If the data object
10839      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10840      * the data set size</em>
10841      */
10842     getCount : function(){
10843         return this.data.length || 0;
10844     },
10845
10846     /**
10847      * Gets the total number of records in the dataset as returned by the server.
10848      * <p>
10849      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10850      * the dataset size</em>
10851      */
10852     getTotalCount : function(){
10853         return this.totalLength || 0;
10854     },
10855
10856     /**
10857      * Returns the sort state of the Store as an object with two properties:
10858      * <pre><code>
10859  field {String} The name of the field by which the Records are sorted
10860  direction {String} The sort order, "ASC" or "DESC"
10861      * </code></pre>
10862      */
10863     getSortState : function(){
10864         return this.sortInfo;
10865     },
10866
10867     // private
10868     applySort : function(){
10869         if(this.sortInfo && !this.remoteSort){
10870             var s = this.sortInfo, f = s.field;
10871             var st = this.fields.get(f).sortType;
10872             var fn = function(r1, r2){
10873                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10874                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10875             };
10876             this.data.sort(s.direction, fn);
10877             if(this.snapshot && this.snapshot != this.data){
10878                 this.snapshot.sort(s.direction, fn);
10879             }
10880         }
10881     },
10882
10883     /**
10884      * Sets the default sort column and order to be used by the next load operation.
10885      * @param {String} fieldName The name of the field to sort by.
10886      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10887      */
10888     setDefaultSort : function(field, dir){
10889         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10890     },
10891
10892     /**
10893      * Sort the Records.
10894      * If remote sorting is used, the sort is performed on the server, and the cache is
10895      * reloaded. If local sorting is used, the cache is sorted internally.
10896      * @param {String} fieldName The name of the field to sort by.
10897      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10898      */
10899     sort : function(fieldName, dir){
10900         var f = this.fields.get(fieldName);
10901         if(!dir){
10902             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10903             
10904             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10905                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10906             }else{
10907                 dir = f.sortDir;
10908             }
10909         }
10910         this.sortToggle[f.name] = dir;
10911         this.sortInfo = {field: f.name, direction: dir};
10912         if(!this.remoteSort){
10913             this.applySort();
10914             this.fireEvent("datachanged", this);
10915         }else{
10916             this.load(this.lastOptions);
10917         }
10918     },
10919
10920     /**
10921      * Calls the specified function for each of the Records in the cache.
10922      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10923      * Returning <em>false</em> aborts and exits the iteration.
10924      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10925      */
10926     each : function(fn, scope){
10927         this.data.each(fn, scope);
10928     },
10929
10930     /**
10931      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10932      * (e.g., during paging).
10933      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10934      */
10935     getModifiedRecords : function(){
10936         return this.modified;
10937     },
10938
10939     // private
10940     createFilterFn : function(property, value, anyMatch){
10941         if(!value.exec){ // not a regex
10942             value = String(value);
10943             if(value.length == 0){
10944                 return false;
10945             }
10946             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10947         }
10948         return function(r){
10949             return value.test(r.data[property]);
10950         };
10951     },
10952
10953     /**
10954      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10955      * @param {String} property A field on your records
10956      * @param {Number} start The record index to start at (defaults to 0)
10957      * @param {Number} end The last record index to include (defaults to length - 1)
10958      * @return {Number} The sum
10959      */
10960     sum : function(property, start, end){
10961         var rs = this.data.items, v = 0;
10962         start = start || 0;
10963         end = (end || end === 0) ? end : rs.length-1;
10964
10965         for(var i = start; i <= end; i++){
10966             v += (rs[i].data[property] || 0);
10967         }
10968         return v;
10969     },
10970
10971     /**
10972      * Filter the records by a specified property.
10973      * @param {String} field A field on your records
10974      * @param {String/RegExp} value Either a string that the field
10975      * should start with or a RegExp to test against the field
10976      * @param {Boolean} anyMatch True to match any part not just the beginning
10977      */
10978     filter : function(property, value, anyMatch){
10979         var fn = this.createFilterFn(property, value, anyMatch);
10980         return fn ? this.filterBy(fn) : this.clearFilter();
10981     },
10982
10983     /**
10984      * Filter by a function. The specified function will be called with each
10985      * record in this data source. If the function returns true the record is included,
10986      * otherwise it is filtered.
10987      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10988      * @param {Object} scope (optional) The scope of the function (defaults to this)
10989      */
10990     filterBy : function(fn, scope){
10991         this.snapshot = this.snapshot || this.data;
10992         this.data = this.queryBy(fn, scope||this);
10993         this.fireEvent("datachanged", this);
10994     },
10995
10996     /**
10997      * Query the records by a specified property.
10998      * @param {String} field A field on your records
10999      * @param {String/RegExp} value Either a string that the field
11000      * should start with or a RegExp to test against the field
11001      * @param {Boolean} anyMatch True to match any part not just the beginning
11002      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11003      */
11004     query : function(property, value, anyMatch){
11005         var fn = this.createFilterFn(property, value, anyMatch);
11006         return fn ? this.queryBy(fn) : this.data.clone();
11007     },
11008
11009     /**
11010      * Query by a function. The specified function will be called with each
11011      * record in this data source. If the function returns true the record is included
11012      * in the results.
11013      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11014      * @param {Object} scope (optional) The scope of the function (defaults to this)
11015       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11016      **/
11017     queryBy : function(fn, scope){
11018         var data = this.snapshot || this.data;
11019         return data.filterBy(fn, scope||this);
11020     },
11021
11022     /**
11023      * Collects unique values for a particular dataIndex from this store.
11024      * @param {String} dataIndex The property to collect
11025      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11026      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11027      * @return {Array} An array of the unique values
11028      **/
11029     collect : function(dataIndex, allowNull, bypassFilter){
11030         var d = (bypassFilter === true && this.snapshot) ?
11031                 this.snapshot.items : this.data.items;
11032         var v, sv, r = [], l = {};
11033         for(var i = 0, len = d.length; i < len; i++){
11034             v = d[i].data[dataIndex];
11035             sv = String(v);
11036             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11037                 l[sv] = true;
11038                 r[r.length] = v;
11039             }
11040         }
11041         return r;
11042     },
11043
11044     /**
11045      * Revert to a view of the Record cache with no filtering applied.
11046      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11047      */
11048     clearFilter : function(suppressEvent){
11049         if(this.snapshot && this.snapshot != this.data){
11050             this.data = this.snapshot;
11051             delete this.snapshot;
11052             if(suppressEvent !== true){
11053                 this.fireEvent("datachanged", this);
11054             }
11055         }
11056     },
11057
11058     // private
11059     afterEdit : function(record){
11060         if(this.modified.indexOf(record) == -1){
11061             this.modified.push(record);
11062         }
11063         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11064     },
11065     
11066     // private
11067     afterReject : function(record){
11068         this.modified.remove(record);
11069         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11070     },
11071
11072     // private
11073     afterCommit : function(record){
11074         this.modified.remove(record);
11075         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11076     },
11077
11078     /**
11079      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11080      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11081      */
11082     commitChanges : function(){
11083         var m = this.modified.slice(0);
11084         this.modified = [];
11085         for(var i = 0, len = m.length; i < len; i++){
11086             m[i].commit();
11087         }
11088     },
11089
11090     /**
11091      * Cancel outstanding changes on all changed records.
11092      */
11093     rejectChanges : function(){
11094         var m = this.modified.slice(0);
11095         this.modified = [];
11096         for(var i = 0, len = m.length; i < len; i++){
11097             m[i].reject();
11098         }
11099     },
11100
11101     onMetaChange : function(meta, rtype, o){
11102         this.recordType = rtype;
11103         this.fields = rtype.prototype.fields;
11104         delete this.snapshot;
11105         this.sortInfo = meta.sortInfo || this.sortInfo;
11106         this.modified = [];
11107         this.fireEvent('metachange', this, this.reader.meta);
11108     },
11109     
11110     moveIndex : function(data, type)
11111     {
11112         var index = this.indexOf(data);
11113         
11114         var newIndex = index + type;
11115         
11116         this.remove(data);
11117         
11118         this.insert(newIndex, data);
11119         
11120     }
11121 });/*
11122  * Based on:
11123  * Ext JS Library 1.1.1
11124  * Copyright(c) 2006-2007, Ext JS, LLC.
11125  *
11126  * Originally Released Under LGPL - original licence link has changed is not relivant.
11127  *
11128  * Fork - LGPL
11129  * <script type="text/javascript">
11130  */
11131
11132 /**
11133  * @class Roo.data.SimpleStore
11134  * @extends Roo.data.Store
11135  * Small helper class to make creating Stores from Array data easier.
11136  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11137  * @cfg {Array} fields An array of field definition objects, or field name strings.
11138  * @cfg {Array} data The multi-dimensional array of data
11139  * @constructor
11140  * @param {Object} config
11141  */
11142 Roo.data.SimpleStore = function(config){
11143     Roo.data.SimpleStore.superclass.constructor.call(this, {
11144         isLocal : true,
11145         reader: new Roo.data.ArrayReader({
11146                 id: config.id
11147             },
11148             Roo.data.Record.create(config.fields)
11149         ),
11150         proxy : new Roo.data.MemoryProxy(config.data)
11151     });
11152     this.load();
11153 };
11154 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11155  * Based on:
11156  * Ext JS Library 1.1.1
11157  * Copyright(c) 2006-2007, Ext JS, LLC.
11158  *
11159  * Originally Released Under LGPL - original licence link has changed is not relivant.
11160  *
11161  * Fork - LGPL
11162  * <script type="text/javascript">
11163  */
11164
11165 /**
11166 /**
11167  * @extends Roo.data.Store
11168  * @class Roo.data.JsonStore
11169  * Small helper class to make creating Stores for JSON data easier. <br/>
11170 <pre><code>
11171 var store = new Roo.data.JsonStore({
11172     url: 'get-images.php',
11173     root: 'images',
11174     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11175 });
11176 </code></pre>
11177  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11178  * JsonReader and HttpProxy (unless inline data is provided).</b>
11179  * @cfg {Array} fields An array of field definition objects, or field name strings.
11180  * @constructor
11181  * @param {Object} config
11182  */
11183 Roo.data.JsonStore = function(c){
11184     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11185         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11186         reader: new Roo.data.JsonReader(c, c.fields)
11187     }));
11188 };
11189 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11190  * Based on:
11191  * Ext JS Library 1.1.1
11192  * Copyright(c) 2006-2007, Ext JS, LLC.
11193  *
11194  * Originally Released Under LGPL - original licence link has changed is not relivant.
11195  *
11196  * Fork - LGPL
11197  * <script type="text/javascript">
11198  */
11199
11200  
11201 Roo.data.Field = function(config){
11202     if(typeof config == "string"){
11203         config = {name: config};
11204     }
11205     Roo.apply(this, config);
11206     
11207     if(!this.type){
11208         this.type = "auto";
11209     }
11210     
11211     var st = Roo.data.SortTypes;
11212     // named sortTypes are supported, here we look them up
11213     if(typeof this.sortType == "string"){
11214         this.sortType = st[this.sortType];
11215     }
11216     
11217     // set default sortType for strings and dates
11218     if(!this.sortType){
11219         switch(this.type){
11220             case "string":
11221                 this.sortType = st.asUCString;
11222                 break;
11223             case "date":
11224                 this.sortType = st.asDate;
11225                 break;
11226             default:
11227                 this.sortType = st.none;
11228         }
11229     }
11230
11231     // define once
11232     var stripRe = /[\$,%]/g;
11233
11234     // prebuilt conversion function for this field, instead of
11235     // switching every time we're reading a value
11236     if(!this.convert){
11237         var cv, dateFormat = this.dateFormat;
11238         switch(this.type){
11239             case "":
11240             case "auto":
11241             case undefined:
11242                 cv = function(v){ return v; };
11243                 break;
11244             case "string":
11245                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11246                 break;
11247             case "int":
11248                 cv = function(v){
11249                     return v !== undefined && v !== null && v !== '' ?
11250                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11251                     };
11252                 break;
11253             case "float":
11254                 cv = function(v){
11255                     return v !== undefined && v !== null && v !== '' ?
11256                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11257                     };
11258                 break;
11259             case "bool":
11260             case "boolean":
11261                 cv = function(v){ return v === true || v === "true" || v == 1; };
11262                 break;
11263             case "date":
11264                 cv = function(v){
11265                     if(!v){
11266                         return '';
11267                     }
11268                     if(v instanceof Date){
11269                         return v;
11270                     }
11271                     if(dateFormat){
11272                         if(dateFormat == "timestamp"){
11273                             return new Date(v*1000);
11274                         }
11275                         return Date.parseDate(v, dateFormat);
11276                     }
11277                     var parsed = Date.parse(v);
11278                     return parsed ? new Date(parsed) : null;
11279                 };
11280              break;
11281             
11282         }
11283         this.convert = cv;
11284     }
11285 };
11286
11287 Roo.data.Field.prototype = {
11288     dateFormat: null,
11289     defaultValue: "",
11290     mapping: null,
11291     sortType : null,
11292     sortDir : "ASC"
11293 };/*
11294  * Based on:
11295  * Ext JS Library 1.1.1
11296  * Copyright(c) 2006-2007, Ext JS, LLC.
11297  *
11298  * Originally Released Under LGPL - original licence link has changed is not relivant.
11299  *
11300  * Fork - LGPL
11301  * <script type="text/javascript">
11302  */
11303  
11304 // Base class for reading structured data from a data source.  This class is intended to be
11305 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11306
11307 /**
11308  * @class Roo.data.DataReader
11309  * Base class for reading structured data from a data source.  This class is intended to be
11310  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11311  */
11312
11313 Roo.data.DataReader = function(meta, recordType){
11314     
11315     this.meta = meta;
11316     
11317     this.recordType = recordType instanceof Array ? 
11318         Roo.data.Record.create(recordType) : recordType;
11319 };
11320
11321 Roo.data.DataReader.prototype = {
11322      /**
11323      * Create an empty record
11324      * @param {Object} data (optional) - overlay some values
11325      * @return {Roo.data.Record} record created.
11326      */
11327     newRow :  function(d) {
11328         var da =  {};
11329         this.recordType.prototype.fields.each(function(c) {
11330             switch( c.type) {
11331                 case 'int' : da[c.name] = 0; break;
11332                 case 'date' : da[c.name] = new Date(); break;
11333                 case 'float' : da[c.name] = 0.0; break;
11334                 case 'boolean' : da[c.name] = false; break;
11335                 default : da[c.name] = ""; break;
11336             }
11337             
11338         });
11339         return new this.recordType(Roo.apply(da, d));
11340     }
11341     
11342 };/*
11343  * Based on:
11344  * Ext JS Library 1.1.1
11345  * Copyright(c) 2006-2007, Ext JS, LLC.
11346  *
11347  * Originally Released Under LGPL - original licence link has changed is not relivant.
11348  *
11349  * Fork - LGPL
11350  * <script type="text/javascript">
11351  */
11352
11353 /**
11354  * @class Roo.data.DataProxy
11355  * @extends Roo.data.Observable
11356  * This class is an abstract base class for implementations which provide retrieval of
11357  * unformatted data objects.<br>
11358  * <p>
11359  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11360  * (of the appropriate type which knows how to parse the data object) to provide a block of
11361  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11362  * <p>
11363  * Custom implementations must implement the load method as described in
11364  * {@link Roo.data.HttpProxy#load}.
11365  */
11366 Roo.data.DataProxy = function(){
11367     this.addEvents({
11368         /**
11369          * @event beforeload
11370          * Fires before a network request is made to retrieve a data object.
11371          * @param {Object} This DataProxy object.
11372          * @param {Object} params The params parameter to the load function.
11373          */
11374         beforeload : true,
11375         /**
11376          * @event load
11377          * Fires before the load method's callback is called.
11378          * @param {Object} This DataProxy object.
11379          * @param {Object} o The data object.
11380          * @param {Object} arg The callback argument object passed to the load function.
11381          */
11382         load : true,
11383         /**
11384          * @event loadexception
11385          * Fires if an Exception occurs during data retrieval.
11386          * @param {Object} This DataProxy object.
11387          * @param {Object} o The data object.
11388          * @param {Object} arg The callback argument object passed to the load function.
11389          * @param {Object} e The Exception.
11390          */
11391         loadexception : true
11392     });
11393     Roo.data.DataProxy.superclass.constructor.call(this);
11394 };
11395
11396 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11397
11398     /**
11399      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11400      */
11401 /*
11402  * Based on:
11403  * Ext JS Library 1.1.1
11404  * Copyright(c) 2006-2007, Ext JS, LLC.
11405  *
11406  * Originally Released Under LGPL - original licence link has changed is not relivant.
11407  *
11408  * Fork - LGPL
11409  * <script type="text/javascript">
11410  */
11411 /**
11412  * @class Roo.data.MemoryProxy
11413  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11414  * to the Reader when its load method is called.
11415  * @constructor
11416  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11417  */
11418 Roo.data.MemoryProxy = function(data){
11419     if (data.data) {
11420         data = data.data;
11421     }
11422     Roo.data.MemoryProxy.superclass.constructor.call(this);
11423     this.data = data;
11424 };
11425
11426 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11427     
11428     /**
11429      * Load data from the requested source (in this case an in-memory
11430      * data object passed to the constructor), read the data object into
11431      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11432      * process that block using the passed callback.
11433      * @param {Object} params This parameter is not used by the MemoryProxy class.
11434      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11435      * object into a block of Roo.data.Records.
11436      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11437      * The function must be passed <ul>
11438      * <li>The Record block object</li>
11439      * <li>The "arg" argument from the load function</li>
11440      * <li>A boolean success indicator</li>
11441      * </ul>
11442      * @param {Object} scope The scope in which to call the callback
11443      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11444      */
11445     load : function(params, reader, callback, scope, arg){
11446         params = params || {};
11447         var result;
11448         try {
11449             result = reader.readRecords(this.data);
11450         }catch(e){
11451             this.fireEvent("loadexception", this, arg, null, e);
11452             callback.call(scope, null, arg, false);
11453             return;
11454         }
11455         callback.call(scope, result, arg, true);
11456     },
11457     
11458     // private
11459     update : function(params, records){
11460         
11461     }
11462 });/*
11463  * Based on:
11464  * Ext JS Library 1.1.1
11465  * Copyright(c) 2006-2007, Ext JS, LLC.
11466  *
11467  * Originally Released Under LGPL - original licence link has changed is not relivant.
11468  *
11469  * Fork - LGPL
11470  * <script type="text/javascript">
11471  */
11472 /**
11473  * @class Roo.data.HttpProxy
11474  * @extends Roo.data.DataProxy
11475  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11476  * configured to reference a certain URL.<br><br>
11477  * <p>
11478  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11479  * from which the running page was served.<br><br>
11480  * <p>
11481  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11482  * <p>
11483  * Be aware that to enable the browser to parse an XML document, the server must set
11484  * the Content-Type header in the HTTP response to "text/xml".
11485  * @constructor
11486  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11487  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11488  * will be used to make the request.
11489  */
11490 Roo.data.HttpProxy = function(conn){
11491     Roo.data.HttpProxy.superclass.constructor.call(this);
11492     // is conn a conn config or a real conn?
11493     this.conn = conn;
11494     this.useAjax = !conn || !conn.events;
11495   
11496 };
11497
11498 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11499     // thse are take from connection...
11500     
11501     /**
11502      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11503      */
11504     /**
11505      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11506      * extra parameters to each request made by this object. (defaults to undefined)
11507      */
11508     /**
11509      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11510      *  to each request made by this object. (defaults to undefined)
11511      */
11512     /**
11513      * @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)
11514      */
11515     /**
11516      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11517      */
11518      /**
11519      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11520      * @type Boolean
11521      */
11522   
11523
11524     /**
11525      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11526      * @type Boolean
11527      */
11528     /**
11529      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11530      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11531      * a finer-grained basis than the DataProxy events.
11532      */
11533     getConnection : function(){
11534         return this.useAjax ? Roo.Ajax : this.conn;
11535     },
11536
11537     /**
11538      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11539      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11540      * process that block using the passed callback.
11541      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11542      * for the request to the remote server.
11543      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11544      * object into a block of Roo.data.Records.
11545      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11546      * The function must be passed <ul>
11547      * <li>The Record block object</li>
11548      * <li>The "arg" argument from the load function</li>
11549      * <li>A boolean success indicator</li>
11550      * </ul>
11551      * @param {Object} scope The scope in which to call the callback
11552      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11553      */
11554     load : function(params, reader, callback, scope, arg){
11555         if(this.fireEvent("beforeload", this, params) !== false){
11556             var  o = {
11557                 params : params || {},
11558                 request: {
11559                     callback : callback,
11560                     scope : scope,
11561                     arg : arg
11562                 },
11563                 reader: reader,
11564                 callback : this.loadResponse,
11565                 scope: this
11566             };
11567             if(this.useAjax){
11568                 Roo.applyIf(o, this.conn);
11569                 if(this.activeRequest){
11570                     Roo.Ajax.abort(this.activeRequest);
11571                 }
11572                 this.activeRequest = Roo.Ajax.request(o);
11573             }else{
11574                 this.conn.request(o);
11575             }
11576         }else{
11577             callback.call(scope||this, null, arg, false);
11578         }
11579     },
11580
11581     // private
11582     loadResponse : function(o, success, response){
11583         delete this.activeRequest;
11584         if(!success){
11585             this.fireEvent("loadexception", this, o, response);
11586             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11587             return;
11588         }
11589         var result;
11590         try {
11591             result = o.reader.read(response);
11592         }catch(e){
11593             this.fireEvent("loadexception", this, o, response, e);
11594             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11595             return;
11596         }
11597         
11598         this.fireEvent("load", this, o, o.request.arg);
11599         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11600     },
11601
11602     // private
11603     update : function(dataSet){
11604
11605     },
11606
11607     // private
11608     updateResponse : function(dataSet){
11609
11610     }
11611 });/*
11612  * Based on:
11613  * Ext JS Library 1.1.1
11614  * Copyright(c) 2006-2007, Ext JS, LLC.
11615  *
11616  * Originally Released Under LGPL - original licence link has changed is not relivant.
11617  *
11618  * Fork - LGPL
11619  * <script type="text/javascript">
11620  */
11621
11622 /**
11623  * @class Roo.data.ScriptTagProxy
11624  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11625  * other than the originating domain of the running page.<br><br>
11626  * <p>
11627  * <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
11628  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11629  * <p>
11630  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11631  * source code that is used as the source inside a &lt;script> tag.<br><br>
11632  * <p>
11633  * In order for the browser to process the returned data, the server must wrap the data object
11634  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11635  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11636  * depending on whether the callback name was passed:
11637  * <p>
11638  * <pre><code>
11639 boolean scriptTag = false;
11640 String cb = request.getParameter("callback");
11641 if (cb != null) {
11642     scriptTag = true;
11643     response.setContentType("text/javascript");
11644 } else {
11645     response.setContentType("application/x-json");
11646 }
11647 Writer out = response.getWriter();
11648 if (scriptTag) {
11649     out.write(cb + "(");
11650 }
11651 out.print(dataBlock.toJsonString());
11652 if (scriptTag) {
11653     out.write(");");
11654 }
11655 </pre></code>
11656  *
11657  * @constructor
11658  * @param {Object} config A configuration object.
11659  */
11660 Roo.data.ScriptTagProxy = function(config){
11661     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11662     Roo.apply(this, config);
11663     this.head = document.getElementsByTagName("head")[0];
11664 };
11665
11666 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11667
11668 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11669     /**
11670      * @cfg {String} url The URL from which to request the data object.
11671      */
11672     /**
11673      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11674      */
11675     timeout : 30000,
11676     /**
11677      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11678      * the server the name of the callback function set up by the load call to process the returned data object.
11679      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11680      * javascript output which calls this named function passing the data object as its only parameter.
11681      */
11682     callbackParam : "callback",
11683     /**
11684      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11685      * name to the request.
11686      */
11687     nocache : true,
11688
11689     /**
11690      * Load data from the configured URL, read the data object into
11691      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11692      * process that block using the passed callback.
11693      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11694      * for the request to the remote server.
11695      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11696      * object into a block of Roo.data.Records.
11697      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11698      * The function must be passed <ul>
11699      * <li>The Record block object</li>
11700      * <li>The "arg" argument from the load function</li>
11701      * <li>A boolean success indicator</li>
11702      * </ul>
11703      * @param {Object} scope The scope in which to call the callback
11704      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11705      */
11706     load : function(params, reader, callback, scope, arg){
11707         if(this.fireEvent("beforeload", this, params) !== false){
11708
11709             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11710
11711             var url = this.url;
11712             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11713             if(this.nocache){
11714                 url += "&_dc=" + (new Date().getTime());
11715             }
11716             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11717             var trans = {
11718                 id : transId,
11719                 cb : "stcCallback"+transId,
11720                 scriptId : "stcScript"+transId,
11721                 params : params,
11722                 arg : arg,
11723                 url : url,
11724                 callback : callback,
11725                 scope : scope,
11726                 reader : reader
11727             };
11728             var conn = this;
11729
11730             window[trans.cb] = function(o){
11731                 conn.handleResponse(o, trans);
11732             };
11733
11734             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11735
11736             if(this.autoAbort !== false){
11737                 this.abort();
11738             }
11739
11740             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11741
11742             var script = document.createElement("script");
11743             script.setAttribute("src", url);
11744             script.setAttribute("type", "text/javascript");
11745             script.setAttribute("id", trans.scriptId);
11746             this.head.appendChild(script);
11747
11748             this.trans = trans;
11749         }else{
11750             callback.call(scope||this, null, arg, false);
11751         }
11752     },
11753
11754     // private
11755     isLoading : function(){
11756         return this.trans ? true : false;
11757     },
11758
11759     /**
11760      * Abort the current server request.
11761      */
11762     abort : function(){
11763         if(this.isLoading()){
11764             this.destroyTrans(this.trans);
11765         }
11766     },
11767
11768     // private
11769     destroyTrans : function(trans, isLoaded){
11770         this.head.removeChild(document.getElementById(trans.scriptId));
11771         clearTimeout(trans.timeoutId);
11772         if(isLoaded){
11773             window[trans.cb] = undefined;
11774             try{
11775                 delete window[trans.cb];
11776             }catch(e){}
11777         }else{
11778             // if hasn't been loaded, wait for load to remove it to prevent script error
11779             window[trans.cb] = function(){
11780                 window[trans.cb] = undefined;
11781                 try{
11782                     delete window[trans.cb];
11783                 }catch(e){}
11784             };
11785         }
11786     },
11787
11788     // private
11789     handleResponse : function(o, trans){
11790         this.trans = false;
11791         this.destroyTrans(trans, true);
11792         var result;
11793         try {
11794             result = trans.reader.readRecords(o);
11795         }catch(e){
11796             this.fireEvent("loadexception", this, o, trans.arg, e);
11797             trans.callback.call(trans.scope||window, null, trans.arg, false);
11798             return;
11799         }
11800         this.fireEvent("load", this, o, trans.arg);
11801         trans.callback.call(trans.scope||window, result, trans.arg, true);
11802     },
11803
11804     // private
11805     handleFailure : function(trans){
11806         this.trans = false;
11807         this.destroyTrans(trans, false);
11808         this.fireEvent("loadexception", this, null, trans.arg);
11809         trans.callback.call(trans.scope||window, null, trans.arg, false);
11810     }
11811 });/*
11812  * Based on:
11813  * Ext JS Library 1.1.1
11814  * Copyright(c) 2006-2007, Ext JS, LLC.
11815  *
11816  * Originally Released Under LGPL - original licence link has changed is not relivant.
11817  *
11818  * Fork - LGPL
11819  * <script type="text/javascript">
11820  */
11821
11822 /**
11823  * @class Roo.data.JsonReader
11824  * @extends Roo.data.DataReader
11825  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11826  * based on mappings in a provided Roo.data.Record constructor.
11827  * 
11828  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11829  * in the reply previously. 
11830  * 
11831  * <p>
11832  * Example code:
11833  * <pre><code>
11834 var RecordDef = Roo.data.Record.create([
11835     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11836     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11837 ]);
11838 var myReader = new Roo.data.JsonReader({
11839     totalProperty: "results",    // The property which contains the total dataset size (optional)
11840     root: "rows",                // The property which contains an Array of row objects
11841     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11842 }, RecordDef);
11843 </code></pre>
11844  * <p>
11845  * This would consume a JSON file like this:
11846  * <pre><code>
11847 { 'results': 2, 'rows': [
11848     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11849     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11850 }
11851 </code></pre>
11852  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11853  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11854  * paged from the remote server.
11855  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11856  * @cfg {String} root name of the property which contains the Array of row objects.
11857  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11858  * @cfg {Array} fields Array of field definition objects
11859  * @constructor
11860  * Create a new JsonReader
11861  * @param {Object} meta Metadata configuration options
11862  * @param {Object} recordType Either an Array of field definition objects,
11863  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11864  */
11865 Roo.data.JsonReader = function(meta, recordType){
11866     
11867     meta = meta || {};
11868     // set some defaults:
11869     Roo.applyIf(meta, {
11870         totalProperty: 'total',
11871         successProperty : 'success',
11872         root : 'data',
11873         id : 'id'
11874     });
11875     
11876     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11877 };
11878 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11879     
11880     /**
11881      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11882      * Used by Store query builder to append _requestMeta to params.
11883      * 
11884      */
11885     metaFromRemote : false,
11886     /**
11887      * This method is only used by a DataProxy which has retrieved data from a remote server.
11888      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11889      * @return {Object} data A data block which is used by an Roo.data.Store object as
11890      * a cache of Roo.data.Records.
11891      */
11892     read : function(response){
11893         var json = response.responseText;
11894        
11895         var o = /* eval:var:o */ eval("("+json+")");
11896         if(!o) {
11897             throw {message: "JsonReader.read: Json object not found"};
11898         }
11899         
11900         if(o.metaData){
11901             
11902             delete this.ef;
11903             this.metaFromRemote = true;
11904             this.meta = o.metaData;
11905             this.recordType = Roo.data.Record.create(o.metaData.fields);
11906             this.onMetaChange(this.meta, this.recordType, o);
11907         }
11908         return this.readRecords(o);
11909     },
11910
11911     // private function a store will implement
11912     onMetaChange : function(meta, recordType, o){
11913
11914     },
11915
11916     /**
11917          * @ignore
11918          */
11919     simpleAccess: function(obj, subsc) {
11920         return obj[subsc];
11921     },
11922
11923         /**
11924          * @ignore
11925          */
11926     getJsonAccessor: function(){
11927         var re = /[\[\.]/;
11928         return function(expr) {
11929             try {
11930                 return(re.test(expr))
11931                     ? new Function("obj", "return obj." + expr)
11932                     : function(obj){
11933                         return obj[expr];
11934                     };
11935             } catch(e){}
11936             return Roo.emptyFn;
11937         };
11938     }(),
11939
11940     /**
11941      * Create a data block containing Roo.data.Records from an XML document.
11942      * @param {Object} o An object which contains an Array of row objects in the property specified
11943      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11944      * which contains the total size of the dataset.
11945      * @return {Object} data A data block which is used by an Roo.data.Store object as
11946      * a cache of Roo.data.Records.
11947      */
11948     readRecords : function(o){
11949         /**
11950          * After any data loads, the raw JSON data is available for further custom processing.
11951          * @type Object
11952          */
11953         this.o = o;
11954         var s = this.meta, Record = this.recordType,
11955             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11956
11957 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11958         if (!this.ef) {
11959             if(s.totalProperty) {
11960                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11961                 }
11962                 if(s.successProperty) {
11963                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11964                 }
11965                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11966                 if (s.id) {
11967                         var g = this.getJsonAccessor(s.id);
11968                         this.getId = function(rec) {
11969                                 var r = g(rec);  
11970                                 return (r === undefined || r === "") ? null : r;
11971                         };
11972                 } else {
11973                         this.getId = function(){return null;};
11974                 }
11975             this.ef = [];
11976             for(var jj = 0; jj < fl; jj++){
11977                 f = fi[jj];
11978                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11979                 this.ef[jj] = this.getJsonAccessor(map);
11980             }
11981         }
11982
11983         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11984         if(s.totalProperty){
11985             var vt = parseInt(this.getTotal(o), 10);
11986             if(!isNaN(vt)){
11987                 totalRecords = vt;
11988             }
11989         }
11990         if(s.successProperty){
11991             var vs = this.getSuccess(o);
11992             if(vs === false || vs === 'false'){
11993                 success = false;
11994             }
11995         }
11996         var records = [];
11997         for(var i = 0; i < c; i++){
11998                 var n = root[i];
11999             var values = {};
12000             var id = this.getId(n);
12001             for(var j = 0; j < fl; j++){
12002                 f = fi[j];
12003             var v = this.ef[j](n);
12004             if (!f.convert) {
12005                 Roo.log('missing convert for ' + f.name);
12006                 Roo.log(f);
12007                 continue;
12008             }
12009             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12010             }
12011             var record = new Record(values, id);
12012             record.json = n;
12013             records[i] = record;
12014         }
12015         return {
12016             raw : o,
12017             success : success,
12018             records : records,
12019             totalRecords : totalRecords
12020         };
12021     }
12022 });/*
12023  * Based on:
12024  * Ext JS Library 1.1.1
12025  * Copyright(c) 2006-2007, Ext JS, LLC.
12026  *
12027  * Originally Released Under LGPL - original licence link has changed is not relivant.
12028  *
12029  * Fork - LGPL
12030  * <script type="text/javascript">
12031  */
12032
12033 /**
12034  * @class Roo.data.ArrayReader
12035  * @extends Roo.data.DataReader
12036  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12037  * Each element of that Array represents a row of data fields. The
12038  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12039  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12040  * <p>
12041  * Example code:.
12042  * <pre><code>
12043 var RecordDef = Roo.data.Record.create([
12044     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12045     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12046 ]);
12047 var myReader = new Roo.data.ArrayReader({
12048     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12049 }, RecordDef);
12050 </code></pre>
12051  * <p>
12052  * This would consume an Array like this:
12053  * <pre><code>
12054 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12055   </code></pre>
12056  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12057  * @constructor
12058  * Create a new JsonReader
12059  * @param {Object} meta Metadata configuration options.
12060  * @param {Object} recordType Either an Array of field definition objects
12061  * as specified to {@link Roo.data.Record#create},
12062  * or an {@link Roo.data.Record} object
12063  * created using {@link Roo.data.Record#create}.
12064  */
12065 Roo.data.ArrayReader = function(meta, recordType){
12066     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12067 };
12068
12069 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12070     /**
12071      * Create a data block containing Roo.data.Records from an XML document.
12072      * @param {Object} o An Array of row objects which represents the dataset.
12073      * @return {Object} data A data block which is used by an Roo.data.Store object as
12074      * a cache of Roo.data.Records.
12075      */
12076     readRecords : function(o){
12077         var sid = this.meta ? this.meta.id : null;
12078         var recordType = this.recordType, fields = recordType.prototype.fields;
12079         var records = [];
12080         var root = o;
12081             for(var i = 0; i < root.length; i++){
12082                     var n = root[i];
12083                 var values = {};
12084                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12085                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12086                 var f = fields.items[j];
12087                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12088                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12089                 v = f.convert(v);
12090                 values[f.name] = v;
12091             }
12092                 var record = new recordType(values, id);
12093                 record.json = n;
12094                 records[records.length] = record;
12095             }
12096             return {
12097                 records : records,
12098                 totalRecords : records.length
12099             };
12100     }
12101 });/*
12102  * - LGPL
12103  * * 
12104  */
12105
12106 /**
12107  * @class Roo.bootstrap.ComboBox
12108  * @extends Roo.bootstrap.TriggerField
12109  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12110  * @cfg {Boolean} append (true|false) default false
12111  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12112  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12113  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12114  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12115  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12116  * @cfg {Boolean} animate default true
12117  * @cfg {Boolean} emptyResultText only for touch device
12118  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12119  * @constructor
12120  * Create a new ComboBox.
12121  * @param {Object} config Configuration options
12122  */
12123 Roo.bootstrap.ComboBox = function(config){
12124     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12125     this.addEvents({
12126         /**
12127          * @event expand
12128          * Fires when the dropdown list is expanded
12129              * @param {Roo.bootstrap.ComboBox} combo This combo box
12130              */
12131         'expand' : true,
12132         /**
12133          * @event collapse
12134          * Fires when the dropdown list is collapsed
12135              * @param {Roo.bootstrap.ComboBox} combo This combo box
12136              */
12137         'collapse' : true,
12138         /**
12139          * @event beforeselect
12140          * Fires before a list item is selected. Return false to cancel the selection.
12141              * @param {Roo.bootstrap.ComboBox} combo This combo box
12142              * @param {Roo.data.Record} record The data record returned from the underlying store
12143              * @param {Number} index The index of the selected item in the dropdown list
12144              */
12145         'beforeselect' : true,
12146         /**
12147          * @event select
12148          * Fires when a list item is selected
12149              * @param {Roo.bootstrap.ComboBox} combo This combo box
12150              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12151              * @param {Number} index The index of the selected item in the dropdown list
12152              */
12153         'select' : true,
12154         /**
12155          * @event beforequery
12156          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12157          * The event object passed has these properties:
12158              * @param {Roo.bootstrap.ComboBox} combo This combo box
12159              * @param {String} query The query
12160              * @param {Boolean} forceAll true to force "all" query
12161              * @param {Boolean} cancel true to cancel the query
12162              * @param {Object} e The query event object
12163              */
12164         'beforequery': true,
12165          /**
12166          * @event add
12167          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12168              * @param {Roo.bootstrap.ComboBox} combo This combo box
12169              */
12170         'add' : true,
12171         /**
12172          * @event edit
12173          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12174              * @param {Roo.bootstrap.ComboBox} combo This combo box
12175              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12176              */
12177         'edit' : true,
12178         /**
12179          * @event remove
12180          * Fires when the remove value from the combobox array
12181              * @param {Roo.bootstrap.ComboBox} combo This combo box
12182              */
12183         'remove' : true,
12184         /**
12185          * @event afterremove
12186          * Fires when the remove value from the combobox array
12187              * @param {Roo.bootstrap.ComboBox} combo This combo box
12188              */
12189         'afterremove' : true,
12190         /**
12191          * @event specialfilter
12192          * Fires when specialfilter
12193             * @param {Roo.bootstrap.ComboBox} combo This combo box
12194             */
12195         'specialfilter' : true,
12196         /**
12197          * @event tick
12198          * Fires when tick the element
12199             * @param {Roo.bootstrap.ComboBox} combo This combo box
12200             */
12201         'tick' : true,
12202         /**
12203          * @event touchviewdisplay
12204          * Fires when touch view require special display (default is using displayField)
12205             * @param {Roo.bootstrap.ComboBox} combo This combo box
12206             * @param {Object} cfg set html .
12207             */
12208         'touchviewdisplay' : true
12209         
12210     });
12211     
12212     this.item = [];
12213     this.tickItems = [];
12214     
12215     this.selectedIndex = -1;
12216     if(this.mode == 'local'){
12217         if(config.queryDelay === undefined){
12218             this.queryDelay = 10;
12219         }
12220         if(config.minChars === undefined){
12221             this.minChars = 0;
12222         }
12223     }
12224 };
12225
12226 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12227      
12228     /**
12229      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12230      * rendering into an Roo.Editor, defaults to false)
12231      */
12232     /**
12233      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12234      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12235      */
12236     /**
12237      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12238      */
12239     /**
12240      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12241      * the dropdown list (defaults to undefined, with no header element)
12242      */
12243
12244      /**
12245      * @cfg {String/Roo.Template} tpl The template to use to render the output
12246      */
12247      
12248      /**
12249      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12250      */
12251     listWidth: undefined,
12252     /**
12253      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12254      * mode = 'remote' or 'text' if mode = 'local')
12255      */
12256     displayField: undefined,
12257     
12258     /**
12259      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12260      * mode = 'remote' or 'value' if mode = 'local'). 
12261      * Note: use of a valueField requires the user make a selection
12262      * in order for a value to be mapped.
12263      */
12264     valueField: undefined,
12265     /**
12266      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12267      */
12268     modalTitle : '',
12269     
12270     /**
12271      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12272      * field's data value (defaults to the underlying DOM element's name)
12273      */
12274     hiddenName: undefined,
12275     /**
12276      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12277      */
12278     listClass: '',
12279     /**
12280      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12281      */
12282     selectedClass: 'active',
12283     
12284     /**
12285      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12286      */
12287     shadow:'sides',
12288     /**
12289      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12290      * anchor positions (defaults to 'tl-bl')
12291      */
12292     listAlign: 'tl-bl?',
12293     /**
12294      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12295      */
12296     maxHeight: 300,
12297     /**
12298      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12299      * query specified by the allQuery config option (defaults to 'query')
12300      */
12301     triggerAction: 'query',
12302     /**
12303      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12304      * (defaults to 4, does not apply if editable = false)
12305      */
12306     minChars : 4,
12307     /**
12308      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12309      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12310      */
12311     typeAhead: false,
12312     /**
12313      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12314      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12315      */
12316     queryDelay: 500,
12317     /**
12318      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12319      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12320      */
12321     pageSize: 0,
12322     /**
12323      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12324      * when editable = true (defaults to false)
12325      */
12326     selectOnFocus:false,
12327     /**
12328      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12329      */
12330     queryParam: 'query',
12331     /**
12332      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12333      * when mode = 'remote' (defaults to 'Loading...')
12334      */
12335     loadingText: 'Loading...',
12336     /**
12337      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12338      */
12339     resizable: false,
12340     /**
12341      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12342      */
12343     handleHeight : 8,
12344     /**
12345      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12346      * traditional select (defaults to true)
12347      */
12348     editable: true,
12349     /**
12350      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12351      */
12352     allQuery: '',
12353     /**
12354      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12355      */
12356     mode: 'remote',
12357     /**
12358      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12359      * listWidth has a higher value)
12360      */
12361     minListWidth : 70,
12362     /**
12363      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12364      * allow the user to set arbitrary text into the field (defaults to false)
12365      */
12366     forceSelection:false,
12367     /**
12368      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12369      * if typeAhead = true (defaults to 250)
12370      */
12371     typeAheadDelay : 250,
12372     /**
12373      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12374      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12375      */
12376     valueNotFoundText : undefined,
12377     /**
12378      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12379      */
12380     blockFocus : false,
12381     
12382     /**
12383      * @cfg {Boolean} disableClear Disable showing of clear button.
12384      */
12385     disableClear : false,
12386     /**
12387      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12388      */
12389     alwaysQuery : false,
12390     
12391     /**
12392      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12393      */
12394     multiple : false,
12395     
12396     /**
12397      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12398      */
12399     invalidClass : "has-warning",
12400     
12401     /**
12402      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12403      */
12404     validClass : "has-success",
12405     
12406     /**
12407      * @cfg {Boolean} specialFilter (true|false) special filter default false
12408      */
12409     specialFilter : false,
12410     
12411     /**
12412      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12413      */
12414     mobileTouchView : true,
12415     
12416     /**
12417      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12418      */
12419     useNativeIOS : false,
12420     
12421     ios_options : false,
12422     
12423     //private
12424     addicon : false,
12425     editicon: false,
12426     
12427     page: 0,
12428     hasQuery: false,
12429     append: false,
12430     loadNext: false,
12431     autoFocus : true,
12432     tickable : false,
12433     btnPosition : 'right',
12434     triggerList : true,
12435     showToggleBtn : true,
12436     animate : true,
12437     emptyResultText: 'Empty',
12438     triggerText : 'Select',
12439     
12440     // element that contains real text value.. (when hidden is used..)
12441     
12442     getAutoCreate : function()
12443     {
12444         var cfg = false;
12445         
12446         /*
12447          * Render classic select for iso
12448          */
12449         
12450         if(Roo.isIOS && this.useNativeIOS){
12451             cfg = this.getAutoCreateNativeIOS();
12452             return cfg;
12453         }
12454         
12455         /*
12456          * Touch Devices
12457          */
12458         
12459         if(Roo.isTouch && this.mobileTouchView){
12460             cfg = this.getAutoCreateTouchView();
12461             return cfg;;
12462         }
12463         
12464         /*
12465          *  Normal ComboBox
12466          */
12467         if(!this.tickable){
12468             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12469             return cfg;
12470         }
12471         
12472         /*
12473          *  ComboBox with tickable selections
12474          */
12475              
12476         var align = this.labelAlign || this.parentLabelAlign();
12477         
12478         cfg = {
12479             cls : 'form-group roo-combobox-tickable' //input-group
12480         };
12481         
12482         var btn_text_select = '';
12483         var btn_text_done = '';
12484         var btn_text_cancel = '';
12485         
12486         if (this.btn_text_show) {
12487             btn_text_select = 'Select';
12488             btn_text_done = 'Done';
12489             btn_text_cancel = 'Cancel'; 
12490         }
12491         
12492         var buttons = {
12493             tag : 'div',
12494             cls : 'tickable-buttons',
12495             cn : [
12496                 {
12497                     tag : 'button',
12498                     type : 'button',
12499                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12500                     //html : this.triggerText
12501                     html: btn_text_select
12502                 },
12503                 {
12504                     tag : 'button',
12505                     type : 'button',
12506                     name : 'ok',
12507                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12508                     //html : 'Done'
12509                     html: btn_text_done
12510                 },
12511                 {
12512                     tag : 'button',
12513                     type : 'button',
12514                     name : 'cancel',
12515                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12516                     //html : 'Cancel'
12517                     html: btn_text_cancel
12518                 }
12519             ]
12520         };
12521         
12522         if(this.editable){
12523             buttons.cn.unshift({
12524                 tag: 'input',
12525                 cls: 'roo-select2-search-field-input'
12526             });
12527         }
12528         
12529         var _this = this;
12530         
12531         Roo.each(buttons.cn, function(c){
12532             if (_this.size) {
12533                 c.cls += ' btn-' + _this.size;
12534             }
12535
12536             if (_this.disabled) {
12537                 c.disabled = true;
12538             }
12539         });
12540         
12541         var box = {
12542             tag: 'div',
12543             cn: [
12544                 {
12545                     tag: 'input',
12546                     type : 'hidden',
12547                     cls: 'form-hidden-field'
12548                 },
12549                 {
12550                     tag: 'ul',
12551                     cls: 'roo-select2-choices',
12552                     cn:[
12553                         {
12554                             tag: 'li',
12555                             cls: 'roo-select2-search-field',
12556                             cn: [
12557
12558                                 buttons
12559                             ]
12560                         }
12561                     ]
12562                 }
12563             ]
12564         };
12565         
12566         var combobox = {
12567             cls: 'roo-select2-container input-group roo-select2-container-multi',
12568             cn: [
12569                 box
12570 //                {
12571 //                    tag: 'ul',
12572 //                    cls: 'typeahead typeahead-long dropdown-menu',
12573 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12574 //                }
12575             ]
12576         };
12577         
12578         if(this.hasFeedback && !this.allowBlank){
12579             
12580             var feedback = {
12581                 tag: 'span',
12582                 cls: 'glyphicon form-control-feedback'
12583             };
12584
12585             combobox.cn.push(feedback);
12586         }
12587         
12588         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12589             
12590 //                Roo.log("left and has label");
12591             cfg.cn = [
12592                 {
12593                     tag : 'i',
12594                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12595                     tooltip : 'This field is required'
12596                 },
12597                 {
12598                     tag: 'label',
12599                     'for' :  id,
12600                     cls : 'control-label col-sm-' + this.labelWidth,
12601                     html : this.fieldLabel
12602
12603                 },
12604                 {
12605                     cls : "col-sm-" + (12 - this.labelWidth), 
12606                     cn: [
12607                         combobox
12608                     ]
12609                 }
12610
12611             ];
12612
12613             if(this.indicatorpos == 'right'){
12614                 
12615                 cfg.cn = [
12616                     {
12617                         tag: 'label',
12618                         'for' :  id,
12619                         cls : 'control-label col-sm-' + this.labelWidth,
12620                         html : this.fieldLabel
12621
12622                     },
12623                     {
12624                         tag : 'i',
12625                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12626                         tooltip : 'This field is required'
12627                     },
12628                     {
12629                         cls : "col-sm-" + (12 - this.labelWidth), 
12630                         cn: [
12631                             combobox
12632                         ]
12633                     }
12634
12635                 ];
12636             
12637             }
12638                 
12639                 
12640         } else if ( this.fieldLabel.length) {
12641 //                Roo.log(" label");
12642                  cfg.cn = [
12643                     {
12644                         tag : 'i',
12645                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12646                         tooltip : 'This field is required'
12647                     },
12648                     {
12649                         tag: 'label',
12650                         //cls : 'input-group-addon',
12651                         html : this.fieldLabel
12652                         
12653                     },
12654                     
12655                     combobox
12656                     
12657                 ];
12658                 
12659                 if(this.indicatorpos == 'right'){
12660                     
12661                     cfg.cn = [
12662                         {
12663                             tag: 'label',
12664                             //cls : 'input-group-addon',
12665                             html : this.fieldLabel
12666
12667                         },
12668                         
12669                         {
12670                             tag : 'i',
12671                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12672                             tooltip : 'This field is required'
12673                         },
12674                         
12675                         combobox
12676
12677                     ];
12678                 
12679                 }
12680
12681         } else {
12682             
12683 //                Roo.log(" no label && no align");
12684                 cfg = combobox
12685                      
12686                 
12687         }
12688          
12689         var settings=this;
12690         ['xs','sm','md','lg'].map(function(size){
12691             if (settings[size]) {
12692                 cfg.cls += ' col-' + size + '-' + settings[size];
12693             }
12694         });
12695         
12696         return cfg;
12697         
12698     },
12699     
12700     _initEventsCalled : false,
12701     
12702     // private
12703     initEvents: function()
12704     {   
12705         if (this._initEventsCalled) { // as we call render... prevent looping...
12706             return;
12707         }
12708         this._initEventsCalled = true;
12709         
12710         if (!this.store) {
12711             throw "can not find store for combo";
12712         }
12713         
12714         this.store = Roo.factory(this.store, Roo.data);
12715         
12716         // if we are building from html. then this element is so complex, that we can not really
12717         // use the rendered HTML.
12718         // so we have to trash and replace the previous code.
12719         if (Roo.XComponent.build_from_html) {
12720             
12721             // remove this element....
12722             var e = this.el.dom, k=0;
12723             while (e ) { e = e.previousSibling;  ++k;}
12724
12725             this.el.remove();
12726             
12727             this.el=false;
12728             this.rendered = false;
12729             
12730             this.render(this.parent().getChildContainer(true), k);
12731             
12732             
12733             
12734         }
12735         
12736         if(Roo.isIOS && this.useNativeIOS){
12737             this.initIOSView();
12738             return;
12739         }
12740         
12741         /*
12742          * Touch Devices
12743          */
12744         
12745         if(Roo.isTouch && this.mobileTouchView){
12746             this.initTouchView();
12747             return;
12748         }
12749         
12750         if(this.tickable){
12751             this.initTickableEvents();
12752             return;
12753         }
12754         
12755         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12756         
12757         if(this.hiddenName){
12758             
12759             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12760             
12761             this.hiddenField.dom.value =
12762                 this.hiddenValue !== undefined ? this.hiddenValue :
12763                 this.value !== undefined ? this.value : '';
12764
12765             // prevent input submission
12766             this.el.dom.removeAttribute('name');
12767             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12768              
12769              
12770         }
12771         //if(Roo.isGecko){
12772         //    this.el.dom.setAttribute('autocomplete', 'off');
12773         //}
12774         
12775         var cls = 'x-combo-list';
12776         
12777         //this.list = new Roo.Layer({
12778         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12779         //});
12780         
12781         var _this = this;
12782         
12783         (function(){
12784             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12785             _this.list.setWidth(lw);
12786         }).defer(100);
12787         
12788         this.list.on('mouseover', this.onViewOver, this);
12789         this.list.on('mousemove', this.onViewMove, this);
12790         
12791         this.list.on('scroll', this.onViewScroll, this);
12792         
12793         /*
12794         this.list.swallowEvent('mousewheel');
12795         this.assetHeight = 0;
12796
12797         if(this.title){
12798             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12799             this.assetHeight += this.header.getHeight();
12800         }
12801
12802         this.innerList = this.list.createChild({cls:cls+'-inner'});
12803         this.innerList.on('mouseover', this.onViewOver, this);
12804         this.innerList.on('mousemove', this.onViewMove, this);
12805         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12806         
12807         if(this.allowBlank && !this.pageSize && !this.disableClear){
12808             this.footer = this.list.createChild({cls:cls+'-ft'});
12809             this.pageTb = new Roo.Toolbar(this.footer);
12810            
12811         }
12812         if(this.pageSize){
12813             this.footer = this.list.createChild({cls:cls+'-ft'});
12814             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12815                     {pageSize: this.pageSize});
12816             
12817         }
12818         
12819         if (this.pageTb && this.allowBlank && !this.disableClear) {
12820             var _this = this;
12821             this.pageTb.add(new Roo.Toolbar.Fill(), {
12822                 cls: 'x-btn-icon x-btn-clear',
12823                 text: '&#160;',
12824                 handler: function()
12825                 {
12826                     _this.collapse();
12827                     _this.clearValue();
12828                     _this.onSelect(false, -1);
12829                 }
12830             });
12831         }
12832         if (this.footer) {
12833             this.assetHeight += this.footer.getHeight();
12834         }
12835         */
12836             
12837         if(!this.tpl){
12838             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12839         }
12840
12841         this.view = new Roo.View(this.list, this.tpl, {
12842             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12843         });
12844         //this.view.wrapEl.setDisplayed(false);
12845         this.view.on('click', this.onViewClick, this);
12846         
12847         
12848         
12849         this.store.on('beforeload', this.onBeforeLoad, this);
12850         this.store.on('load', this.onLoad, this);
12851         this.store.on('loadexception', this.onLoadException, this);
12852         /*
12853         if(this.resizable){
12854             this.resizer = new Roo.Resizable(this.list,  {
12855                pinned:true, handles:'se'
12856             });
12857             this.resizer.on('resize', function(r, w, h){
12858                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12859                 this.listWidth = w;
12860                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12861                 this.restrictHeight();
12862             }, this);
12863             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12864         }
12865         */
12866         if(!this.editable){
12867             this.editable = true;
12868             this.setEditable(false);
12869         }
12870         
12871         /*
12872         
12873         if (typeof(this.events.add.listeners) != 'undefined') {
12874             
12875             this.addicon = this.wrap.createChild(
12876                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12877        
12878             this.addicon.on('click', function(e) {
12879                 this.fireEvent('add', this);
12880             }, this);
12881         }
12882         if (typeof(this.events.edit.listeners) != 'undefined') {
12883             
12884             this.editicon = this.wrap.createChild(
12885                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12886             if (this.addicon) {
12887                 this.editicon.setStyle('margin-left', '40px');
12888             }
12889             this.editicon.on('click', function(e) {
12890                 
12891                 // we fire even  if inothing is selected..
12892                 this.fireEvent('edit', this, this.lastData );
12893                 
12894             }, this);
12895         }
12896         */
12897         
12898         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12899             "up" : function(e){
12900                 this.inKeyMode = true;
12901                 this.selectPrev();
12902             },
12903
12904             "down" : function(e){
12905                 if(!this.isExpanded()){
12906                     this.onTriggerClick();
12907                 }else{
12908                     this.inKeyMode = true;
12909                     this.selectNext();
12910                 }
12911             },
12912
12913             "enter" : function(e){
12914 //                this.onViewClick();
12915                 //return true;
12916                 this.collapse();
12917                 
12918                 if(this.fireEvent("specialkey", this, e)){
12919                     this.onViewClick(false);
12920                 }
12921                 
12922                 return true;
12923             },
12924
12925             "esc" : function(e){
12926                 this.collapse();
12927             },
12928
12929             "tab" : function(e){
12930                 this.collapse();
12931                 
12932                 if(this.fireEvent("specialkey", this, e)){
12933                     this.onViewClick(false);
12934                 }
12935                 
12936                 return true;
12937             },
12938
12939             scope : this,
12940
12941             doRelay : function(foo, bar, hname){
12942                 if(hname == 'down' || this.scope.isExpanded()){
12943                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12944                 }
12945                 return true;
12946             },
12947
12948             forceKeyDown: true
12949         });
12950         
12951         
12952         this.queryDelay = Math.max(this.queryDelay || 10,
12953                 this.mode == 'local' ? 10 : 250);
12954         
12955         
12956         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12957         
12958         if(this.typeAhead){
12959             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12960         }
12961         if(this.editable !== false){
12962             this.inputEl().on("keyup", this.onKeyUp, this);
12963         }
12964         if(this.forceSelection){
12965             this.inputEl().on('blur', this.doForce, this);
12966         }
12967         
12968         if(this.multiple){
12969             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12970             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12971         }
12972     },
12973     
12974     initTickableEvents: function()
12975     {   
12976         this.createList();
12977         
12978         if(this.hiddenName){
12979             
12980             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12981             
12982             this.hiddenField.dom.value =
12983                 this.hiddenValue !== undefined ? this.hiddenValue :
12984                 this.value !== undefined ? this.value : '';
12985
12986             // prevent input submission
12987             this.el.dom.removeAttribute('name');
12988             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12989              
12990              
12991         }
12992         
12993 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12994         
12995         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12996         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12997         if(this.triggerList){
12998             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12999         }
13000          
13001         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13002         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13003         
13004         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13005         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13006         
13007         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13008         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13009         
13010         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13011         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13012         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13013         
13014         this.okBtn.hide();
13015         this.cancelBtn.hide();
13016         
13017         var _this = this;
13018         
13019         (function(){
13020             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13021             _this.list.setWidth(lw);
13022         }).defer(100);
13023         
13024         this.list.on('mouseover', this.onViewOver, this);
13025         this.list.on('mousemove', this.onViewMove, this);
13026         
13027         this.list.on('scroll', this.onViewScroll, this);
13028         
13029         if(!this.tpl){
13030             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></div></li>';
13031         }
13032
13033         this.view = new Roo.View(this.list, this.tpl, {
13034             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13035         });
13036         
13037         //this.view.wrapEl.setDisplayed(false);
13038         this.view.on('click', this.onViewClick, this);
13039         
13040         
13041         
13042         this.store.on('beforeload', this.onBeforeLoad, this);
13043         this.store.on('load', this.onLoad, this);
13044         this.store.on('loadexception', this.onLoadException, this);
13045         
13046         if(this.editable){
13047             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13048                 "up" : function(e){
13049                     this.inKeyMode = true;
13050                     this.selectPrev();
13051                 },
13052
13053                 "down" : function(e){
13054                     this.inKeyMode = true;
13055                     this.selectNext();
13056                 },
13057
13058                 "enter" : function(e){
13059                     if(this.fireEvent("specialkey", this, e)){
13060                         this.onViewClick(false);
13061                     }
13062                     
13063                     return true;
13064                 },
13065
13066                 "esc" : function(e){
13067                     this.onTickableFooterButtonClick(e, false, false);
13068                 },
13069
13070                 "tab" : function(e){
13071                     this.fireEvent("specialkey", this, e);
13072                     
13073                     this.onTickableFooterButtonClick(e, false, false);
13074                     
13075                     return true;
13076                 },
13077
13078                 scope : this,
13079
13080                 doRelay : function(e, fn, key){
13081                     if(this.scope.isExpanded()){
13082                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13083                     }
13084                     return true;
13085                 },
13086
13087                 forceKeyDown: true
13088             });
13089         }
13090         
13091         this.queryDelay = Math.max(this.queryDelay || 10,
13092                 this.mode == 'local' ? 10 : 250);
13093         
13094         
13095         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13096         
13097         if(this.typeAhead){
13098             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13099         }
13100         
13101         if(this.editable !== false){
13102             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13103         }
13104         
13105     },
13106
13107     onDestroy : function(){
13108         if(this.view){
13109             this.view.setStore(null);
13110             this.view.el.removeAllListeners();
13111             this.view.el.remove();
13112             this.view.purgeListeners();
13113         }
13114         if(this.list){
13115             this.list.dom.innerHTML  = '';
13116         }
13117         
13118         if(this.store){
13119             this.store.un('beforeload', this.onBeforeLoad, this);
13120             this.store.un('load', this.onLoad, this);
13121             this.store.un('loadexception', this.onLoadException, this);
13122         }
13123         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13124     },
13125
13126     // private
13127     fireKey : function(e){
13128         if(e.isNavKeyPress() && !this.list.isVisible()){
13129             this.fireEvent("specialkey", this, e);
13130         }
13131     },
13132
13133     // private
13134     onResize: function(w, h){
13135 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13136 //        
13137 //        if(typeof w != 'number'){
13138 //            // we do not handle it!?!?
13139 //            return;
13140 //        }
13141 //        var tw = this.trigger.getWidth();
13142 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13143 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13144 //        var x = w - tw;
13145 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13146 //            
13147 //        //this.trigger.setStyle('left', x+'px');
13148 //        
13149 //        if(this.list && this.listWidth === undefined){
13150 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13151 //            this.list.setWidth(lw);
13152 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13153 //        }
13154         
13155     
13156         
13157     },
13158
13159     /**
13160      * Allow or prevent the user from directly editing the field text.  If false is passed,
13161      * the user will only be able to select from the items defined in the dropdown list.  This method
13162      * is the runtime equivalent of setting the 'editable' config option at config time.
13163      * @param {Boolean} value True to allow the user to directly edit the field text
13164      */
13165     setEditable : function(value){
13166         if(value == this.editable){
13167             return;
13168         }
13169         this.editable = value;
13170         if(!value){
13171             this.inputEl().dom.setAttribute('readOnly', true);
13172             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13173             this.inputEl().addClass('x-combo-noedit');
13174         }else{
13175             this.inputEl().dom.setAttribute('readOnly', false);
13176             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13177             this.inputEl().removeClass('x-combo-noedit');
13178         }
13179     },
13180
13181     // private
13182     
13183     onBeforeLoad : function(combo,opts){
13184         if(!this.hasFocus){
13185             return;
13186         }
13187          if (!opts.add) {
13188             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13189          }
13190         this.restrictHeight();
13191         this.selectedIndex = -1;
13192     },
13193
13194     // private
13195     onLoad : function(){
13196         
13197         this.hasQuery = false;
13198         
13199         if(!this.hasFocus){
13200             return;
13201         }
13202         
13203         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13204             this.loading.hide();
13205         }
13206              
13207         if(this.store.getCount() > 0){
13208             this.expand();
13209             this.restrictHeight();
13210             if(this.lastQuery == this.allQuery){
13211                 if(this.editable && !this.tickable){
13212                     this.inputEl().dom.select();
13213                 }
13214                 
13215                 if(
13216                     !this.selectByValue(this.value, true) &&
13217                     this.autoFocus && 
13218                     (
13219                         !this.store.lastOptions ||
13220                         typeof(this.store.lastOptions.add) == 'undefined' || 
13221                         this.store.lastOptions.add != true
13222                     )
13223                 ){
13224                     this.select(0, true);
13225                 }
13226             }else{
13227                 if(this.autoFocus){
13228                     this.selectNext();
13229                 }
13230                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13231                     this.taTask.delay(this.typeAheadDelay);
13232                 }
13233             }
13234         }else{
13235             this.onEmptyResults();
13236         }
13237         
13238         //this.el.focus();
13239     },
13240     // private
13241     onLoadException : function()
13242     {
13243         this.hasQuery = false;
13244         
13245         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13246             this.loading.hide();
13247         }
13248         
13249         if(this.tickable && this.editable){
13250             return;
13251         }
13252         
13253         this.collapse();
13254         // only causes errors at present
13255         //Roo.log(this.store.reader.jsonData);
13256         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13257             // fixme
13258             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13259         //}
13260         
13261         
13262     },
13263     // private
13264     onTypeAhead : function(){
13265         if(this.store.getCount() > 0){
13266             var r = this.store.getAt(0);
13267             var newValue = r.data[this.displayField];
13268             var len = newValue.length;
13269             var selStart = this.getRawValue().length;
13270             
13271             if(selStart != len){
13272                 this.setRawValue(newValue);
13273                 this.selectText(selStart, newValue.length);
13274             }
13275         }
13276     },
13277
13278     // private
13279     onSelect : function(record, index){
13280         
13281         if(this.fireEvent('beforeselect', this, record, index) !== false){
13282         
13283             this.setFromData(index > -1 ? record.data : false);
13284             
13285             this.collapse();
13286             this.fireEvent('select', this, record, index);
13287         }
13288     },
13289
13290     /**
13291      * Returns the currently selected field value or empty string if no value is set.
13292      * @return {String} value The selected value
13293      */
13294     getValue : function()
13295     {
13296         if(Roo.isIOS && this.useNativeIOS){
13297             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13298         }
13299         
13300         if(this.multiple){
13301             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13302         }
13303         
13304         if(this.valueField){
13305             return typeof this.value != 'undefined' ? this.value : '';
13306         }else{
13307             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13308         }
13309     },
13310     
13311     getRawValue : function()
13312     {
13313         if(Roo.isIOS && this.useNativeIOS){
13314             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13315         }
13316         
13317         var v = this.inputEl().getValue();
13318         
13319         return v;
13320     },
13321
13322     /**
13323      * Clears any text/value currently set in the field
13324      */
13325     clearValue : function(){
13326         
13327         if(this.hiddenField){
13328             this.hiddenField.dom.value = '';
13329         }
13330         this.value = '';
13331         this.setRawValue('');
13332         this.lastSelectionText = '';
13333         this.lastData = false;
13334         
13335         var close = this.closeTriggerEl();
13336         
13337         if(close){
13338             close.hide();
13339         }
13340         
13341         this.validate();
13342         
13343     },
13344
13345     /**
13346      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13347      * will be displayed in the field.  If the value does not match the data value of an existing item,
13348      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13349      * Otherwise the field will be blank (although the value will still be set).
13350      * @param {String} value The value to match
13351      */
13352     setValue : function(v)
13353     {
13354         if(Roo.isIOS && this.useNativeIOS){
13355             this.setIOSValue(v);
13356             return;
13357         }
13358         
13359         if(this.multiple){
13360             this.syncValue();
13361             return;
13362         }
13363         
13364         var text = v;
13365         if(this.valueField){
13366             var r = this.findRecord(this.valueField, v);
13367             if(r){
13368                 text = r.data[this.displayField];
13369             }else if(this.valueNotFoundText !== undefined){
13370                 text = this.valueNotFoundText;
13371             }
13372         }
13373         this.lastSelectionText = text;
13374         if(this.hiddenField){
13375             this.hiddenField.dom.value = v;
13376         }
13377         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13378         this.value = v;
13379         
13380         var close = this.closeTriggerEl();
13381         
13382         if(close){
13383             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13384         }
13385         
13386         this.validate();
13387     },
13388     /**
13389      * @property {Object} the last set data for the element
13390      */
13391     
13392     lastData : false,
13393     /**
13394      * Sets the value of the field based on a object which is related to the record format for the store.
13395      * @param {Object} value the value to set as. or false on reset?
13396      */
13397     setFromData : function(o){
13398         
13399         if(this.multiple){
13400             this.addItem(o);
13401             return;
13402         }
13403             
13404         var dv = ''; // display value
13405         var vv = ''; // value value..
13406         this.lastData = o;
13407         if (this.displayField) {
13408             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13409         } else {
13410             // this is an error condition!!!
13411             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13412         }
13413         
13414         if(this.valueField){
13415             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13416         }
13417         
13418         var close = this.closeTriggerEl();
13419         
13420         if(close){
13421             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13422         }
13423         
13424         if(this.hiddenField){
13425             this.hiddenField.dom.value = vv;
13426             
13427             this.lastSelectionText = dv;
13428             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13429             this.value = vv;
13430             return;
13431         }
13432         // no hidden field.. - we store the value in 'value', but still display
13433         // display field!!!!
13434         this.lastSelectionText = dv;
13435         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13436         this.value = vv;
13437         
13438         
13439         
13440     },
13441     // private
13442     reset : function(){
13443         // overridden so that last data is reset..
13444         
13445         if(this.multiple){
13446             this.clearItem();
13447             return;
13448         }
13449         
13450         this.setValue(this.originalValue);
13451         //this.clearInvalid();
13452         this.lastData = false;
13453         if (this.view) {
13454             this.view.clearSelections();
13455         }
13456         
13457         this.validate();
13458     },
13459     // private
13460     findRecord : function(prop, value){
13461         var record;
13462         if(this.store.getCount() > 0){
13463             this.store.each(function(r){
13464                 if(r.data[prop] == value){
13465                     record = r;
13466                     return false;
13467                 }
13468                 return true;
13469             });
13470         }
13471         return record;
13472     },
13473     
13474     getName: function()
13475     {
13476         // returns hidden if it's set..
13477         if (!this.rendered) {return ''};
13478         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13479         
13480     },
13481     // private
13482     onViewMove : function(e, t){
13483         this.inKeyMode = false;
13484     },
13485
13486     // private
13487     onViewOver : function(e, t){
13488         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13489             return;
13490         }
13491         var item = this.view.findItemFromChild(t);
13492         
13493         if(item){
13494             var index = this.view.indexOf(item);
13495             this.select(index, false);
13496         }
13497     },
13498
13499     // private
13500     onViewClick : function(view, doFocus, el, e)
13501     {
13502         var index = this.view.getSelectedIndexes()[0];
13503         
13504         var r = this.store.getAt(index);
13505         
13506         if(this.tickable){
13507             
13508             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13509                 return;
13510             }
13511             
13512             var rm = false;
13513             var _this = this;
13514             
13515             Roo.each(this.tickItems, function(v,k){
13516                 
13517                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13518                     Roo.log(v);
13519                     _this.tickItems.splice(k, 1);
13520                     
13521                     if(typeof(e) == 'undefined' && view == false){
13522                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13523                     }
13524                     
13525                     rm = true;
13526                     return;
13527                 }
13528             });
13529             
13530             if(rm){
13531                 return;
13532             }
13533             
13534             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13535                 this.tickItems.push(r.data);
13536             }
13537             
13538             if(typeof(e) == 'undefined' && view == false){
13539                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13540             }
13541                     
13542             return;
13543         }
13544         
13545         if(r){
13546             this.onSelect(r, index);
13547         }
13548         if(doFocus !== false && !this.blockFocus){
13549             this.inputEl().focus();
13550         }
13551     },
13552
13553     // private
13554     restrictHeight : function(){
13555         //this.innerList.dom.style.height = '';
13556         //var inner = this.innerList.dom;
13557         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13558         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13559         //this.list.beginUpdate();
13560         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13561         this.list.alignTo(this.inputEl(), this.listAlign);
13562         this.list.alignTo(this.inputEl(), this.listAlign);
13563         //this.list.endUpdate();
13564     },
13565
13566     // private
13567     onEmptyResults : function(){
13568         
13569         if(this.tickable && this.editable){
13570             this.restrictHeight();
13571             return;
13572         }
13573         
13574         this.collapse();
13575     },
13576
13577     /**
13578      * Returns true if the dropdown list is expanded, else false.
13579      */
13580     isExpanded : function(){
13581         return this.list.isVisible();
13582     },
13583
13584     /**
13585      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13586      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13587      * @param {String} value The data value of the item to select
13588      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13589      * selected item if it is not currently in view (defaults to true)
13590      * @return {Boolean} True if the value matched an item in the list, else false
13591      */
13592     selectByValue : function(v, scrollIntoView){
13593         if(v !== undefined && v !== null){
13594             var r = this.findRecord(this.valueField || this.displayField, v);
13595             if(r){
13596                 this.select(this.store.indexOf(r), scrollIntoView);
13597                 return true;
13598             }
13599         }
13600         return false;
13601     },
13602
13603     /**
13604      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13605      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13606      * @param {Number} index The zero-based index of the list item to select
13607      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13608      * selected item if it is not currently in view (defaults to true)
13609      */
13610     select : function(index, scrollIntoView){
13611         this.selectedIndex = index;
13612         this.view.select(index);
13613         if(scrollIntoView !== false){
13614             var el = this.view.getNode(index);
13615             /*
13616              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13617              */
13618             if(el){
13619                 this.list.scrollChildIntoView(el, false);
13620             }
13621         }
13622     },
13623
13624     // private
13625     selectNext : function(){
13626         var ct = this.store.getCount();
13627         if(ct > 0){
13628             if(this.selectedIndex == -1){
13629                 this.select(0);
13630             }else if(this.selectedIndex < ct-1){
13631                 this.select(this.selectedIndex+1);
13632             }
13633         }
13634     },
13635
13636     // private
13637     selectPrev : function(){
13638         var ct = this.store.getCount();
13639         if(ct > 0){
13640             if(this.selectedIndex == -1){
13641                 this.select(0);
13642             }else if(this.selectedIndex != 0){
13643                 this.select(this.selectedIndex-1);
13644             }
13645         }
13646     },
13647
13648     // private
13649     onKeyUp : function(e){
13650         if(this.editable !== false && !e.isSpecialKey()){
13651             this.lastKey = e.getKey();
13652             this.dqTask.delay(this.queryDelay);
13653         }
13654     },
13655
13656     // private
13657     validateBlur : function(){
13658         return !this.list || !this.list.isVisible();   
13659     },
13660
13661     // private
13662     initQuery : function(){
13663         
13664         var v = this.getRawValue();
13665         
13666         if(this.tickable && this.editable){
13667             v = this.tickableInputEl().getValue();
13668         }
13669         
13670         this.doQuery(v);
13671     },
13672
13673     // private
13674     doForce : function(){
13675         if(this.inputEl().dom.value.length > 0){
13676             this.inputEl().dom.value =
13677                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13678              
13679         }
13680     },
13681
13682     /**
13683      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13684      * query allowing the query action to be canceled if needed.
13685      * @param {String} query The SQL query to execute
13686      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13687      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13688      * saved in the current store (defaults to false)
13689      */
13690     doQuery : function(q, forceAll){
13691         
13692         if(q === undefined || q === null){
13693             q = '';
13694         }
13695         var qe = {
13696             query: q,
13697             forceAll: forceAll,
13698             combo: this,
13699             cancel:false
13700         };
13701         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13702             return false;
13703         }
13704         q = qe.query;
13705         
13706         forceAll = qe.forceAll;
13707         if(forceAll === true || (q.length >= this.minChars)){
13708             
13709             this.hasQuery = true;
13710             
13711             if(this.lastQuery != q || this.alwaysQuery){
13712                 this.lastQuery = q;
13713                 if(this.mode == 'local'){
13714                     this.selectedIndex = -1;
13715                     if(forceAll){
13716                         this.store.clearFilter();
13717                     }else{
13718                         
13719                         if(this.specialFilter){
13720                             this.fireEvent('specialfilter', this);
13721                             this.onLoad();
13722                             return;
13723                         }
13724                         
13725                         this.store.filter(this.displayField, q);
13726                     }
13727                     
13728                     this.store.fireEvent("datachanged", this.store);
13729                     
13730                     this.onLoad();
13731                     
13732                     
13733                 }else{
13734                     
13735                     this.store.baseParams[this.queryParam] = q;
13736                     
13737                     var options = {params : this.getParams(q)};
13738                     
13739                     if(this.loadNext){
13740                         options.add = true;
13741                         options.params.start = this.page * this.pageSize;
13742                     }
13743                     
13744                     this.store.load(options);
13745                     
13746                     /*
13747                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13748                      *  we should expand the list on onLoad
13749                      *  so command out it
13750                      */
13751 //                    this.expand();
13752                 }
13753             }else{
13754                 this.selectedIndex = -1;
13755                 this.onLoad();   
13756             }
13757         }
13758         
13759         this.loadNext = false;
13760     },
13761     
13762     // private
13763     getParams : function(q){
13764         var p = {};
13765         //p[this.queryParam] = q;
13766         
13767         if(this.pageSize){
13768             p.start = 0;
13769             p.limit = this.pageSize;
13770         }
13771         return p;
13772     },
13773
13774     /**
13775      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13776      */
13777     collapse : function(){
13778         if(!this.isExpanded()){
13779             return;
13780         }
13781         
13782         this.list.hide();
13783         
13784         this.hasFocus = false;
13785         
13786         if(this.tickable){
13787             this.okBtn.hide();
13788             this.cancelBtn.hide();
13789             this.trigger.show();
13790             
13791             if(this.editable){
13792                 this.tickableInputEl().dom.value = '';
13793                 this.tickableInputEl().blur();
13794             }
13795             
13796         }
13797         
13798         Roo.get(document).un('mousedown', this.collapseIf, this);
13799         Roo.get(document).un('mousewheel', this.collapseIf, this);
13800         if (!this.editable) {
13801             Roo.get(document).un('keydown', this.listKeyPress, this);
13802         }
13803         this.fireEvent('collapse', this);
13804         
13805         this.validate();
13806     },
13807
13808     // private
13809     collapseIf : function(e){
13810         var in_combo  = e.within(this.el);
13811         var in_list =  e.within(this.list);
13812         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13813         
13814         if (in_combo || in_list || is_list) {
13815             //e.stopPropagation();
13816             return;
13817         }
13818         
13819         if(this.tickable){
13820             this.onTickableFooterButtonClick(e, false, false);
13821         }
13822
13823         this.collapse();
13824         
13825     },
13826
13827     /**
13828      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13829      */
13830     expand : function(){
13831        
13832         if(this.isExpanded() || !this.hasFocus){
13833             return;
13834         }
13835         
13836         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13837         this.list.setWidth(lw);
13838         
13839         Roo.log('expand');
13840         
13841         this.list.show();
13842         
13843         this.restrictHeight();
13844         
13845         if(this.tickable){
13846             
13847             this.tickItems = Roo.apply([], this.item);
13848             
13849             this.okBtn.show();
13850             this.cancelBtn.show();
13851             this.trigger.hide();
13852             
13853             if(this.editable){
13854                 this.tickableInputEl().focus();
13855             }
13856             
13857         }
13858         
13859         Roo.get(document).on('mousedown', this.collapseIf, this);
13860         Roo.get(document).on('mousewheel', this.collapseIf, this);
13861         if (!this.editable) {
13862             Roo.get(document).on('keydown', this.listKeyPress, this);
13863         }
13864         
13865         this.fireEvent('expand', this);
13866     },
13867
13868     // private
13869     // Implements the default empty TriggerField.onTriggerClick function
13870     onTriggerClick : function(e)
13871     {
13872         Roo.log('trigger click');
13873         
13874         if(this.disabled || !this.triggerList){
13875             return;
13876         }
13877         
13878         this.page = 0;
13879         this.loadNext = false;
13880         
13881         if(this.isExpanded()){
13882             this.collapse();
13883             if (!this.blockFocus) {
13884                 this.inputEl().focus();
13885             }
13886             
13887         }else {
13888             this.hasFocus = true;
13889             if(this.triggerAction == 'all') {
13890                 this.doQuery(this.allQuery, true);
13891             } else {
13892                 this.doQuery(this.getRawValue());
13893             }
13894             if (!this.blockFocus) {
13895                 this.inputEl().focus();
13896             }
13897         }
13898     },
13899     
13900     onTickableTriggerClick : function(e)
13901     {
13902         if(this.disabled){
13903             return;
13904         }
13905         
13906         this.page = 0;
13907         this.loadNext = false;
13908         this.hasFocus = true;
13909         
13910         if(this.triggerAction == 'all') {
13911             this.doQuery(this.allQuery, true);
13912         } else {
13913             this.doQuery(this.getRawValue());
13914         }
13915     },
13916     
13917     onSearchFieldClick : function(e)
13918     {
13919         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13920             this.onTickableFooterButtonClick(e, false, false);
13921             return;
13922         }
13923         
13924         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13925             return;
13926         }
13927         
13928         this.page = 0;
13929         this.loadNext = false;
13930         this.hasFocus = true;
13931         
13932         if(this.triggerAction == 'all') {
13933             this.doQuery(this.allQuery, true);
13934         } else {
13935             this.doQuery(this.getRawValue());
13936         }
13937     },
13938     
13939     listKeyPress : function(e)
13940     {
13941         //Roo.log('listkeypress');
13942         // scroll to first matching element based on key pres..
13943         if (e.isSpecialKey()) {
13944             return false;
13945         }
13946         var k = String.fromCharCode(e.getKey()).toUpperCase();
13947         //Roo.log(k);
13948         var match  = false;
13949         var csel = this.view.getSelectedNodes();
13950         var cselitem = false;
13951         if (csel.length) {
13952             var ix = this.view.indexOf(csel[0]);
13953             cselitem  = this.store.getAt(ix);
13954             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13955                 cselitem = false;
13956             }
13957             
13958         }
13959         
13960         this.store.each(function(v) { 
13961             if (cselitem) {
13962                 // start at existing selection.
13963                 if (cselitem.id == v.id) {
13964                     cselitem = false;
13965                 }
13966                 return true;
13967             }
13968                 
13969             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13970                 match = this.store.indexOf(v);
13971                 return false;
13972             }
13973             return true;
13974         }, this);
13975         
13976         if (match === false) {
13977             return true; // no more action?
13978         }
13979         // scroll to?
13980         this.view.select(match);
13981         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13982         sn.scrollIntoView(sn.dom.parentNode, false);
13983     },
13984     
13985     onViewScroll : function(e, t){
13986         
13987         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){
13988             return;
13989         }
13990         
13991         this.hasQuery = true;
13992         
13993         this.loading = this.list.select('.loading', true).first();
13994         
13995         if(this.loading === null){
13996             this.list.createChild({
13997                 tag: 'div',
13998                 cls: 'loading roo-select2-more-results roo-select2-active',
13999                 html: 'Loading more results...'
14000             });
14001             
14002             this.loading = this.list.select('.loading', true).first();
14003             
14004             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14005             
14006             this.loading.hide();
14007         }
14008         
14009         this.loading.show();
14010         
14011         var _combo = this;
14012         
14013         this.page++;
14014         this.loadNext = true;
14015         
14016         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14017         
14018         return;
14019     },
14020     
14021     addItem : function(o)
14022     {   
14023         var dv = ''; // display value
14024         
14025         if (this.displayField) {
14026             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14027         } else {
14028             // this is an error condition!!!
14029             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14030         }
14031         
14032         if(!dv.length){
14033             return;
14034         }
14035         
14036         var choice = this.choices.createChild({
14037             tag: 'li',
14038             cls: 'roo-select2-search-choice',
14039             cn: [
14040                 {
14041                     tag: 'div',
14042                     html: dv
14043                 },
14044                 {
14045                     tag: 'a',
14046                     href: '#',
14047                     cls: 'roo-select2-search-choice-close',
14048                     tabindex: '-1'
14049                 }
14050             ]
14051             
14052         }, this.searchField);
14053         
14054         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14055         
14056         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14057         
14058         this.item.push(o);
14059         
14060         this.lastData = o;
14061         
14062         this.syncValue();
14063         
14064         this.inputEl().dom.value = '';
14065         
14066         this.validate();
14067     },
14068     
14069     onRemoveItem : function(e, _self, o)
14070     {
14071         e.preventDefault();
14072         
14073         this.lastItem = Roo.apply([], this.item);
14074         
14075         var index = this.item.indexOf(o.data) * 1;
14076         
14077         if( index < 0){
14078             Roo.log('not this item?!');
14079             return;
14080         }
14081         
14082         this.item.splice(index, 1);
14083         o.item.remove();
14084         
14085         this.syncValue();
14086         
14087         this.fireEvent('remove', this, e);
14088         
14089         this.validate();
14090         
14091     },
14092     
14093     syncValue : function()
14094     {
14095         if(!this.item.length){
14096             this.clearValue();
14097             return;
14098         }
14099             
14100         var value = [];
14101         var _this = this;
14102         Roo.each(this.item, function(i){
14103             if(_this.valueField){
14104                 value.push(i[_this.valueField]);
14105                 return;
14106             }
14107
14108             value.push(i);
14109         });
14110
14111         this.value = value.join(',');
14112
14113         if(this.hiddenField){
14114             this.hiddenField.dom.value = this.value;
14115         }
14116         
14117         this.store.fireEvent("datachanged", this.store);
14118         
14119         this.validate();
14120     },
14121     
14122     clearItem : function()
14123     {
14124         if(!this.multiple){
14125             return;
14126         }
14127         
14128         this.item = [];
14129         
14130         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14131            c.remove();
14132         });
14133         
14134         this.syncValue();
14135         
14136         this.validate();
14137         
14138         if(this.tickable && !Roo.isTouch){
14139             this.view.refresh();
14140         }
14141     },
14142     
14143     inputEl: function ()
14144     {
14145         if(Roo.isIOS && this.useNativeIOS){
14146             return this.el.select('select.roo-ios-select', true).first();
14147         }
14148         
14149         if(Roo.isTouch && this.mobileTouchView){
14150             return this.el.select('input.form-control',true).first();
14151         }
14152         
14153         if(this.tickable){
14154             return this.searchField;
14155         }
14156         
14157         return this.el.select('input.form-control',true).first();
14158     },
14159     
14160     onTickableFooterButtonClick : function(e, btn, el)
14161     {
14162         e.preventDefault();
14163         
14164         this.lastItem = Roo.apply([], this.item);
14165         
14166         if(btn && btn.name == 'cancel'){
14167             this.tickItems = Roo.apply([], this.item);
14168             this.collapse();
14169             return;
14170         }
14171         
14172         this.clearItem();
14173         
14174         var _this = this;
14175         
14176         Roo.each(this.tickItems, function(o){
14177             _this.addItem(o);
14178         });
14179         
14180         this.collapse();
14181         
14182     },
14183     
14184     validate : function()
14185     {
14186         var v = this.getRawValue();
14187         
14188         if(this.multiple){
14189             v = this.getValue();
14190         }
14191         
14192         if(this.disabled || this.allowBlank || v.length){
14193             this.markValid();
14194             return true;
14195         }
14196         
14197         this.markInvalid();
14198         return false;
14199     },
14200     
14201     tickableInputEl : function()
14202     {
14203         if(!this.tickable || !this.editable){
14204             return this.inputEl();
14205         }
14206         
14207         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14208     },
14209     
14210     
14211     getAutoCreateTouchView : function()
14212     {
14213         var id = Roo.id();
14214         
14215         var cfg = {
14216             cls: 'form-group' //input-group
14217         };
14218         
14219         var input =  {
14220             tag: 'input',
14221             id : id,
14222             type : this.inputType,
14223             cls : 'form-control x-combo-noedit',
14224             autocomplete: 'new-password',
14225             placeholder : this.placeholder || '',
14226             readonly : true
14227         };
14228         
14229         if (this.name) {
14230             input.name = this.name;
14231         }
14232         
14233         if (this.size) {
14234             input.cls += ' input-' + this.size;
14235         }
14236         
14237         if (this.disabled) {
14238             input.disabled = true;
14239         }
14240         
14241         var inputblock = {
14242             cls : '',
14243             cn : [
14244                 input
14245             ]
14246         };
14247         
14248         if(this.before){
14249             inputblock.cls += ' input-group';
14250             
14251             inputblock.cn.unshift({
14252                 tag :'span',
14253                 cls : 'input-group-addon',
14254                 html : this.before
14255             });
14256         }
14257         
14258         if(this.removable && !this.multiple){
14259             inputblock.cls += ' roo-removable';
14260             
14261             inputblock.cn.push({
14262                 tag: 'button',
14263                 html : 'x',
14264                 cls : 'roo-combo-removable-btn close'
14265             });
14266         }
14267
14268         if(this.hasFeedback && !this.allowBlank){
14269             
14270             inputblock.cls += ' has-feedback';
14271             
14272             inputblock.cn.push({
14273                 tag: 'span',
14274                 cls: 'glyphicon form-control-feedback'
14275             });
14276             
14277         }
14278         
14279         if (this.after) {
14280             
14281             inputblock.cls += (this.before) ? '' : ' input-group';
14282             
14283             inputblock.cn.push({
14284                 tag :'span',
14285                 cls : 'input-group-addon',
14286                 html : this.after
14287             });
14288         }
14289
14290         var box = {
14291             tag: 'div',
14292             cn: [
14293                 {
14294                     tag: 'input',
14295                     type : 'hidden',
14296                     cls: 'form-hidden-field'
14297                 },
14298                 inputblock
14299             ]
14300             
14301         };
14302         
14303         if(this.multiple){
14304             box = {
14305                 tag: 'div',
14306                 cn: [
14307                     {
14308                         tag: 'input',
14309                         type : 'hidden',
14310                         cls: 'form-hidden-field'
14311                     },
14312                     {
14313                         tag: 'ul',
14314                         cls: 'roo-select2-choices',
14315                         cn:[
14316                             {
14317                                 tag: 'li',
14318                                 cls: 'roo-select2-search-field',
14319                                 cn: [
14320
14321                                     inputblock
14322                                 ]
14323                             }
14324                         ]
14325                     }
14326                 ]
14327             }
14328         };
14329         
14330         var combobox = {
14331             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14332             cn: [
14333                 box
14334             ]
14335         };
14336         
14337         if(!this.multiple && this.showToggleBtn){
14338             
14339             var caret = {
14340                         tag: 'span',
14341                         cls: 'caret'
14342             };
14343             
14344             if (this.caret != false) {
14345                 caret = {
14346                      tag: 'i',
14347                      cls: 'fa fa-' + this.caret
14348                 };
14349                 
14350             }
14351             
14352             combobox.cn.push({
14353                 tag :'span',
14354                 cls : 'input-group-addon btn dropdown-toggle',
14355                 cn : [
14356                     caret,
14357                     {
14358                         tag: 'span',
14359                         cls: 'combobox-clear',
14360                         cn  : [
14361                             {
14362                                 tag : 'i',
14363                                 cls: 'icon-remove'
14364                             }
14365                         ]
14366                     }
14367                 ]
14368
14369             })
14370         }
14371         
14372         if(this.multiple){
14373             combobox.cls += ' roo-select2-container-multi';
14374         }
14375         
14376         var align = this.labelAlign || this.parentLabelAlign();
14377         
14378         cfg.cn = combobox;
14379         
14380         if(this.fieldLabel.length && this.labelWidth){
14381             
14382             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14383             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14384             
14385             cfg.cn = [
14386                 {
14387                    tag : 'i',
14388                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14389                    tooltip : 'This field is required'
14390                 },
14391                 {
14392                     tag: 'label',
14393                     cls : 'control-label ' + lw,
14394                     html : this.fieldLabel
14395
14396                 },
14397                 {
14398                     cls : cw, 
14399                     cn: [
14400                         combobox
14401                     ]
14402                 }
14403             ];
14404             
14405             if(this.indicatorpos == 'right'){
14406                 cfg.cn = [
14407                     {
14408                         tag: 'label',
14409                         cls : 'control-label ' + lw,
14410                         html : this.fieldLabel
14411
14412                     },
14413                     {
14414                        tag : 'i',
14415                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14416                        tooltip : 'This field is required'
14417                     },
14418                     {
14419                         cls : cw, 
14420                         cn: [
14421                             combobox
14422                         ]
14423                     }
14424                 ];
14425             }
14426         }
14427         
14428         var settings = this;
14429         
14430         ['xs','sm','md','lg'].map(function(size){
14431             if (settings[size]) {
14432                 cfg.cls += ' col-' + size + '-' + settings[size];
14433             }
14434         });
14435         
14436         return cfg;
14437     },
14438     
14439     initTouchView : function()
14440     {
14441         this.renderTouchView();
14442         
14443         this.touchViewEl.on('scroll', function(){
14444             this.el.dom.scrollTop = 0;
14445         }, this);
14446         
14447         this.originalValue = this.getValue();
14448         
14449         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14450         
14451         this.inputEl().on("click", this.showTouchView, this);
14452         if (this.triggerEl) {
14453             this.triggerEl.on("click", this.showTouchView, this);
14454         }
14455         
14456         
14457         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14458         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14459         
14460         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14461         
14462         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14463         this.store.on('load', this.onTouchViewLoad, this);
14464         this.store.on('loadexception', this.onTouchViewLoadException, this);
14465         
14466         if(this.hiddenName){
14467             
14468             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14469             
14470             this.hiddenField.dom.value =
14471                 this.hiddenValue !== undefined ? this.hiddenValue :
14472                 this.value !== undefined ? this.value : '';
14473         
14474             this.el.dom.removeAttribute('name');
14475             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14476         }
14477         
14478         if(this.multiple){
14479             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14480             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14481         }
14482         
14483         if(this.removable && !this.multiple){
14484             var close = this.closeTriggerEl();
14485             if(close){
14486                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14487                 close.on('click', this.removeBtnClick, this, close);
14488             }
14489         }
14490         /*
14491          * fix the bug in Safari iOS8
14492          */
14493         this.inputEl().on("focus", function(e){
14494             document.activeElement.blur();
14495         }, this);
14496         
14497         return;
14498         
14499         
14500     },
14501     
14502     renderTouchView : function()
14503     {
14504         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14505         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14506         
14507         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14508         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14509         
14510         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14511         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14512         this.touchViewBodyEl.setStyle('overflow', 'auto');
14513         
14514         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14515         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14516         
14517         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14518         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14519         
14520     },
14521     
14522     showTouchView : function()
14523     {
14524         if(this.disabled){
14525             return;
14526         }
14527         
14528         this.touchViewHeaderEl.hide();
14529
14530         if(this.modalTitle.length){
14531             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14532             this.touchViewHeaderEl.show();
14533         }
14534
14535         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14536         this.touchViewEl.show();
14537
14538         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14539         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14540                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14541
14542         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14543
14544         if(this.modalTitle.length){
14545             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14546         }
14547         
14548         this.touchViewBodyEl.setHeight(bodyHeight);
14549
14550         if(this.animate){
14551             var _this = this;
14552             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14553         }else{
14554             this.touchViewEl.addClass('in');
14555         }
14556
14557         this.doTouchViewQuery();
14558         
14559     },
14560     
14561     hideTouchView : function()
14562     {
14563         this.touchViewEl.removeClass('in');
14564
14565         if(this.animate){
14566             var _this = this;
14567             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14568         }else{
14569             this.touchViewEl.setStyle('display', 'none');
14570         }
14571         
14572     },
14573     
14574     setTouchViewValue : function()
14575     {
14576         if(this.multiple){
14577             this.clearItem();
14578         
14579             var _this = this;
14580
14581             Roo.each(this.tickItems, function(o){
14582                 this.addItem(o);
14583             }, this);
14584         }
14585         
14586         this.hideTouchView();
14587     },
14588     
14589     doTouchViewQuery : function()
14590     {
14591         var qe = {
14592             query: '',
14593             forceAll: true,
14594             combo: this,
14595             cancel:false
14596         };
14597         
14598         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14599             return false;
14600         }
14601         
14602         if(!this.alwaysQuery || this.mode == 'local'){
14603             this.onTouchViewLoad();
14604             return;
14605         }
14606         
14607         this.store.load();
14608     },
14609     
14610     onTouchViewBeforeLoad : function(combo,opts)
14611     {
14612         return;
14613     },
14614
14615     // private
14616     onTouchViewLoad : function()
14617     {
14618         if(this.store.getCount() < 1){
14619             this.onTouchViewEmptyResults();
14620             return;
14621         }
14622         
14623         this.clearTouchView();
14624         
14625         var rawValue = this.getRawValue();
14626         
14627         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14628         
14629         this.tickItems = [];
14630         
14631         this.store.data.each(function(d, rowIndex){
14632             var row = this.touchViewListGroup.createChild(template);
14633             
14634             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14635                 row.addClass(d.data.cls);
14636             }
14637             
14638             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14639                 var cfg = {
14640                     data : d.data,
14641                     html : d.data[this.displayField]
14642                 };
14643                 
14644                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14645                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14646                 }
14647             }
14648             row.removeClass('selected');
14649             if(!this.multiple && this.valueField &&
14650                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14651             {
14652                 // radio buttons..
14653                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14654                 row.addClass('selected');
14655             }
14656             
14657             if(this.multiple && this.valueField &&
14658                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14659             {
14660                 
14661                 // checkboxes...
14662                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14663                 this.tickItems.push(d.data);
14664             }
14665             
14666             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14667             
14668         }, this);
14669         
14670         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14671         
14672         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14673
14674         if(this.modalTitle.length){
14675             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14676         }
14677
14678         var listHeight = this.touchViewListGroup.getHeight();
14679         
14680         var _this = this;
14681         
14682         if(firstChecked && listHeight > bodyHeight){
14683             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14684         }
14685         
14686     },
14687     
14688     onTouchViewLoadException : function()
14689     {
14690         this.hideTouchView();
14691     },
14692     
14693     onTouchViewEmptyResults : function()
14694     {
14695         this.clearTouchView();
14696         
14697         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14698         
14699         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14700         
14701     },
14702     
14703     clearTouchView : function()
14704     {
14705         this.touchViewListGroup.dom.innerHTML = '';
14706     },
14707     
14708     onTouchViewClick : function(e, el, o)
14709     {
14710         e.preventDefault();
14711         
14712         var row = o.row;
14713         var rowIndex = o.rowIndex;
14714         
14715         var r = this.store.getAt(rowIndex);
14716         
14717         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14718             
14719             if(!this.multiple){
14720                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14721                     c.dom.removeAttribute('checked');
14722                 }, this);
14723
14724                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14725
14726                 this.setFromData(r.data);
14727
14728                 var close = this.closeTriggerEl();
14729
14730                 if(close){
14731                     close.show();
14732                 }
14733
14734                 this.hideTouchView();
14735
14736                 this.fireEvent('select', this, r, rowIndex);
14737
14738                 return;
14739             }
14740
14741             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14742                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14743                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14744                 return;
14745             }
14746
14747             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14748             this.addItem(r.data);
14749             this.tickItems.push(r.data);
14750         }
14751     },
14752     
14753     getAutoCreateNativeIOS : function()
14754     {
14755         var cfg = {
14756             cls: 'form-group' //input-group,
14757         };
14758         
14759         var combobox =  {
14760             tag: 'select',
14761             cls : 'roo-ios-select'
14762         };
14763         
14764         if (this.name) {
14765             combobox.name = this.name;
14766         }
14767         
14768         if (this.disabled) {
14769             combobox.disabled = true;
14770         }
14771         
14772         var settings = this;
14773         
14774         ['xs','sm','md','lg'].map(function(size){
14775             if (settings[size]) {
14776                 cfg.cls += ' col-' + size + '-' + settings[size];
14777             }
14778         });
14779         
14780         cfg.cn = combobox;
14781         
14782         return cfg;
14783         
14784     },
14785     
14786     initIOSView : function()
14787     {
14788         this.store.on('load', this.onIOSViewLoad, this);
14789         
14790         return;
14791     },
14792     
14793     onIOSViewLoad : function()
14794     {
14795         if(this.store.getCount() < 1){
14796             return;
14797         }
14798         
14799         this.clearIOSView();
14800         
14801         if(this.allowBlank) {
14802             
14803             var default_text = '-- SELECT --';
14804             
14805             var opt = this.inputEl().createChild({
14806                 tag: 'option',
14807                 value : 0,
14808                 html : default_text
14809             });
14810             
14811             var o = {};
14812             o[this.valueField] = 0;
14813             o[this.displayField] = default_text;
14814             
14815             this.ios_options.push({
14816                 data : o,
14817                 el : opt
14818             });
14819             
14820         }
14821         
14822         this.store.data.each(function(d, rowIndex){
14823             
14824             var html = '';
14825             
14826             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14827                 html = d.data[this.displayField];
14828             }
14829             
14830             var value = '';
14831             
14832             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14833                 value = d.data[this.valueField];
14834             }
14835             
14836             var option = {
14837                 tag: 'option',
14838                 value : value,
14839                 html : html
14840             };
14841             
14842             if(this.value == d.data[this.valueField]){
14843                 option['selected'] = true;
14844             }
14845             
14846             var opt = this.inputEl().createChild(option);
14847             
14848             this.ios_options.push({
14849                 data : d.data,
14850                 el : opt
14851             });
14852             
14853         }, this);
14854         
14855         this.inputEl().on('change', function(){
14856            this.fireEvent('select', this);
14857         }, this);
14858         
14859     },
14860     
14861     clearIOSView: function()
14862     {
14863         this.inputEl().dom.innerHTML = '';
14864         
14865         this.ios_options = [];
14866     },
14867     
14868     setIOSValue: function(v)
14869     {
14870         this.value = v;
14871         
14872         if(!this.ios_options){
14873             return;
14874         }
14875         
14876         Roo.each(this.ios_options, function(opts){
14877            
14878            opts.el.dom.removeAttribute('selected');
14879            
14880            if(opts.data[this.valueField] != v){
14881                return;
14882            }
14883            
14884            opts.el.dom.setAttribute('selected', true);
14885            
14886         }, this);
14887     }
14888
14889     /** 
14890     * @cfg {Boolean} grow 
14891     * @hide 
14892     */
14893     /** 
14894     * @cfg {Number} growMin 
14895     * @hide 
14896     */
14897     /** 
14898     * @cfg {Number} growMax 
14899     * @hide 
14900     */
14901     /**
14902      * @hide
14903      * @method autoSize
14904      */
14905 });
14906
14907 Roo.apply(Roo.bootstrap.ComboBox,  {
14908     
14909     header : {
14910         tag: 'div',
14911         cls: 'modal-header',
14912         cn: [
14913             {
14914                 tag: 'h4',
14915                 cls: 'modal-title'
14916             }
14917         ]
14918     },
14919     
14920     body : {
14921         tag: 'div',
14922         cls: 'modal-body',
14923         cn: [
14924             {
14925                 tag: 'ul',
14926                 cls: 'list-group'
14927             }
14928         ]
14929     },
14930     
14931     listItemRadio : {
14932         tag: 'li',
14933         cls: 'list-group-item',
14934         cn: [
14935             {
14936                 tag: 'span',
14937                 cls: 'roo-combobox-list-group-item-value'
14938             },
14939             {
14940                 tag: 'div',
14941                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14942                 cn: [
14943                     {
14944                         tag: 'input',
14945                         type: 'radio'
14946                     },
14947                     {
14948                         tag: 'label'
14949                     }
14950                 ]
14951             }
14952         ]
14953     },
14954     
14955     listItemCheckbox : {
14956         tag: 'li',
14957         cls: 'list-group-item',
14958         cn: [
14959             {
14960                 tag: 'span',
14961                 cls: 'roo-combobox-list-group-item-value'
14962             },
14963             {
14964                 tag: 'div',
14965                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14966                 cn: [
14967                     {
14968                         tag: 'input',
14969                         type: 'checkbox'
14970                     },
14971                     {
14972                         tag: 'label'
14973                     }
14974                 ]
14975             }
14976         ]
14977     },
14978     
14979     emptyResult : {
14980         tag: 'div',
14981         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14982     },
14983     
14984     footer : {
14985         tag: 'div',
14986         cls: 'modal-footer',
14987         cn: [
14988             {
14989                 tag: 'div',
14990                 cls: 'row',
14991                 cn: [
14992                     {
14993                         tag: 'div',
14994                         cls: 'col-xs-6 text-left',
14995                         cn: {
14996                             tag: 'button',
14997                             cls: 'btn btn-danger roo-touch-view-cancel',
14998                             html: 'Cancel'
14999                         }
15000                     },
15001                     {
15002                         tag: 'div',
15003                         cls: 'col-xs-6 text-right',
15004                         cn: {
15005                             tag: 'button',
15006                             cls: 'btn btn-success roo-touch-view-ok',
15007                             html: 'OK'
15008                         }
15009                     }
15010                 ]
15011             }
15012         ]
15013         
15014     }
15015 });
15016
15017 Roo.apply(Roo.bootstrap.ComboBox,  {
15018     
15019     touchViewTemplate : {
15020         tag: 'div',
15021         cls: 'modal fade roo-combobox-touch-view',
15022         cn: [
15023             {
15024                 tag: 'div',
15025                 cls: 'modal-dialog',
15026                 style : 'position:fixed', // we have to fix position....
15027                 cn: [
15028                     {
15029                         tag: 'div',
15030                         cls: 'modal-content',
15031                         cn: [
15032                             Roo.bootstrap.ComboBox.header,
15033                             Roo.bootstrap.ComboBox.body,
15034                             Roo.bootstrap.ComboBox.footer
15035                         ]
15036                     }
15037                 ]
15038             }
15039         ]
15040     }
15041 });/*
15042  * Based on:
15043  * Ext JS Library 1.1.1
15044  * Copyright(c) 2006-2007, Ext JS, LLC.
15045  *
15046  * Originally Released Under LGPL - original licence link has changed is not relivant.
15047  *
15048  * Fork - LGPL
15049  * <script type="text/javascript">
15050  */
15051
15052 /**
15053  * @class Roo.View
15054  * @extends Roo.util.Observable
15055  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15056  * This class also supports single and multi selection modes. <br>
15057  * Create a data model bound view:
15058  <pre><code>
15059  var store = new Roo.data.Store(...);
15060
15061  var view = new Roo.View({
15062     el : "my-element",
15063     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15064  
15065     singleSelect: true,
15066     selectedClass: "ydataview-selected",
15067     store: store
15068  });
15069
15070  // listen for node click?
15071  view.on("click", function(vw, index, node, e){
15072  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15073  });
15074
15075  // load XML data
15076  dataModel.load("foobar.xml");
15077  </code></pre>
15078  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15079  * <br><br>
15080  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15081  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15082  * 
15083  * Note: old style constructor is still suported (container, template, config)
15084  * 
15085  * @constructor
15086  * Create a new View
15087  * @param {Object} config The config object
15088  * 
15089  */
15090 Roo.View = function(config, depreciated_tpl, depreciated_config){
15091     
15092     this.parent = false;
15093     
15094     if (typeof(depreciated_tpl) == 'undefined') {
15095         // new way.. - universal constructor.
15096         Roo.apply(this, config);
15097         this.el  = Roo.get(this.el);
15098     } else {
15099         // old format..
15100         this.el  = Roo.get(config);
15101         this.tpl = depreciated_tpl;
15102         Roo.apply(this, depreciated_config);
15103     }
15104     this.wrapEl  = this.el.wrap().wrap();
15105     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15106     
15107     
15108     if(typeof(this.tpl) == "string"){
15109         this.tpl = new Roo.Template(this.tpl);
15110     } else {
15111         // support xtype ctors..
15112         this.tpl = new Roo.factory(this.tpl, Roo);
15113     }
15114     
15115     
15116     this.tpl.compile();
15117     
15118     /** @private */
15119     this.addEvents({
15120         /**
15121          * @event beforeclick
15122          * Fires before a click is processed. Returns false to cancel the default action.
15123          * @param {Roo.View} this
15124          * @param {Number} index The index of the target node
15125          * @param {HTMLElement} node The target node
15126          * @param {Roo.EventObject} e The raw event object
15127          */
15128             "beforeclick" : true,
15129         /**
15130          * @event click
15131          * Fires when a template node is clicked.
15132          * @param {Roo.View} this
15133          * @param {Number} index The index of the target node
15134          * @param {HTMLElement} node The target node
15135          * @param {Roo.EventObject} e The raw event object
15136          */
15137             "click" : true,
15138         /**
15139          * @event dblclick
15140          * Fires when a template node is double clicked.
15141          * @param {Roo.View} this
15142          * @param {Number} index The index of the target node
15143          * @param {HTMLElement} node The target node
15144          * @param {Roo.EventObject} e The raw event object
15145          */
15146             "dblclick" : true,
15147         /**
15148          * @event contextmenu
15149          * Fires when a template node is right clicked.
15150          * @param {Roo.View} this
15151          * @param {Number} index The index of the target node
15152          * @param {HTMLElement} node The target node
15153          * @param {Roo.EventObject} e The raw event object
15154          */
15155             "contextmenu" : true,
15156         /**
15157          * @event selectionchange
15158          * Fires when the selected nodes change.
15159          * @param {Roo.View} this
15160          * @param {Array} selections Array of the selected nodes
15161          */
15162             "selectionchange" : true,
15163     
15164         /**
15165          * @event beforeselect
15166          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15167          * @param {Roo.View} this
15168          * @param {HTMLElement} node The node to be selected
15169          * @param {Array} selections Array of currently selected nodes
15170          */
15171             "beforeselect" : true,
15172         /**
15173          * @event preparedata
15174          * Fires on every row to render, to allow you to change the data.
15175          * @param {Roo.View} this
15176          * @param {Object} data to be rendered (change this)
15177          */
15178           "preparedata" : true
15179           
15180           
15181         });
15182
15183
15184
15185     this.el.on({
15186         "click": this.onClick,
15187         "dblclick": this.onDblClick,
15188         "contextmenu": this.onContextMenu,
15189         scope:this
15190     });
15191
15192     this.selections = [];
15193     this.nodes = [];
15194     this.cmp = new Roo.CompositeElementLite([]);
15195     if(this.store){
15196         this.store = Roo.factory(this.store, Roo.data);
15197         this.setStore(this.store, true);
15198     }
15199     
15200     if ( this.footer && this.footer.xtype) {
15201            
15202          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15203         
15204         this.footer.dataSource = this.store;
15205         this.footer.container = fctr;
15206         this.footer = Roo.factory(this.footer, Roo);
15207         fctr.insertFirst(this.el);
15208         
15209         // this is a bit insane - as the paging toolbar seems to detach the el..
15210 //        dom.parentNode.parentNode.parentNode
15211          // they get detached?
15212     }
15213     
15214     
15215     Roo.View.superclass.constructor.call(this);
15216     
15217     
15218 };
15219
15220 Roo.extend(Roo.View, Roo.util.Observable, {
15221     
15222      /**
15223      * @cfg {Roo.data.Store} store Data store to load data from.
15224      */
15225     store : false,
15226     
15227     /**
15228      * @cfg {String|Roo.Element} el The container element.
15229      */
15230     el : '',
15231     
15232     /**
15233      * @cfg {String|Roo.Template} tpl The template used by this View 
15234      */
15235     tpl : false,
15236     /**
15237      * @cfg {String} dataName the named area of the template to use as the data area
15238      *                          Works with domtemplates roo-name="name"
15239      */
15240     dataName: false,
15241     /**
15242      * @cfg {String} selectedClass The css class to add to selected nodes
15243      */
15244     selectedClass : "x-view-selected",
15245      /**
15246      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15247      */
15248     emptyText : "",
15249     
15250     /**
15251      * @cfg {String} text to display on mask (default Loading)
15252      */
15253     mask : false,
15254     /**
15255      * @cfg {Boolean} multiSelect Allow multiple selection
15256      */
15257     multiSelect : false,
15258     /**
15259      * @cfg {Boolean} singleSelect Allow single selection
15260      */
15261     singleSelect:  false,
15262     
15263     /**
15264      * @cfg {Boolean} toggleSelect - selecting 
15265      */
15266     toggleSelect : false,
15267     
15268     /**
15269      * @cfg {Boolean} tickable - selecting 
15270      */
15271     tickable : false,
15272     
15273     /**
15274      * Returns the element this view is bound to.
15275      * @return {Roo.Element}
15276      */
15277     getEl : function(){
15278         return this.wrapEl;
15279     },
15280     
15281     
15282
15283     /**
15284      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15285      */
15286     refresh : function(){
15287         //Roo.log('refresh');
15288         var t = this.tpl;
15289         
15290         // if we are using something like 'domtemplate', then
15291         // the what gets used is:
15292         // t.applySubtemplate(NAME, data, wrapping data..)
15293         // the outer template then get' applied with
15294         //     the store 'extra data'
15295         // and the body get's added to the
15296         //      roo-name="data" node?
15297         //      <span class='roo-tpl-{name}'></span> ?????
15298         
15299         
15300         
15301         this.clearSelections();
15302         this.el.update("");
15303         var html = [];
15304         var records = this.store.getRange();
15305         if(records.length < 1) {
15306             
15307             // is this valid??  = should it render a template??
15308             
15309             this.el.update(this.emptyText);
15310             return;
15311         }
15312         var el = this.el;
15313         if (this.dataName) {
15314             this.el.update(t.apply(this.store.meta)); //????
15315             el = this.el.child('.roo-tpl-' + this.dataName);
15316         }
15317         
15318         for(var i = 0, len = records.length; i < len; i++){
15319             var data = this.prepareData(records[i].data, i, records[i]);
15320             this.fireEvent("preparedata", this, data, i, records[i]);
15321             
15322             var d = Roo.apply({}, data);
15323             
15324             if(this.tickable){
15325                 Roo.apply(d, {'roo-id' : Roo.id()});
15326                 
15327                 var _this = this;
15328             
15329                 Roo.each(this.parent.item, function(item){
15330                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15331                         return;
15332                     }
15333                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15334                 });
15335             }
15336             
15337             html[html.length] = Roo.util.Format.trim(
15338                 this.dataName ?
15339                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15340                     t.apply(d)
15341             );
15342         }
15343         
15344         
15345         
15346         el.update(html.join(""));
15347         this.nodes = el.dom.childNodes;
15348         this.updateIndexes(0);
15349     },
15350     
15351
15352     /**
15353      * Function to override to reformat the data that is sent to
15354      * the template for each node.
15355      * DEPRICATED - use the preparedata event handler.
15356      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15357      * a JSON object for an UpdateManager bound view).
15358      */
15359     prepareData : function(data, index, record)
15360     {
15361         this.fireEvent("preparedata", this, data, index, record);
15362         return data;
15363     },
15364
15365     onUpdate : function(ds, record){
15366         // Roo.log('on update');   
15367         this.clearSelections();
15368         var index = this.store.indexOf(record);
15369         var n = this.nodes[index];
15370         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15371         n.parentNode.removeChild(n);
15372         this.updateIndexes(index, index);
15373     },
15374
15375     
15376     
15377 // --------- FIXME     
15378     onAdd : function(ds, records, index)
15379     {
15380         //Roo.log(['on Add', ds, records, index] );        
15381         this.clearSelections();
15382         if(this.nodes.length == 0){
15383             this.refresh();
15384             return;
15385         }
15386         var n = this.nodes[index];
15387         for(var i = 0, len = records.length; i < len; i++){
15388             var d = this.prepareData(records[i].data, i, records[i]);
15389             if(n){
15390                 this.tpl.insertBefore(n, d);
15391             }else{
15392                 
15393                 this.tpl.append(this.el, d);
15394             }
15395         }
15396         this.updateIndexes(index);
15397     },
15398
15399     onRemove : function(ds, record, index){
15400        // Roo.log('onRemove');
15401         this.clearSelections();
15402         var el = this.dataName  ?
15403             this.el.child('.roo-tpl-' + this.dataName) :
15404             this.el; 
15405         
15406         el.dom.removeChild(this.nodes[index]);
15407         this.updateIndexes(index);
15408     },
15409
15410     /**
15411      * Refresh an individual node.
15412      * @param {Number} index
15413      */
15414     refreshNode : function(index){
15415         this.onUpdate(this.store, this.store.getAt(index));
15416     },
15417
15418     updateIndexes : function(startIndex, endIndex){
15419         var ns = this.nodes;
15420         startIndex = startIndex || 0;
15421         endIndex = endIndex || ns.length - 1;
15422         for(var i = startIndex; i <= endIndex; i++){
15423             ns[i].nodeIndex = i;
15424         }
15425     },
15426
15427     /**
15428      * Changes the data store this view uses and refresh the view.
15429      * @param {Store} store
15430      */
15431     setStore : function(store, initial){
15432         if(!initial && this.store){
15433             this.store.un("datachanged", this.refresh);
15434             this.store.un("add", this.onAdd);
15435             this.store.un("remove", this.onRemove);
15436             this.store.un("update", this.onUpdate);
15437             this.store.un("clear", this.refresh);
15438             this.store.un("beforeload", this.onBeforeLoad);
15439             this.store.un("load", this.onLoad);
15440             this.store.un("loadexception", this.onLoad);
15441         }
15442         if(store){
15443           
15444             store.on("datachanged", this.refresh, this);
15445             store.on("add", this.onAdd, this);
15446             store.on("remove", this.onRemove, this);
15447             store.on("update", this.onUpdate, this);
15448             store.on("clear", this.refresh, this);
15449             store.on("beforeload", this.onBeforeLoad, this);
15450             store.on("load", this.onLoad, this);
15451             store.on("loadexception", this.onLoad, this);
15452         }
15453         
15454         if(store){
15455             this.refresh();
15456         }
15457     },
15458     /**
15459      * onbeforeLoad - masks the loading area.
15460      *
15461      */
15462     onBeforeLoad : function(store,opts)
15463     {
15464          //Roo.log('onBeforeLoad');   
15465         if (!opts.add) {
15466             this.el.update("");
15467         }
15468         this.el.mask(this.mask ? this.mask : "Loading" ); 
15469     },
15470     onLoad : function ()
15471     {
15472         this.el.unmask();
15473     },
15474     
15475
15476     /**
15477      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15478      * @param {HTMLElement} node
15479      * @return {HTMLElement} The template node
15480      */
15481     findItemFromChild : function(node){
15482         var el = this.dataName  ?
15483             this.el.child('.roo-tpl-' + this.dataName,true) :
15484             this.el.dom; 
15485         
15486         if(!node || node.parentNode == el){
15487                     return node;
15488             }
15489             var p = node.parentNode;
15490             while(p && p != el){
15491             if(p.parentNode == el){
15492                 return p;
15493             }
15494             p = p.parentNode;
15495         }
15496             return null;
15497     },
15498
15499     /** @ignore */
15500     onClick : function(e){
15501         var item = this.findItemFromChild(e.getTarget());
15502         if(item){
15503             var index = this.indexOf(item);
15504             if(this.onItemClick(item, index, e) !== false){
15505                 this.fireEvent("click", this, index, item, e);
15506             }
15507         }else{
15508             this.clearSelections();
15509         }
15510     },
15511
15512     /** @ignore */
15513     onContextMenu : function(e){
15514         var item = this.findItemFromChild(e.getTarget());
15515         if(item){
15516             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15517         }
15518     },
15519
15520     /** @ignore */
15521     onDblClick : function(e){
15522         var item = this.findItemFromChild(e.getTarget());
15523         if(item){
15524             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15525         }
15526     },
15527
15528     onItemClick : function(item, index, e)
15529     {
15530         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15531             return false;
15532         }
15533         if (this.toggleSelect) {
15534             var m = this.isSelected(item) ? 'unselect' : 'select';
15535             //Roo.log(m);
15536             var _t = this;
15537             _t[m](item, true, false);
15538             return true;
15539         }
15540         if(this.multiSelect || this.singleSelect){
15541             if(this.multiSelect && e.shiftKey && this.lastSelection){
15542                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15543             }else{
15544                 this.select(item, this.multiSelect && e.ctrlKey);
15545                 this.lastSelection = item;
15546             }
15547             
15548             if(!this.tickable){
15549                 e.preventDefault();
15550             }
15551             
15552         }
15553         return true;
15554     },
15555
15556     /**
15557      * Get the number of selected nodes.
15558      * @return {Number}
15559      */
15560     getSelectionCount : function(){
15561         return this.selections.length;
15562     },
15563
15564     /**
15565      * Get the currently selected nodes.
15566      * @return {Array} An array of HTMLElements
15567      */
15568     getSelectedNodes : function(){
15569         return this.selections;
15570     },
15571
15572     /**
15573      * Get the indexes of the selected nodes.
15574      * @return {Array}
15575      */
15576     getSelectedIndexes : function(){
15577         var indexes = [], s = this.selections;
15578         for(var i = 0, len = s.length; i < len; i++){
15579             indexes.push(s[i].nodeIndex);
15580         }
15581         return indexes;
15582     },
15583
15584     /**
15585      * Clear all selections
15586      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15587      */
15588     clearSelections : function(suppressEvent){
15589         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15590             this.cmp.elements = this.selections;
15591             this.cmp.removeClass(this.selectedClass);
15592             this.selections = [];
15593             if(!suppressEvent){
15594                 this.fireEvent("selectionchange", this, this.selections);
15595             }
15596         }
15597     },
15598
15599     /**
15600      * Returns true if the passed node is selected
15601      * @param {HTMLElement/Number} node The node or node index
15602      * @return {Boolean}
15603      */
15604     isSelected : function(node){
15605         var s = this.selections;
15606         if(s.length < 1){
15607             return false;
15608         }
15609         node = this.getNode(node);
15610         return s.indexOf(node) !== -1;
15611     },
15612
15613     /**
15614      * Selects nodes.
15615      * @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
15616      * @param {Boolean} keepExisting (optional) true to keep existing selections
15617      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15618      */
15619     select : function(nodeInfo, keepExisting, suppressEvent){
15620         if(nodeInfo instanceof Array){
15621             if(!keepExisting){
15622                 this.clearSelections(true);
15623             }
15624             for(var i = 0, len = nodeInfo.length; i < len; i++){
15625                 this.select(nodeInfo[i], true, true);
15626             }
15627             return;
15628         } 
15629         var node = this.getNode(nodeInfo);
15630         if(!node || this.isSelected(node)){
15631             return; // already selected.
15632         }
15633         if(!keepExisting){
15634             this.clearSelections(true);
15635         }
15636         
15637         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15638             Roo.fly(node).addClass(this.selectedClass);
15639             this.selections.push(node);
15640             if(!suppressEvent){
15641                 this.fireEvent("selectionchange", this, this.selections);
15642             }
15643         }
15644         
15645         
15646     },
15647       /**
15648      * Unselects nodes.
15649      * @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
15650      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15651      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15652      */
15653     unselect : function(nodeInfo, keepExisting, suppressEvent)
15654     {
15655         if(nodeInfo instanceof Array){
15656             Roo.each(this.selections, function(s) {
15657                 this.unselect(s, nodeInfo);
15658             }, this);
15659             return;
15660         }
15661         var node = this.getNode(nodeInfo);
15662         if(!node || !this.isSelected(node)){
15663             //Roo.log("not selected");
15664             return; // not selected.
15665         }
15666         // fireevent???
15667         var ns = [];
15668         Roo.each(this.selections, function(s) {
15669             if (s == node ) {
15670                 Roo.fly(node).removeClass(this.selectedClass);
15671
15672                 return;
15673             }
15674             ns.push(s);
15675         },this);
15676         
15677         this.selections= ns;
15678         this.fireEvent("selectionchange", this, this.selections);
15679     },
15680
15681     /**
15682      * Gets a template node.
15683      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15684      * @return {HTMLElement} The node or null if it wasn't found
15685      */
15686     getNode : function(nodeInfo){
15687         if(typeof nodeInfo == "string"){
15688             return document.getElementById(nodeInfo);
15689         }else if(typeof nodeInfo == "number"){
15690             return this.nodes[nodeInfo];
15691         }
15692         return nodeInfo;
15693     },
15694
15695     /**
15696      * Gets a range template nodes.
15697      * @param {Number} startIndex
15698      * @param {Number} endIndex
15699      * @return {Array} An array of nodes
15700      */
15701     getNodes : function(start, end){
15702         var ns = this.nodes;
15703         start = start || 0;
15704         end = typeof end == "undefined" ? ns.length - 1 : end;
15705         var nodes = [];
15706         if(start <= end){
15707             for(var i = start; i <= end; i++){
15708                 nodes.push(ns[i]);
15709             }
15710         } else{
15711             for(var i = start; i >= end; i--){
15712                 nodes.push(ns[i]);
15713             }
15714         }
15715         return nodes;
15716     },
15717
15718     /**
15719      * Finds the index of the passed node
15720      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15721      * @return {Number} The index of the node or -1
15722      */
15723     indexOf : function(node){
15724         node = this.getNode(node);
15725         if(typeof node.nodeIndex == "number"){
15726             return node.nodeIndex;
15727         }
15728         var ns = this.nodes;
15729         for(var i = 0, len = ns.length; i < len; i++){
15730             if(ns[i] == node){
15731                 return i;
15732             }
15733         }
15734         return -1;
15735     }
15736 });
15737 /*
15738  * - LGPL
15739  *
15740  * based on jquery fullcalendar
15741  * 
15742  */
15743
15744 Roo.bootstrap = Roo.bootstrap || {};
15745 /**
15746  * @class Roo.bootstrap.Calendar
15747  * @extends Roo.bootstrap.Component
15748  * Bootstrap Calendar class
15749  * @cfg {Boolean} loadMask (true|false) default false
15750  * @cfg {Object} header generate the user specific header of the calendar, default false
15751
15752  * @constructor
15753  * Create a new Container
15754  * @param {Object} config The config object
15755  */
15756
15757
15758
15759 Roo.bootstrap.Calendar = function(config){
15760     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15761      this.addEvents({
15762         /**
15763              * @event select
15764              * Fires when a date is selected
15765              * @param {DatePicker} this
15766              * @param {Date} date The selected date
15767              */
15768         'select': true,
15769         /**
15770              * @event monthchange
15771              * Fires when the displayed month changes 
15772              * @param {DatePicker} this
15773              * @param {Date} date The selected month
15774              */
15775         'monthchange': true,
15776         /**
15777              * @event evententer
15778              * Fires when mouse over an event
15779              * @param {Calendar} this
15780              * @param {event} Event
15781              */
15782         'evententer': true,
15783         /**
15784              * @event eventleave
15785              * Fires when the mouse leaves an
15786              * @param {Calendar} this
15787              * @param {event}
15788              */
15789         'eventleave': true,
15790         /**
15791              * @event eventclick
15792              * Fires when the mouse click an
15793              * @param {Calendar} this
15794              * @param {event}
15795              */
15796         'eventclick': true
15797         
15798     });
15799
15800 };
15801
15802 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15803     
15804      /**
15805      * @cfg {Number} startDay
15806      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15807      */
15808     startDay : 0,
15809     
15810     loadMask : false,
15811     
15812     header : false,
15813       
15814     getAutoCreate : function(){
15815         
15816         
15817         var fc_button = function(name, corner, style, content ) {
15818             return Roo.apply({},{
15819                 tag : 'span',
15820                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15821                          (corner.length ?
15822                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15823                             ''
15824                         ),
15825                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15826                 unselectable: 'on'
15827             });
15828         };
15829         
15830         var header = {};
15831         
15832         if(!this.header){
15833             header = {
15834                 tag : 'table',
15835                 cls : 'fc-header',
15836                 style : 'width:100%',
15837                 cn : [
15838                     {
15839                         tag: 'tr',
15840                         cn : [
15841                             {
15842                                 tag : 'td',
15843                                 cls : 'fc-header-left',
15844                                 cn : [
15845                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15846                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15847                                     { tag: 'span', cls: 'fc-header-space' },
15848                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15849
15850
15851                                 ]
15852                             },
15853
15854                             {
15855                                 tag : 'td',
15856                                 cls : 'fc-header-center',
15857                                 cn : [
15858                                     {
15859                                         tag: 'span',
15860                                         cls: 'fc-header-title',
15861                                         cn : {
15862                                             tag: 'H2',
15863                                             html : 'month / year'
15864                                         }
15865                                     }
15866
15867                                 ]
15868                             },
15869                             {
15870                                 tag : 'td',
15871                                 cls : 'fc-header-right',
15872                                 cn : [
15873                               /*      fc_button('month', 'left', '', 'month' ),
15874                                     fc_button('week', '', '', 'week' ),
15875                                     fc_button('day', 'right', '', 'day' )
15876                                 */    
15877
15878                                 ]
15879                             }
15880
15881                         ]
15882                     }
15883                 ]
15884             };
15885         }
15886         
15887         header = this.header;
15888         
15889        
15890         var cal_heads = function() {
15891             var ret = [];
15892             // fixme - handle this.
15893             
15894             for (var i =0; i < Date.dayNames.length; i++) {
15895                 var d = Date.dayNames[i];
15896                 ret.push({
15897                     tag: 'th',
15898                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15899                     html : d.substring(0,3)
15900                 });
15901                 
15902             }
15903             ret[0].cls += ' fc-first';
15904             ret[6].cls += ' fc-last';
15905             return ret;
15906         };
15907         var cal_cell = function(n) {
15908             return  {
15909                 tag: 'td',
15910                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15911                 cn : [
15912                     {
15913                         cn : [
15914                             {
15915                                 cls: 'fc-day-number',
15916                                 html: 'D'
15917                             },
15918                             {
15919                                 cls: 'fc-day-content',
15920                              
15921                                 cn : [
15922                                      {
15923                                         style: 'position: relative;' // height: 17px;
15924                                     }
15925                                 ]
15926                             }
15927                             
15928                             
15929                         ]
15930                     }
15931                 ]
15932                 
15933             }
15934         };
15935         var cal_rows = function() {
15936             
15937             var ret = [];
15938             for (var r = 0; r < 6; r++) {
15939                 var row= {
15940                     tag : 'tr',
15941                     cls : 'fc-week',
15942                     cn : []
15943                 };
15944                 
15945                 for (var i =0; i < Date.dayNames.length; i++) {
15946                     var d = Date.dayNames[i];
15947                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15948
15949                 }
15950                 row.cn[0].cls+=' fc-first';
15951                 row.cn[0].cn[0].style = 'min-height:90px';
15952                 row.cn[6].cls+=' fc-last';
15953                 ret.push(row);
15954                 
15955             }
15956             ret[0].cls += ' fc-first';
15957             ret[4].cls += ' fc-prev-last';
15958             ret[5].cls += ' fc-last';
15959             return ret;
15960             
15961         };
15962         
15963         var cal_table = {
15964             tag: 'table',
15965             cls: 'fc-border-separate',
15966             style : 'width:100%',
15967             cellspacing  : 0,
15968             cn : [
15969                 { 
15970                     tag: 'thead',
15971                     cn : [
15972                         { 
15973                             tag: 'tr',
15974                             cls : 'fc-first fc-last',
15975                             cn : cal_heads()
15976                         }
15977                     ]
15978                 },
15979                 { 
15980                     tag: 'tbody',
15981                     cn : cal_rows()
15982                 }
15983                   
15984             ]
15985         };
15986          
15987          var cfg = {
15988             cls : 'fc fc-ltr',
15989             cn : [
15990                 header,
15991                 {
15992                     cls : 'fc-content',
15993                     style : "position: relative;",
15994                     cn : [
15995                         {
15996                             cls : 'fc-view fc-view-month fc-grid',
15997                             style : 'position: relative',
15998                             unselectable : 'on',
15999                             cn : [
16000                                 {
16001                                     cls : 'fc-event-container',
16002                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16003                                 },
16004                                 cal_table
16005                             ]
16006                         }
16007                     ]
16008     
16009                 }
16010            ] 
16011             
16012         };
16013         
16014          
16015         
16016         return cfg;
16017     },
16018     
16019     
16020     initEvents : function()
16021     {
16022         if(!this.store){
16023             throw "can not find store for calendar";
16024         }
16025         
16026         var mark = {
16027             tag: "div",
16028             cls:"x-dlg-mask",
16029             style: "text-align:center",
16030             cn: [
16031                 {
16032                     tag: "div",
16033                     style: "background-color:white;width:50%;margin:250 auto",
16034                     cn: [
16035                         {
16036                             tag: "img",
16037                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16038                         },
16039                         {
16040                             tag: "span",
16041                             html: "Loading"
16042                         }
16043                         
16044                     ]
16045                 }
16046             ]
16047         };
16048         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16049         
16050         var size = this.el.select('.fc-content', true).first().getSize();
16051         this.maskEl.setSize(size.width, size.height);
16052         this.maskEl.enableDisplayMode("block");
16053         if(!this.loadMask){
16054             this.maskEl.hide();
16055         }
16056         
16057         this.store = Roo.factory(this.store, Roo.data);
16058         this.store.on('load', this.onLoad, this);
16059         this.store.on('beforeload', this.onBeforeLoad, this);
16060         
16061         this.resize();
16062         
16063         this.cells = this.el.select('.fc-day',true);
16064         //Roo.log(this.cells);
16065         this.textNodes = this.el.query('.fc-day-number');
16066         this.cells.addClassOnOver('fc-state-hover');
16067         
16068         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16069         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16070         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16071         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16072         
16073         this.on('monthchange', this.onMonthChange, this);
16074         
16075         this.update(new Date().clearTime());
16076     },
16077     
16078     resize : function() {
16079         var sz  = this.el.getSize();
16080         
16081         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16082         this.el.select('.fc-day-content div',true).setHeight(34);
16083     },
16084     
16085     
16086     // private
16087     showPrevMonth : function(e){
16088         this.update(this.activeDate.add("mo", -1));
16089     },
16090     showToday : function(e){
16091         this.update(new Date().clearTime());
16092     },
16093     // private
16094     showNextMonth : function(e){
16095         this.update(this.activeDate.add("mo", 1));
16096     },
16097
16098     // private
16099     showPrevYear : function(){
16100         this.update(this.activeDate.add("y", -1));
16101     },
16102
16103     // private
16104     showNextYear : function(){
16105         this.update(this.activeDate.add("y", 1));
16106     },
16107
16108     
16109    // private
16110     update : function(date)
16111     {
16112         var vd = this.activeDate;
16113         this.activeDate = date;
16114 //        if(vd && this.el){
16115 //            var t = date.getTime();
16116 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16117 //                Roo.log('using add remove');
16118 //                
16119 //                this.fireEvent('monthchange', this, date);
16120 //                
16121 //                this.cells.removeClass("fc-state-highlight");
16122 //                this.cells.each(function(c){
16123 //                   if(c.dateValue == t){
16124 //                       c.addClass("fc-state-highlight");
16125 //                       setTimeout(function(){
16126 //                            try{c.dom.firstChild.focus();}catch(e){}
16127 //                       }, 50);
16128 //                       return false;
16129 //                   }
16130 //                   return true;
16131 //                });
16132 //                return;
16133 //            }
16134 //        }
16135         
16136         var days = date.getDaysInMonth();
16137         
16138         var firstOfMonth = date.getFirstDateOfMonth();
16139         var startingPos = firstOfMonth.getDay()-this.startDay;
16140         
16141         if(startingPos < this.startDay){
16142             startingPos += 7;
16143         }
16144         
16145         var pm = date.add(Date.MONTH, -1);
16146         var prevStart = pm.getDaysInMonth()-startingPos;
16147 //        
16148         this.cells = this.el.select('.fc-day',true);
16149         this.textNodes = this.el.query('.fc-day-number');
16150         this.cells.addClassOnOver('fc-state-hover');
16151         
16152         var cells = this.cells.elements;
16153         var textEls = this.textNodes;
16154         
16155         Roo.each(cells, function(cell){
16156             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16157         });
16158         
16159         days += startingPos;
16160
16161         // convert everything to numbers so it's fast
16162         var day = 86400000;
16163         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16164         //Roo.log(d);
16165         //Roo.log(pm);
16166         //Roo.log(prevStart);
16167         
16168         var today = new Date().clearTime().getTime();
16169         var sel = date.clearTime().getTime();
16170         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16171         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16172         var ddMatch = this.disabledDatesRE;
16173         var ddText = this.disabledDatesText;
16174         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16175         var ddaysText = this.disabledDaysText;
16176         var format = this.format;
16177         
16178         var setCellClass = function(cal, cell){
16179             cell.row = 0;
16180             cell.events = [];
16181             cell.more = [];
16182             //Roo.log('set Cell Class');
16183             cell.title = "";
16184             var t = d.getTime();
16185             
16186             //Roo.log(d);
16187             
16188             cell.dateValue = t;
16189             if(t == today){
16190                 cell.className += " fc-today";
16191                 cell.className += " fc-state-highlight";
16192                 cell.title = cal.todayText;
16193             }
16194             if(t == sel){
16195                 // disable highlight in other month..
16196                 //cell.className += " fc-state-highlight";
16197                 
16198             }
16199             // disabling
16200             if(t < min) {
16201                 cell.className = " fc-state-disabled";
16202                 cell.title = cal.minText;
16203                 return;
16204             }
16205             if(t > max) {
16206                 cell.className = " fc-state-disabled";
16207                 cell.title = cal.maxText;
16208                 return;
16209             }
16210             if(ddays){
16211                 if(ddays.indexOf(d.getDay()) != -1){
16212                     cell.title = ddaysText;
16213                     cell.className = " fc-state-disabled";
16214                 }
16215             }
16216             if(ddMatch && format){
16217                 var fvalue = d.dateFormat(format);
16218                 if(ddMatch.test(fvalue)){
16219                     cell.title = ddText.replace("%0", fvalue);
16220                     cell.className = " fc-state-disabled";
16221                 }
16222             }
16223             
16224             if (!cell.initialClassName) {
16225                 cell.initialClassName = cell.dom.className;
16226             }
16227             
16228             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16229         };
16230
16231         var i = 0;
16232         
16233         for(; i < startingPos; i++) {
16234             textEls[i].innerHTML = (++prevStart);
16235             d.setDate(d.getDate()+1);
16236             
16237             cells[i].className = "fc-past fc-other-month";
16238             setCellClass(this, cells[i]);
16239         }
16240         
16241         var intDay = 0;
16242         
16243         for(; i < days; i++){
16244             intDay = i - startingPos + 1;
16245             textEls[i].innerHTML = (intDay);
16246             d.setDate(d.getDate()+1);
16247             
16248             cells[i].className = ''; // "x-date-active";
16249             setCellClass(this, cells[i]);
16250         }
16251         var extraDays = 0;
16252         
16253         for(; i < 42; i++) {
16254             textEls[i].innerHTML = (++extraDays);
16255             d.setDate(d.getDate()+1);
16256             
16257             cells[i].className = "fc-future fc-other-month";
16258             setCellClass(this, cells[i]);
16259         }
16260         
16261         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16262         
16263         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16264         
16265         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16266         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16267         
16268         if(totalRows != 6){
16269             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16270             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16271         }
16272         
16273         this.fireEvent('monthchange', this, date);
16274         
16275         
16276         /*
16277         if(!this.internalRender){
16278             var main = this.el.dom.firstChild;
16279             var w = main.offsetWidth;
16280             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16281             Roo.fly(main).setWidth(w);
16282             this.internalRender = true;
16283             // opera does not respect the auto grow header center column
16284             // then, after it gets a width opera refuses to recalculate
16285             // without a second pass
16286             if(Roo.isOpera && !this.secondPass){
16287                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16288                 this.secondPass = true;
16289                 this.update.defer(10, this, [date]);
16290             }
16291         }
16292         */
16293         
16294     },
16295     
16296     findCell : function(dt) {
16297         dt = dt.clearTime().getTime();
16298         var ret = false;
16299         this.cells.each(function(c){
16300             //Roo.log("check " +c.dateValue + '?=' + dt);
16301             if(c.dateValue == dt){
16302                 ret = c;
16303                 return false;
16304             }
16305             return true;
16306         });
16307         
16308         return ret;
16309     },
16310     
16311     findCells : function(ev) {
16312         var s = ev.start.clone().clearTime().getTime();
16313        // Roo.log(s);
16314         var e= ev.end.clone().clearTime().getTime();
16315        // Roo.log(e);
16316         var ret = [];
16317         this.cells.each(function(c){
16318              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16319             
16320             if(c.dateValue > e){
16321                 return ;
16322             }
16323             if(c.dateValue < s){
16324                 return ;
16325             }
16326             ret.push(c);
16327         });
16328         
16329         return ret;    
16330     },
16331     
16332 //    findBestRow: function(cells)
16333 //    {
16334 //        var ret = 0;
16335 //        
16336 //        for (var i =0 ; i < cells.length;i++) {
16337 //            ret  = Math.max(cells[i].rows || 0,ret);
16338 //        }
16339 //        return ret;
16340 //        
16341 //    },
16342     
16343     
16344     addItem : function(ev)
16345     {
16346         // look for vertical location slot in
16347         var cells = this.findCells(ev);
16348         
16349 //        ev.row = this.findBestRow(cells);
16350         
16351         // work out the location.
16352         
16353         var crow = false;
16354         var rows = [];
16355         for(var i =0; i < cells.length; i++) {
16356             
16357             cells[i].row = cells[0].row;
16358             
16359             if(i == 0){
16360                 cells[i].row = cells[i].row + 1;
16361             }
16362             
16363             if (!crow) {
16364                 crow = {
16365                     start : cells[i],
16366                     end :  cells[i]
16367                 };
16368                 continue;
16369             }
16370             if (crow.start.getY() == cells[i].getY()) {
16371                 // on same row.
16372                 crow.end = cells[i];
16373                 continue;
16374             }
16375             // different row.
16376             rows.push(crow);
16377             crow = {
16378                 start: cells[i],
16379                 end : cells[i]
16380             };
16381             
16382         }
16383         
16384         rows.push(crow);
16385         ev.els = [];
16386         ev.rows = rows;
16387         ev.cells = cells;
16388         
16389         cells[0].events.push(ev);
16390         
16391         this.calevents.push(ev);
16392     },
16393     
16394     clearEvents: function() {
16395         
16396         if(!this.calevents){
16397             return;
16398         }
16399         
16400         Roo.each(this.cells.elements, function(c){
16401             c.row = 0;
16402             c.events = [];
16403             c.more = [];
16404         });
16405         
16406         Roo.each(this.calevents, function(e) {
16407             Roo.each(e.els, function(el) {
16408                 el.un('mouseenter' ,this.onEventEnter, this);
16409                 el.un('mouseleave' ,this.onEventLeave, this);
16410                 el.remove();
16411             },this);
16412         },this);
16413         
16414         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16415             e.remove();
16416         });
16417         
16418     },
16419     
16420     renderEvents: function()
16421     {   
16422         var _this = this;
16423         
16424         this.cells.each(function(c) {
16425             
16426             if(c.row < 5){
16427                 return;
16428             }
16429             
16430             var ev = c.events;
16431             
16432             var r = 4;
16433             if(c.row != c.events.length){
16434                 r = 4 - (4 - (c.row - c.events.length));
16435             }
16436             
16437             c.events = ev.slice(0, r);
16438             c.more = ev.slice(r);
16439             
16440             if(c.more.length && c.more.length == 1){
16441                 c.events.push(c.more.pop());
16442             }
16443             
16444             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16445             
16446         });
16447             
16448         this.cells.each(function(c) {
16449             
16450             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16451             
16452             
16453             for (var e = 0; e < c.events.length; e++){
16454                 var ev = c.events[e];
16455                 var rows = ev.rows;
16456                 
16457                 for(var i = 0; i < rows.length; i++) {
16458                 
16459                     // how many rows should it span..
16460
16461                     var  cfg = {
16462                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16463                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16464
16465                         unselectable : "on",
16466                         cn : [
16467                             {
16468                                 cls: 'fc-event-inner',
16469                                 cn : [
16470     //                                {
16471     //                                  tag:'span',
16472     //                                  cls: 'fc-event-time',
16473     //                                  html : cells.length > 1 ? '' : ev.time
16474     //                                },
16475                                     {
16476                                       tag:'span',
16477                                       cls: 'fc-event-title',
16478                                       html : String.format('{0}', ev.title)
16479                                     }
16480
16481
16482                                 ]
16483                             },
16484                             {
16485                                 cls: 'ui-resizable-handle ui-resizable-e',
16486                                 html : '&nbsp;&nbsp;&nbsp'
16487                             }
16488
16489                         ]
16490                     };
16491
16492                     if (i == 0) {
16493                         cfg.cls += ' fc-event-start';
16494                     }
16495                     if ((i+1) == rows.length) {
16496                         cfg.cls += ' fc-event-end';
16497                     }
16498
16499                     var ctr = _this.el.select('.fc-event-container',true).first();
16500                     var cg = ctr.createChild(cfg);
16501
16502                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16503                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16504
16505                     var r = (c.more.length) ? 1 : 0;
16506                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16507                     cg.setWidth(ebox.right - sbox.x -2);
16508
16509                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16510                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16511                     cg.on('click', _this.onEventClick, _this, ev);
16512
16513                     ev.els.push(cg);
16514                     
16515                 }
16516                 
16517             }
16518             
16519             
16520             if(c.more.length){
16521                 var  cfg = {
16522                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16523                     style : 'position: absolute',
16524                     unselectable : "on",
16525                     cn : [
16526                         {
16527                             cls: 'fc-event-inner',
16528                             cn : [
16529                                 {
16530                                   tag:'span',
16531                                   cls: 'fc-event-title',
16532                                   html : 'More'
16533                                 }
16534
16535
16536                             ]
16537                         },
16538                         {
16539                             cls: 'ui-resizable-handle ui-resizable-e',
16540                             html : '&nbsp;&nbsp;&nbsp'
16541                         }
16542
16543                     ]
16544                 };
16545
16546                 var ctr = _this.el.select('.fc-event-container',true).first();
16547                 var cg = ctr.createChild(cfg);
16548
16549                 var sbox = c.select('.fc-day-content',true).first().getBox();
16550                 var ebox = c.select('.fc-day-content',true).first().getBox();
16551                 //Roo.log(cg);
16552                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16553                 cg.setWidth(ebox.right - sbox.x -2);
16554
16555                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16556                 
16557             }
16558             
16559         });
16560         
16561         
16562         
16563     },
16564     
16565     onEventEnter: function (e, el,event,d) {
16566         this.fireEvent('evententer', this, el, event);
16567     },
16568     
16569     onEventLeave: function (e, el,event,d) {
16570         this.fireEvent('eventleave', this, el, event);
16571     },
16572     
16573     onEventClick: function (e, el,event,d) {
16574         this.fireEvent('eventclick', this, el, event);
16575     },
16576     
16577     onMonthChange: function () {
16578         this.store.load();
16579     },
16580     
16581     onMoreEventClick: function(e, el, more)
16582     {
16583         var _this = this;
16584         
16585         this.calpopover.placement = 'right';
16586         this.calpopover.setTitle('More');
16587         
16588         this.calpopover.setContent('');
16589         
16590         var ctr = this.calpopover.el.select('.popover-content', true).first();
16591         
16592         Roo.each(more, function(m){
16593             var cfg = {
16594                 cls : 'fc-event-hori fc-event-draggable',
16595                 html : m.title
16596             };
16597             var cg = ctr.createChild(cfg);
16598             
16599             cg.on('click', _this.onEventClick, _this, m);
16600         });
16601         
16602         this.calpopover.show(el);
16603         
16604         
16605     },
16606     
16607     onLoad: function () 
16608     {   
16609         this.calevents = [];
16610         var cal = this;
16611         
16612         if(this.store.getCount() > 0){
16613             this.store.data.each(function(d){
16614                cal.addItem({
16615                     id : d.data.id,
16616                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16617                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16618                     time : d.data.start_time,
16619                     title : d.data.title,
16620                     description : d.data.description,
16621                     venue : d.data.venue
16622                 });
16623             });
16624         }
16625         
16626         this.renderEvents();
16627         
16628         if(this.calevents.length && this.loadMask){
16629             this.maskEl.hide();
16630         }
16631     },
16632     
16633     onBeforeLoad: function()
16634     {
16635         this.clearEvents();
16636         if(this.loadMask){
16637             this.maskEl.show();
16638         }
16639     }
16640 });
16641
16642  
16643  /*
16644  * - LGPL
16645  *
16646  * element
16647  * 
16648  */
16649
16650 /**
16651  * @class Roo.bootstrap.Popover
16652  * @extends Roo.bootstrap.Component
16653  * Bootstrap Popover class
16654  * @cfg {String} html contents of the popover   (or false to use children..)
16655  * @cfg {String} title of popover (or false to hide)
16656  * @cfg {String} placement how it is placed
16657  * @cfg {String} trigger click || hover (or false to trigger manually)
16658  * @cfg {String} over what (parent or false to trigger manually.)
16659  * @cfg {Number} delay - delay before showing
16660  
16661  * @constructor
16662  * Create a new Popover
16663  * @param {Object} config The config object
16664  */
16665
16666 Roo.bootstrap.Popover = function(config){
16667     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16668     
16669     this.addEvents({
16670         // raw events
16671          /**
16672          * @event show
16673          * After the popover show
16674          * 
16675          * @param {Roo.bootstrap.Popover} this
16676          */
16677         "show" : true,
16678         /**
16679          * @event hide
16680          * After the popover hide
16681          * 
16682          * @param {Roo.bootstrap.Popover} this
16683          */
16684         "hide" : true
16685     });
16686 };
16687
16688 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16689     
16690     title: 'Fill in a title',
16691     html: false,
16692     
16693     placement : 'right',
16694     trigger : 'hover', // hover
16695     
16696     delay : 0,
16697     
16698     over: 'parent',
16699     
16700     can_build_overlaid : false,
16701     
16702     getChildContainer : function()
16703     {
16704         return this.el.select('.popover-content',true).first();
16705     },
16706     
16707     getAutoCreate : function(){
16708          
16709         var cfg = {
16710            cls : 'popover roo-dynamic',
16711            style: 'display:block',
16712            cn : [
16713                 {
16714                     cls : 'arrow'
16715                 },
16716                 {
16717                     cls : 'popover-inner',
16718                     cn : [
16719                         {
16720                             tag: 'h3',
16721                             cls: 'popover-title',
16722                             html : this.title
16723                         },
16724                         {
16725                             cls : 'popover-content',
16726                             html : this.html
16727                         }
16728                     ]
16729                     
16730                 }
16731            ]
16732         };
16733         
16734         return cfg;
16735     },
16736     setTitle: function(str)
16737     {
16738         this.title = str;
16739         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16740     },
16741     setContent: function(str)
16742     {
16743         this.html = str;
16744         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16745     },
16746     // as it get's added to the bottom of the page.
16747     onRender : function(ct, position)
16748     {
16749         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16750         if(!this.el){
16751             var cfg = Roo.apply({},  this.getAutoCreate());
16752             cfg.id = Roo.id();
16753             
16754             if (this.cls) {
16755                 cfg.cls += ' ' + this.cls;
16756             }
16757             if (this.style) {
16758                 cfg.style = this.style;
16759             }
16760             //Roo.log("adding to ");
16761             this.el = Roo.get(document.body).createChild(cfg, position);
16762 //            Roo.log(this.el);
16763         }
16764         this.initEvents();
16765     },
16766     
16767     initEvents : function()
16768     {
16769         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16770         this.el.enableDisplayMode('block');
16771         this.el.hide();
16772         if (this.over === false) {
16773             return; 
16774         }
16775         if (this.triggers === false) {
16776             return;
16777         }
16778         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16779         var triggers = this.trigger ? this.trigger.split(' ') : [];
16780         Roo.each(triggers, function(trigger) {
16781         
16782             if (trigger == 'click') {
16783                 on_el.on('click', this.toggle, this);
16784             } else if (trigger != 'manual') {
16785                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16786                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16787       
16788                 on_el.on(eventIn  ,this.enter, this);
16789                 on_el.on(eventOut, this.leave, this);
16790             }
16791         }, this);
16792         
16793     },
16794     
16795     
16796     // private
16797     timeout : null,
16798     hoverState : null,
16799     
16800     toggle : function () {
16801         this.hoverState == 'in' ? this.leave() : this.enter();
16802     },
16803     
16804     enter : function () {
16805         
16806         clearTimeout(this.timeout);
16807     
16808         this.hoverState = 'in';
16809     
16810         if (!this.delay || !this.delay.show) {
16811             this.show();
16812             return;
16813         }
16814         var _t = this;
16815         this.timeout = setTimeout(function () {
16816             if (_t.hoverState == 'in') {
16817                 _t.show();
16818             }
16819         }, this.delay.show)
16820     },
16821     
16822     leave : function() {
16823         clearTimeout(this.timeout);
16824     
16825         this.hoverState = 'out';
16826     
16827         if (!this.delay || !this.delay.hide) {
16828             this.hide();
16829             return;
16830         }
16831         var _t = this;
16832         this.timeout = setTimeout(function () {
16833             if (_t.hoverState == 'out') {
16834                 _t.hide();
16835             }
16836         }, this.delay.hide)
16837     },
16838     
16839     show : function (on_el)
16840     {
16841         if (!on_el) {
16842             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16843         }
16844         
16845         // set content.
16846         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16847         if (this.html !== false) {
16848             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16849         }
16850         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16851         if (!this.title.length) {
16852             this.el.select('.popover-title',true).hide();
16853         }
16854         
16855         var placement = typeof this.placement == 'function' ?
16856             this.placement.call(this, this.el, on_el) :
16857             this.placement;
16858             
16859         var autoToken = /\s?auto?\s?/i;
16860         var autoPlace = autoToken.test(placement);
16861         if (autoPlace) {
16862             placement = placement.replace(autoToken, '') || 'top';
16863         }
16864         
16865         //this.el.detach()
16866         //this.el.setXY([0,0]);
16867         this.el.show();
16868         this.el.dom.style.display='block';
16869         this.el.addClass(placement);
16870         
16871         //this.el.appendTo(on_el);
16872         
16873         var p = this.getPosition();
16874         var box = this.el.getBox();
16875         
16876         if (autoPlace) {
16877             // fixme..
16878         }
16879         var align = Roo.bootstrap.Popover.alignment[placement];
16880         this.el.alignTo(on_el, align[0],align[1]);
16881         //var arrow = this.el.select('.arrow',true).first();
16882         //arrow.set(align[2], 
16883         
16884         this.el.addClass('in');
16885         
16886         
16887         if (this.el.hasClass('fade')) {
16888             // fade it?
16889         }
16890         
16891         this.hoverState = 'in';
16892         
16893         this.fireEvent('show', this);
16894         
16895     },
16896     hide : function()
16897     {
16898         this.el.setXY([0,0]);
16899         this.el.removeClass('in');
16900         this.el.hide();
16901         this.hoverState = null;
16902         
16903         this.fireEvent('hide', this);
16904     }
16905     
16906 });
16907
16908 Roo.bootstrap.Popover.alignment = {
16909     'left' : ['r-l', [-10,0], 'right'],
16910     'right' : ['l-r', [10,0], 'left'],
16911     'bottom' : ['t-b', [0,10], 'top'],
16912     'top' : [ 'b-t', [0,-10], 'bottom']
16913 };
16914
16915  /*
16916  * - LGPL
16917  *
16918  * Progress
16919  * 
16920  */
16921
16922 /**
16923  * @class Roo.bootstrap.Progress
16924  * @extends Roo.bootstrap.Component
16925  * Bootstrap Progress class
16926  * @cfg {Boolean} striped striped of the progress bar
16927  * @cfg {Boolean} active animated of the progress bar
16928  * 
16929  * 
16930  * @constructor
16931  * Create a new Progress
16932  * @param {Object} config The config object
16933  */
16934
16935 Roo.bootstrap.Progress = function(config){
16936     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16937 };
16938
16939 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16940     
16941     striped : false,
16942     active: false,
16943     
16944     getAutoCreate : function(){
16945         var cfg = {
16946             tag: 'div',
16947             cls: 'progress'
16948         };
16949         
16950         
16951         if(this.striped){
16952             cfg.cls += ' progress-striped';
16953         }
16954       
16955         if(this.active){
16956             cfg.cls += ' active';
16957         }
16958         
16959         
16960         return cfg;
16961     }
16962    
16963 });
16964
16965  
16966
16967  /*
16968  * - LGPL
16969  *
16970  * ProgressBar
16971  * 
16972  */
16973
16974 /**
16975  * @class Roo.bootstrap.ProgressBar
16976  * @extends Roo.bootstrap.Component
16977  * Bootstrap ProgressBar class
16978  * @cfg {Number} aria_valuenow aria-value now
16979  * @cfg {Number} aria_valuemin aria-value min
16980  * @cfg {Number} aria_valuemax aria-value max
16981  * @cfg {String} label label for the progress bar
16982  * @cfg {String} panel (success | info | warning | danger )
16983  * @cfg {String} role role of the progress bar
16984  * @cfg {String} sr_only text
16985  * 
16986  * 
16987  * @constructor
16988  * Create a new ProgressBar
16989  * @param {Object} config The config object
16990  */
16991
16992 Roo.bootstrap.ProgressBar = function(config){
16993     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16994 };
16995
16996 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16997     
16998     aria_valuenow : 0,
16999     aria_valuemin : 0,
17000     aria_valuemax : 100,
17001     label : false,
17002     panel : false,
17003     role : false,
17004     sr_only: false,
17005     
17006     getAutoCreate : function()
17007     {
17008         
17009         var cfg = {
17010             tag: 'div',
17011             cls: 'progress-bar',
17012             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17013         };
17014         
17015         if(this.sr_only){
17016             cfg.cn = {
17017                 tag: 'span',
17018                 cls: 'sr-only',
17019                 html: this.sr_only
17020             }
17021         }
17022         
17023         if(this.role){
17024             cfg.role = this.role;
17025         }
17026         
17027         if(this.aria_valuenow){
17028             cfg['aria-valuenow'] = this.aria_valuenow;
17029         }
17030         
17031         if(this.aria_valuemin){
17032             cfg['aria-valuemin'] = this.aria_valuemin;
17033         }
17034         
17035         if(this.aria_valuemax){
17036             cfg['aria-valuemax'] = this.aria_valuemax;
17037         }
17038         
17039         if(this.label && !this.sr_only){
17040             cfg.html = this.label;
17041         }
17042         
17043         if(this.panel){
17044             cfg.cls += ' progress-bar-' + this.panel;
17045         }
17046         
17047         return cfg;
17048     },
17049     
17050     update : function(aria_valuenow)
17051     {
17052         this.aria_valuenow = aria_valuenow;
17053         
17054         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17055     }
17056    
17057 });
17058
17059  
17060
17061  /*
17062  * - LGPL
17063  *
17064  * column
17065  * 
17066  */
17067
17068 /**
17069  * @class Roo.bootstrap.TabGroup
17070  * @extends Roo.bootstrap.Column
17071  * Bootstrap Column class
17072  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17073  * @cfg {Boolean} carousel true to make the group behave like a carousel
17074  * @cfg {Boolean} bullets show bullets for the panels
17075  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17076  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17077  * @cfg {Boolean} showarrow (true|false) show arrow default true
17078  * 
17079  * @constructor
17080  * Create a new TabGroup
17081  * @param {Object} config The config object
17082  */
17083
17084 Roo.bootstrap.TabGroup = function(config){
17085     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17086     if (!this.navId) {
17087         this.navId = Roo.id();
17088     }
17089     this.tabs = [];
17090     Roo.bootstrap.TabGroup.register(this);
17091     
17092 };
17093
17094 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17095     
17096     carousel : false,
17097     transition : false,
17098     bullets : 0,
17099     timer : 0,
17100     autoslide : false,
17101     slideFn : false,
17102     slideOnTouch : false,
17103     showarrow : true,
17104     
17105     getAutoCreate : function()
17106     {
17107         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17108         
17109         cfg.cls += ' tab-content';
17110         
17111         if (this.carousel) {
17112             cfg.cls += ' carousel slide';
17113             
17114             cfg.cn = [{
17115                cls : 'carousel-inner',
17116                cn : []
17117             }];
17118         
17119             if(this.bullets  && !Roo.isTouch){
17120                 
17121                 var bullets = {
17122                     cls : 'carousel-bullets',
17123                     cn : []
17124                 };
17125                
17126                 if(this.bullets_cls){
17127                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17128                 }
17129                 
17130                 bullets.cn.push({
17131                     cls : 'clear'
17132                 });
17133                 
17134                 cfg.cn[0].cn.push(bullets);
17135             }
17136             
17137             if(this.showarrow){
17138                 cfg.cn[0].cn.push({
17139                     tag : 'div',
17140                     class : 'carousel-arrow',
17141                     cn : [
17142                         {
17143                             tag : 'div',
17144                             class : 'carousel-prev',
17145                             cn : [
17146                                 {
17147                                     tag : 'i',
17148                                     class : 'fa fa-chevron-left'
17149                                 }
17150                             ]
17151                         },
17152                         {
17153                             tag : 'div',
17154                             class : 'carousel-next',
17155                             cn : [
17156                                 {
17157                                     tag : 'i',
17158                                     class : 'fa fa-chevron-right'
17159                                 }
17160                             ]
17161                         }
17162                     ]
17163                 });
17164             }
17165             
17166         }
17167         
17168         return cfg;
17169     },
17170     
17171     initEvents:  function()
17172     {
17173 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17174 //            this.el.on("touchstart", this.onTouchStart, this);
17175 //        }
17176         
17177         if(this.autoslide){
17178             var _this = this;
17179             
17180             this.slideFn = window.setInterval(function() {
17181                 _this.showPanelNext();
17182             }, this.timer);
17183         }
17184         
17185         if(this.showarrow){
17186             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17187             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17188         }
17189         
17190         
17191     },
17192     
17193 //    onTouchStart : function(e, el, o)
17194 //    {
17195 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17196 //            return;
17197 //        }
17198 //        
17199 //        this.showPanelNext();
17200 //    },
17201     
17202     
17203     getChildContainer : function()
17204     {
17205         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17206     },
17207     
17208     /**
17209     * register a Navigation item
17210     * @param {Roo.bootstrap.NavItem} the navitem to add
17211     */
17212     register : function(item)
17213     {
17214         this.tabs.push( item);
17215         item.navId = this.navId; // not really needed..
17216         this.addBullet();
17217     
17218     },
17219     
17220     getActivePanel : function()
17221     {
17222         var r = false;
17223         Roo.each(this.tabs, function(t) {
17224             if (t.active) {
17225                 r = t;
17226                 return false;
17227             }
17228             return null;
17229         });
17230         return r;
17231         
17232     },
17233     getPanelByName : function(n)
17234     {
17235         var r = false;
17236         Roo.each(this.tabs, function(t) {
17237             if (t.tabId == n) {
17238                 r = t;
17239                 return false;
17240             }
17241             return null;
17242         });
17243         return r;
17244     },
17245     indexOfPanel : function(p)
17246     {
17247         var r = false;
17248         Roo.each(this.tabs, function(t,i) {
17249             if (t.tabId == p.tabId) {
17250                 r = i;
17251                 return false;
17252             }
17253             return null;
17254         });
17255         return r;
17256     },
17257     /**
17258      * show a specific panel
17259      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17260      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17261      */
17262     showPanel : function (pan)
17263     {
17264         if(this.transition || typeof(pan) == 'undefined'){
17265             Roo.log("waiting for the transitionend");
17266             return;
17267         }
17268         
17269         if (typeof(pan) == 'number') {
17270             pan = this.tabs[pan];
17271         }
17272         
17273         if (typeof(pan) == 'string') {
17274             pan = this.getPanelByName(pan);
17275         }
17276         
17277         var cur = this.getActivePanel();
17278         
17279         if(!pan || !cur){
17280             Roo.log('pan or acitve pan is undefined');
17281             return false;
17282         }
17283         
17284         if (pan.tabId == this.getActivePanel().tabId) {
17285             return true;
17286         }
17287         
17288         if (false === cur.fireEvent('beforedeactivate')) {
17289             return false;
17290         }
17291         
17292         if(this.bullets > 0 && !Roo.isTouch){
17293             this.setActiveBullet(this.indexOfPanel(pan));
17294         }
17295         
17296         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17297             
17298             this.transition = true;
17299             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17300             var lr = dir == 'next' ? 'left' : 'right';
17301             pan.el.addClass(dir); // or prev
17302             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17303             cur.el.addClass(lr); // or right
17304             pan.el.addClass(lr);
17305             
17306             var _this = this;
17307             cur.el.on('transitionend', function() {
17308                 Roo.log("trans end?");
17309                 
17310                 pan.el.removeClass([lr,dir]);
17311                 pan.setActive(true);
17312                 
17313                 cur.el.removeClass([lr]);
17314                 cur.setActive(false);
17315                 
17316                 _this.transition = false;
17317                 
17318             }, this, { single:  true } );
17319             
17320             return true;
17321         }
17322         
17323         cur.setActive(false);
17324         pan.setActive(true);
17325         
17326         return true;
17327         
17328     },
17329     showPanelNext : function()
17330     {
17331         var i = this.indexOfPanel(this.getActivePanel());
17332         
17333         if (i >= this.tabs.length - 1 && !this.autoslide) {
17334             return;
17335         }
17336         
17337         if (i >= this.tabs.length - 1 && this.autoslide) {
17338             i = -1;
17339         }
17340         
17341         this.showPanel(this.tabs[i+1]);
17342     },
17343     
17344     showPanelPrev : function()
17345     {
17346         var i = this.indexOfPanel(this.getActivePanel());
17347         
17348         if (i  < 1 && !this.autoslide) {
17349             return;
17350         }
17351         
17352         if (i < 1 && this.autoslide) {
17353             i = this.tabs.length;
17354         }
17355         
17356         this.showPanel(this.tabs[i-1]);
17357     },
17358     
17359     
17360     addBullet: function()
17361     {
17362         if(!this.bullets || Roo.isTouch){
17363             return;
17364         }
17365         var ctr = this.el.select('.carousel-bullets',true).first();
17366         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17367         var bullet = ctr.createChild({
17368             cls : 'bullet bullet-' + i
17369         },ctr.dom.lastChild);
17370         
17371         
17372         var _this = this;
17373         
17374         bullet.on('click', (function(e, el, o, ii, t){
17375
17376             e.preventDefault();
17377
17378             this.showPanel(ii);
17379
17380             if(this.autoslide && this.slideFn){
17381                 clearInterval(this.slideFn);
17382                 this.slideFn = window.setInterval(function() {
17383                     _this.showPanelNext();
17384                 }, this.timer);
17385             }
17386
17387         }).createDelegate(this, [i, bullet], true));
17388                 
17389         
17390     },
17391      
17392     setActiveBullet : function(i)
17393     {
17394         if(Roo.isTouch){
17395             return;
17396         }
17397         
17398         Roo.each(this.el.select('.bullet', true).elements, function(el){
17399             el.removeClass('selected');
17400         });
17401
17402         var bullet = this.el.select('.bullet-' + i, true).first();
17403         
17404         if(!bullet){
17405             return;
17406         }
17407         
17408         bullet.addClass('selected');
17409     }
17410     
17411     
17412   
17413 });
17414
17415  
17416
17417  
17418  
17419 Roo.apply(Roo.bootstrap.TabGroup, {
17420     
17421     groups: {},
17422      /**
17423     * register a Navigation Group
17424     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17425     */
17426     register : function(navgrp)
17427     {
17428         this.groups[navgrp.navId] = navgrp;
17429         
17430     },
17431     /**
17432     * fetch a Navigation Group based on the navigation ID
17433     * if one does not exist , it will get created.
17434     * @param {string} the navgroup to add
17435     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17436     */
17437     get: function(navId) {
17438         if (typeof(this.groups[navId]) == 'undefined') {
17439             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17440         }
17441         return this.groups[navId] ;
17442     }
17443     
17444     
17445     
17446 });
17447
17448  /*
17449  * - LGPL
17450  *
17451  * TabPanel
17452  * 
17453  */
17454
17455 /**
17456  * @class Roo.bootstrap.TabPanel
17457  * @extends Roo.bootstrap.Component
17458  * Bootstrap TabPanel class
17459  * @cfg {Boolean} active panel active
17460  * @cfg {String} html panel content
17461  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17462  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17463  * @cfg {String} href click to link..
17464  * 
17465  * 
17466  * @constructor
17467  * Create a new TabPanel
17468  * @param {Object} config The config object
17469  */
17470
17471 Roo.bootstrap.TabPanel = function(config){
17472     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17473     this.addEvents({
17474         /**
17475              * @event changed
17476              * Fires when the active status changes
17477              * @param {Roo.bootstrap.TabPanel} this
17478              * @param {Boolean} state the new state
17479             
17480          */
17481         'changed': true,
17482         /**
17483              * @event beforedeactivate
17484              * Fires before a tab is de-activated - can be used to do validation on a form.
17485              * @param {Roo.bootstrap.TabPanel} this
17486              * @return {Boolean} false if there is an error
17487             
17488          */
17489         'beforedeactivate': true
17490      });
17491     
17492     this.tabId = this.tabId || Roo.id();
17493   
17494 };
17495
17496 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17497     
17498     active: false,
17499     html: false,
17500     tabId: false,
17501     navId : false,
17502     href : '',
17503     
17504     getAutoCreate : function(){
17505         var cfg = {
17506             tag: 'div',
17507             // item is needed for carousel - not sure if it has any effect otherwise
17508             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17509             html: this.html || ''
17510         };
17511         
17512         if(this.active){
17513             cfg.cls += ' active';
17514         }
17515         
17516         if(this.tabId){
17517             cfg.tabId = this.tabId;
17518         }
17519         
17520         
17521         return cfg;
17522     },
17523     
17524     initEvents:  function()
17525     {
17526         var p = this.parent();
17527         
17528         this.navId = this.navId || p.navId;
17529         
17530         if (typeof(this.navId) != 'undefined') {
17531             // not really needed.. but just in case.. parent should be a NavGroup.
17532             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17533             
17534             tg.register(this);
17535             
17536             var i = tg.tabs.length - 1;
17537             
17538             if(this.active && tg.bullets > 0 && i < tg.bullets){
17539                 tg.setActiveBullet(i);
17540             }
17541         }
17542         
17543         this.el.on('click', this.onClick, this);
17544         
17545         if(Roo.isTouch){
17546             this.el.on("touchstart", this.onTouchStart, this);
17547             this.el.on("touchmove", this.onTouchMove, this);
17548             this.el.on("touchend", this.onTouchEnd, this);
17549         }
17550         
17551     },
17552     
17553     onRender : function(ct, position)
17554     {
17555         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17556     },
17557     
17558     setActive : function(state)
17559     {
17560         Roo.log("panel - set active " + this.tabId + "=" + state);
17561         
17562         this.active = state;
17563         if (!state) {
17564             this.el.removeClass('active');
17565             
17566         } else  if (!this.el.hasClass('active')) {
17567             this.el.addClass('active');
17568         }
17569         
17570         this.fireEvent('changed', this, state);
17571     },
17572     
17573     onClick : function(e)
17574     {
17575         e.preventDefault();
17576         
17577         if(!this.href.length){
17578             return;
17579         }
17580         
17581         window.location.href = this.href;
17582     },
17583     
17584     startX : 0,
17585     startY : 0,
17586     endX : 0,
17587     endY : 0,
17588     swiping : false,
17589     
17590     onTouchStart : function(e)
17591     {
17592         this.swiping = false;
17593         
17594         this.startX = e.browserEvent.touches[0].clientX;
17595         this.startY = e.browserEvent.touches[0].clientY;
17596     },
17597     
17598     onTouchMove : function(e)
17599     {
17600         this.swiping = true;
17601         
17602         this.endX = e.browserEvent.touches[0].clientX;
17603         this.endY = e.browserEvent.touches[0].clientY;
17604     },
17605     
17606     onTouchEnd : function(e)
17607     {
17608         if(!this.swiping){
17609             this.onClick(e);
17610             return;
17611         }
17612         
17613         var tabGroup = this.parent();
17614         
17615         if(this.endX > this.startX){ // swiping right
17616             tabGroup.showPanelPrev();
17617             return;
17618         }
17619         
17620         if(this.startX > this.endX){ // swiping left
17621             tabGroup.showPanelNext();
17622             return;
17623         }
17624     }
17625     
17626     
17627 });
17628  
17629
17630  
17631
17632  /*
17633  * - LGPL
17634  *
17635  * DateField
17636  * 
17637  */
17638
17639 /**
17640  * @class Roo.bootstrap.DateField
17641  * @extends Roo.bootstrap.Input
17642  * Bootstrap DateField class
17643  * @cfg {Number} weekStart default 0
17644  * @cfg {String} viewMode default empty, (months|years)
17645  * @cfg {String} minViewMode default empty, (months|years)
17646  * @cfg {Number} startDate default -Infinity
17647  * @cfg {Number} endDate default Infinity
17648  * @cfg {Boolean} todayHighlight default false
17649  * @cfg {Boolean} todayBtn default false
17650  * @cfg {Boolean} calendarWeeks default false
17651  * @cfg {Object} daysOfWeekDisabled default empty
17652  * @cfg {Boolean} singleMode default false (true | false)
17653  * 
17654  * @cfg {Boolean} keyboardNavigation default true
17655  * @cfg {String} language default en
17656  * 
17657  * @constructor
17658  * Create a new DateField
17659  * @param {Object} config The config object
17660  */
17661
17662 Roo.bootstrap.DateField = function(config){
17663     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17664      this.addEvents({
17665             /**
17666              * @event show
17667              * Fires when this field show.
17668              * @param {Roo.bootstrap.DateField} this
17669              * @param {Mixed} date The date value
17670              */
17671             show : true,
17672             /**
17673              * @event show
17674              * Fires when this field hide.
17675              * @param {Roo.bootstrap.DateField} this
17676              * @param {Mixed} date The date value
17677              */
17678             hide : true,
17679             /**
17680              * @event select
17681              * Fires when select a date.
17682              * @param {Roo.bootstrap.DateField} this
17683              * @param {Mixed} date The date value
17684              */
17685             select : true,
17686             /**
17687              * @event beforeselect
17688              * Fires when before select a date.
17689              * @param {Roo.bootstrap.DateField} this
17690              * @param {Mixed} date The date value
17691              */
17692             beforeselect : true
17693         });
17694 };
17695
17696 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17697     
17698     /**
17699      * @cfg {String} format
17700      * The default date format string which can be overriden for localization support.  The format must be
17701      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17702      */
17703     format : "m/d/y",
17704     /**
17705      * @cfg {String} altFormats
17706      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17707      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17708      */
17709     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17710     
17711     weekStart : 0,
17712     
17713     viewMode : '',
17714     
17715     minViewMode : '',
17716     
17717     todayHighlight : false,
17718     
17719     todayBtn: false,
17720     
17721     language: 'en',
17722     
17723     keyboardNavigation: true,
17724     
17725     calendarWeeks: false,
17726     
17727     startDate: -Infinity,
17728     
17729     endDate: Infinity,
17730     
17731     daysOfWeekDisabled: [],
17732     
17733     _events: [],
17734     
17735     singleMode : false,
17736     
17737     UTCDate: function()
17738     {
17739         return new Date(Date.UTC.apply(Date, arguments));
17740     },
17741     
17742     UTCToday: function()
17743     {
17744         var today = new Date();
17745         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17746     },
17747     
17748     getDate: function() {
17749             var d = this.getUTCDate();
17750             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17751     },
17752     
17753     getUTCDate: function() {
17754             return this.date;
17755     },
17756     
17757     setDate: function(d) {
17758             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17759     },
17760     
17761     setUTCDate: function(d) {
17762             this.date = d;
17763             this.setValue(this.formatDate(this.date));
17764     },
17765         
17766     onRender: function(ct, position)
17767     {
17768         
17769         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17770         
17771         this.language = this.language || 'en';
17772         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17773         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17774         
17775         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17776         this.format = this.format || 'm/d/y';
17777         this.isInline = false;
17778         this.isInput = true;
17779         this.component = this.el.select('.add-on', true).first() || false;
17780         this.component = (this.component && this.component.length === 0) ? false : this.component;
17781         this.hasInput = this.component && this.inputEl().length;
17782         
17783         if (typeof(this.minViewMode === 'string')) {
17784             switch (this.minViewMode) {
17785                 case 'months':
17786                     this.minViewMode = 1;
17787                     break;
17788                 case 'years':
17789                     this.minViewMode = 2;
17790                     break;
17791                 default:
17792                     this.minViewMode = 0;
17793                     break;
17794             }
17795         }
17796         
17797         if (typeof(this.viewMode === 'string')) {
17798             switch (this.viewMode) {
17799                 case 'months':
17800                     this.viewMode = 1;
17801                     break;
17802                 case 'years':
17803                     this.viewMode = 2;
17804                     break;
17805                 default:
17806                     this.viewMode = 0;
17807                     break;
17808             }
17809         }
17810                 
17811         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17812         
17813 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17814         
17815         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17816         
17817         this.picker().on('mousedown', this.onMousedown, this);
17818         this.picker().on('click', this.onClick, this);
17819         
17820         this.picker().addClass('datepicker-dropdown');
17821         
17822         this.startViewMode = this.viewMode;
17823         
17824         if(this.singleMode){
17825             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17826                 v.setVisibilityMode(Roo.Element.DISPLAY);
17827                 v.hide();
17828             });
17829             
17830             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17831                 v.setStyle('width', '189px');
17832             });
17833         }
17834         
17835         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17836             if(!this.calendarWeeks){
17837                 v.remove();
17838                 return;
17839             }
17840             
17841             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17842             v.attr('colspan', function(i, val){
17843                 return parseInt(val) + 1;
17844             });
17845         });
17846                         
17847         
17848         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17849         
17850         this.setStartDate(this.startDate);
17851         this.setEndDate(this.endDate);
17852         
17853         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17854         
17855         this.fillDow();
17856         this.fillMonths();
17857         this.update();
17858         this.showMode();
17859         
17860         if(this.isInline) {
17861             this.show();
17862         }
17863     },
17864     
17865     picker : function()
17866     {
17867         return this.pickerEl;
17868 //        return this.el.select('.datepicker', true).first();
17869     },
17870     
17871     fillDow: function()
17872     {
17873         var dowCnt = this.weekStart;
17874         
17875         var dow = {
17876             tag: 'tr',
17877             cn: [
17878                 
17879             ]
17880         };
17881         
17882         if(this.calendarWeeks){
17883             dow.cn.push({
17884                 tag: 'th',
17885                 cls: 'cw',
17886                 html: '&nbsp;'
17887             })
17888         }
17889         
17890         while (dowCnt < this.weekStart + 7) {
17891             dow.cn.push({
17892                 tag: 'th',
17893                 cls: 'dow',
17894                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17895             });
17896         }
17897         
17898         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17899     },
17900     
17901     fillMonths: function()
17902     {    
17903         var i = 0;
17904         var months = this.picker().select('>.datepicker-months td', true).first();
17905         
17906         months.dom.innerHTML = '';
17907         
17908         while (i < 12) {
17909             var month = {
17910                 tag: 'span',
17911                 cls: 'month',
17912                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17913             };
17914             
17915             months.createChild(month);
17916         }
17917         
17918     },
17919     
17920     update: function()
17921     {
17922         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;
17923         
17924         if (this.date < this.startDate) {
17925             this.viewDate = new Date(this.startDate);
17926         } else if (this.date > this.endDate) {
17927             this.viewDate = new Date(this.endDate);
17928         } else {
17929             this.viewDate = new Date(this.date);
17930         }
17931         
17932         this.fill();
17933     },
17934     
17935     fill: function() 
17936     {
17937         var d = new Date(this.viewDate),
17938                 year = d.getUTCFullYear(),
17939                 month = d.getUTCMonth(),
17940                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17941                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17942                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17943                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17944                 currentDate = this.date && this.date.valueOf(),
17945                 today = this.UTCToday();
17946         
17947         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17948         
17949 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17950         
17951 //        this.picker.select('>tfoot th.today').
17952 //                                              .text(dates[this.language].today)
17953 //                                              .toggle(this.todayBtn !== false);
17954     
17955         this.updateNavArrows();
17956         this.fillMonths();
17957                                                 
17958         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17959         
17960         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17961          
17962         prevMonth.setUTCDate(day);
17963         
17964         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17965         
17966         var nextMonth = new Date(prevMonth);
17967         
17968         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17969         
17970         nextMonth = nextMonth.valueOf();
17971         
17972         var fillMonths = false;
17973         
17974         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17975         
17976         while(prevMonth.valueOf() < nextMonth) {
17977             var clsName = '';
17978             
17979             if (prevMonth.getUTCDay() === this.weekStart) {
17980                 if(fillMonths){
17981                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17982                 }
17983                     
17984                 fillMonths = {
17985                     tag: 'tr',
17986                     cn: []
17987                 };
17988                 
17989                 if(this.calendarWeeks){
17990                     // ISO 8601: First week contains first thursday.
17991                     // ISO also states week starts on Monday, but we can be more abstract here.
17992                     var
17993                     // Start of current week: based on weekstart/current date
17994                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17995                     // Thursday of this week
17996                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17997                     // First Thursday of year, year from thursday
17998                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17999                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18000                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18001                     
18002                     fillMonths.cn.push({
18003                         tag: 'td',
18004                         cls: 'cw',
18005                         html: calWeek
18006                     });
18007                 }
18008             }
18009             
18010             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18011                 clsName += ' old';
18012             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18013                 clsName += ' new';
18014             }
18015             if (this.todayHighlight &&
18016                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18017                 prevMonth.getUTCMonth() == today.getMonth() &&
18018                 prevMonth.getUTCDate() == today.getDate()) {
18019                 clsName += ' today';
18020             }
18021             
18022             if (currentDate && prevMonth.valueOf() === currentDate) {
18023                 clsName += ' active';
18024             }
18025             
18026             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18027                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18028                     clsName += ' disabled';
18029             }
18030             
18031             fillMonths.cn.push({
18032                 tag: 'td',
18033                 cls: 'day ' + clsName,
18034                 html: prevMonth.getDate()
18035             });
18036             
18037             prevMonth.setDate(prevMonth.getDate()+1);
18038         }
18039           
18040         var currentYear = this.date && this.date.getUTCFullYear();
18041         var currentMonth = this.date && this.date.getUTCMonth();
18042         
18043         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18044         
18045         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18046             v.removeClass('active');
18047             
18048             if(currentYear === year && k === currentMonth){
18049                 v.addClass('active');
18050             }
18051             
18052             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18053                 v.addClass('disabled');
18054             }
18055             
18056         });
18057         
18058         
18059         year = parseInt(year/10, 10) * 10;
18060         
18061         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18062         
18063         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18064         
18065         year -= 1;
18066         for (var i = -1; i < 11; i++) {
18067             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18068                 tag: 'span',
18069                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18070                 html: year
18071             });
18072             
18073             year += 1;
18074         }
18075     },
18076     
18077     showMode: function(dir) 
18078     {
18079         if (dir) {
18080             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18081         }
18082         
18083         Roo.each(this.picker().select('>div',true).elements, function(v){
18084             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18085             v.hide();
18086         });
18087         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18088     },
18089     
18090     place: function()
18091     {
18092         if(this.isInline) {
18093             return;
18094         }
18095         
18096         this.picker().removeClass(['bottom', 'top']);
18097         
18098         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18099             /*
18100              * place to the top of element!
18101              *
18102              */
18103             
18104             this.picker().addClass('top');
18105             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18106             
18107             return;
18108         }
18109         
18110         this.picker().addClass('bottom');
18111         
18112         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18113     },
18114     
18115     parseDate : function(value)
18116     {
18117         if(!value || value instanceof Date){
18118             return value;
18119         }
18120         var v = Date.parseDate(value, this.format);
18121         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18122             v = Date.parseDate(value, 'Y-m-d');
18123         }
18124         if(!v && this.altFormats){
18125             if(!this.altFormatsArray){
18126                 this.altFormatsArray = this.altFormats.split("|");
18127             }
18128             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18129                 v = Date.parseDate(value, this.altFormatsArray[i]);
18130             }
18131         }
18132         return v;
18133     },
18134     
18135     formatDate : function(date, fmt)
18136     {   
18137         return (!date || !(date instanceof Date)) ?
18138         date : date.dateFormat(fmt || this.format);
18139     },
18140     
18141     onFocus : function()
18142     {
18143         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18144         this.show();
18145     },
18146     
18147     onBlur : function()
18148     {
18149         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18150         
18151         var d = this.inputEl().getValue();
18152         
18153         this.setValue(d);
18154                 
18155         this.hide();
18156     },
18157     
18158     show : function()
18159     {
18160         this.picker().show();
18161         this.update();
18162         this.place();
18163         
18164         this.fireEvent('show', this, this.date);
18165     },
18166     
18167     hide : function()
18168     {
18169         if(this.isInline) {
18170             return;
18171         }
18172         this.picker().hide();
18173         this.viewMode = this.startViewMode;
18174         this.showMode();
18175         
18176         this.fireEvent('hide', this, this.date);
18177         
18178     },
18179     
18180     onMousedown: function(e)
18181     {
18182         e.stopPropagation();
18183         e.preventDefault();
18184     },
18185     
18186     keyup: function(e)
18187     {
18188         Roo.bootstrap.DateField.superclass.keyup.call(this);
18189         this.update();
18190     },
18191
18192     setValue: function(v)
18193     {
18194         if(this.fireEvent('beforeselect', this, v) !== false){
18195             var d = new Date(this.parseDate(v) ).clearTime();
18196         
18197             if(isNaN(d.getTime())){
18198                 this.date = this.viewDate = '';
18199                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18200                 return;
18201             }
18202
18203             v = this.formatDate(d);
18204
18205             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18206
18207             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18208
18209             this.update();
18210
18211             this.fireEvent('select', this, this.date);
18212         }
18213     },
18214     
18215     getValue: function()
18216     {
18217         return this.formatDate(this.date);
18218     },
18219     
18220     fireKey: function(e)
18221     {
18222         if (!this.picker().isVisible()){
18223             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18224                 this.show();
18225             }
18226             return;
18227         }
18228         
18229         var dateChanged = false,
18230         dir, day, month,
18231         newDate, newViewDate;
18232         
18233         switch(e.keyCode){
18234             case 27: // escape
18235                 this.hide();
18236                 e.preventDefault();
18237                 break;
18238             case 37: // left
18239             case 39: // right
18240                 if (!this.keyboardNavigation) {
18241                     break;
18242                 }
18243                 dir = e.keyCode == 37 ? -1 : 1;
18244                 
18245                 if (e.ctrlKey){
18246                     newDate = this.moveYear(this.date, dir);
18247                     newViewDate = this.moveYear(this.viewDate, dir);
18248                 } else if (e.shiftKey){
18249                     newDate = this.moveMonth(this.date, dir);
18250                     newViewDate = this.moveMonth(this.viewDate, dir);
18251                 } else {
18252                     newDate = new Date(this.date);
18253                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18254                     newViewDate = new Date(this.viewDate);
18255                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18256                 }
18257                 if (this.dateWithinRange(newDate)){
18258                     this.date = newDate;
18259                     this.viewDate = newViewDate;
18260                     this.setValue(this.formatDate(this.date));
18261 //                    this.update();
18262                     e.preventDefault();
18263                     dateChanged = true;
18264                 }
18265                 break;
18266             case 38: // up
18267             case 40: // down
18268                 if (!this.keyboardNavigation) {
18269                     break;
18270                 }
18271                 dir = e.keyCode == 38 ? -1 : 1;
18272                 if (e.ctrlKey){
18273                     newDate = this.moveYear(this.date, dir);
18274                     newViewDate = this.moveYear(this.viewDate, dir);
18275                 } else if (e.shiftKey){
18276                     newDate = this.moveMonth(this.date, dir);
18277                     newViewDate = this.moveMonth(this.viewDate, dir);
18278                 } else {
18279                     newDate = new Date(this.date);
18280                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18281                     newViewDate = new Date(this.viewDate);
18282                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18283                 }
18284                 if (this.dateWithinRange(newDate)){
18285                     this.date = newDate;
18286                     this.viewDate = newViewDate;
18287                     this.setValue(this.formatDate(this.date));
18288 //                    this.update();
18289                     e.preventDefault();
18290                     dateChanged = true;
18291                 }
18292                 break;
18293             case 13: // enter
18294                 this.setValue(this.formatDate(this.date));
18295                 this.hide();
18296                 e.preventDefault();
18297                 break;
18298             case 9: // tab
18299                 this.setValue(this.formatDate(this.date));
18300                 this.hide();
18301                 break;
18302             case 16: // shift
18303             case 17: // ctrl
18304             case 18: // alt
18305                 break;
18306             default :
18307                 this.hide();
18308                 
18309         }
18310     },
18311     
18312     
18313     onClick: function(e) 
18314     {
18315         e.stopPropagation();
18316         e.preventDefault();
18317         
18318         var target = e.getTarget();
18319         
18320         if(target.nodeName.toLowerCase() === 'i'){
18321             target = Roo.get(target).dom.parentNode;
18322         }
18323         
18324         var nodeName = target.nodeName;
18325         var className = target.className;
18326         var html = target.innerHTML;
18327         //Roo.log(nodeName);
18328         
18329         switch(nodeName.toLowerCase()) {
18330             case 'th':
18331                 switch(className) {
18332                     case 'switch':
18333                         this.showMode(1);
18334                         break;
18335                     case 'prev':
18336                     case 'next':
18337                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18338                         switch(this.viewMode){
18339                                 case 0:
18340                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18341                                         break;
18342                                 case 1:
18343                                 case 2:
18344                                         this.viewDate = this.moveYear(this.viewDate, dir);
18345                                         break;
18346                         }
18347                         this.fill();
18348                         break;
18349                     case 'today':
18350                         var date = new Date();
18351                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18352 //                        this.fill()
18353                         this.setValue(this.formatDate(this.date));
18354                         
18355                         this.hide();
18356                         break;
18357                 }
18358                 break;
18359             case 'span':
18360                 if (className.indexOf('disabled') < 0) {
18361                     this.viewDate.setUTCDate(1);
18362                     if (className.indexOf('month') > -1) {
18363                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18364                     } else {
18365                         var year = parseInt(html, 10) || 0;
18366                         this.viewDate.setUTCFullYear(year);
18367                         
18368                     }
18369                     
18370                     if(this.singleMode){
18371                         this.setValue(this.formatDate(this.viewDate));
18372                         this.hide();
18373                         return;
18374                     }
18375                     
18376                     this.showMode(-1);
18377                     this.fill();
18378                 }
18379                 break;
18380                 
18381             case 'td':
18382                 //Roo.log(className);
18383                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18384                     var day = parseInt(html, 10) || 1;
18385                     var year = this.viewDate.getUTCFullYear(),
18386                         month = this.viewDate.getUTCMonth();
18387
18388                     if (className.indexOf('old') > -1) {
18389                         if(month === 0 ){
18390                             month = 11;
18391                             year -= 1;
18392                         }else{
18393                             month -= 1;
18394                         }
18395                     } else if (className.indexOf('new') > -1) {
18396                         if (month == 11) {
18397                             month = 0;
18398                             year += 1;
18399                         } else {
18400                             month += 1;
18401                         }
18402                     }
18403                     //Roo.log([year,month,day]);
18404                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18405                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18406 //                    this.fill();
18407                     //Roo.log(this.formatDate(this.date));
18408                     this.setValue(this.formatDate(this.date));
18409                     this.hide();
18410                 }
18411                 break;
18412         }
18413     },
18414     
18415     setStartDate: function(startDate)
18416     {
18417         this.startDate = startDate || -Infinity;
18418         if (this.startDate !== -Infinity) {
18419             this.startDate = this.parseDate(this.startDate);
18420         }
18421         this.update();
18422         this.updateNavArrows();
18423     },
18424
18425     setEndDate: function(endDate)
18426     {
18427         this.endDate = endDate || Infinity;
18428         if (this.endDate !== Infinity) {
18429             this.endDate = this.parseDate(this.endDate);
18430         }
18431         this.update();
18432         this.updateNavArrows();
18433     },
18434     
18435     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18436     {
18437         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18438         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18439             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18440         }
18441         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18442             return parseInt(d, 10);
18443         });
18444         this.update();
18445         this.updateNavArrows();
18446     },
18447     
18448     updateNavArrows: function() 
18449     {
18450         if(this.singleMode){
18451             return;
18452         }
18453         
18454         var d = new Date(this.viewDate),
18455         year = d.getUTCFullYear(),
18456         month = d.getUTCMonth();
18457         
18458         Roo.each(this.picker().select('.prev', true).elements, function(v){
18459             v.show();
18460             switch (this.viewMode) {
18461                 case 0:
18462
18463                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18464                         v.hide();
18465                     }
18466                     break;
18467                 case 1:
18468                 case 2:
18469                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18470                         v.hide();
18471                     }
18472                     break;
18473             }
18474         });
18475         
18476         Roo.each(this.picker().select('.next', true).elements, function(v){
18477             v.show();
18478             switch (this.viewMode) {
18479                 case 0:
18480
18481                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18482                         v.hide();
18483                     }
18484                     break;
18485                 case 1:
18486                 case 2:
18487                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18488                         v.hide();
18489                     }
18490                     break;
18491             }
18492         })
18493     },
18494     
18495     moveMonth: function(date, dir)
18496     {
18497         if (!dir) {
18498             return date;
18499         }
18500         var new_date = new Date(date.valueOf()),
18501         day = new_date.getUTCDate(),
18502         month = new_date.getUTCMonth(),
18503         mag = Math.abs(dir),
18504         new_month, test;
18505         dir = dir > 0 ? 1 : -1;
18506         if (mag == 1){
18507             test = dir == -1
18508             // If going back one month, make sure month is not current month
18509             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18510             ? function(){
18511                 return new_date.getUTCMonth() == month;
18512             }
18513             // If going forward one month, make sure month is as expected
18514             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18515             : function(){
18516                 return new_date.getUTCMonth() != new_month;
18517             };
18518             new_month = month + dir;
18519             new_date.setUTCMonth(new_month);
18520             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18521             if (new_month < 0 || new_month > 11) {
18522                 new_month = (new_month + 12) % 12;
18523             }
18524         } else {
18525             // For magnitudes >1, move one month at a time...
18526             for (var i=0; i<mag; i++) {
18527                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18528                 new_date = this.moveMonth(new_date, dir);
18529             }
18530             // ...then reset the day, keeping it in the new month
18531             new_month = new_date.getUTCMonth();
18532             new_date.setUTCDate(day);
18533             test = function(){
18534                 return new_month != new_date.getUTCMonth();
18535             };
18536         }
18537         // Common date-resetting loop -- if date is beyond end of month, make it
18538         // end of month
18539         while (test()){
18540             new_date.setUTCDate(--day);
18541             new_date.setUTCMonth(new_month);
18542         }
18543         return new_date;
18544     },
18545
18546     moveYear: function(date, dir)
18547     {
18548         return this.moveMonth(date, dir*12);
18549     },
18550
18551     dateWithinRange: function(date)
18552     {
18553         return date >= this.startDate && date <= this.endDate;
18554     },
18555
18556     
18557     remove: function() 
18558     {
18559         this.picker().remove();
18560     },
18561     
18562     validateValue : function(value)
18563     {
18564         if(value.length < 1)  {
18565             if(this.allowBlank){
18566                 return true;
18567             }
18568             return false;
18569         }
18570         
18571         if(value.length < this.minLength){
18572             return false;
18573         }
18574         if(value.length > this.maxLength){
18575             return false;
18576         }
18577         if(this.vtype){
18578             var vt = Roo.form.VTypes;
18579             if(!vt[this.vtype](value, this)){
18580                 return false;
18581             }
18582         }
18583         if(typeof this.validator == "function"){
18584             var msg = this.validator(value);
18585             if(msg !== true){
18586                 return false;
18587             }
18588         }
18589         
18590         if(this.regex && !this.regex.test(value)){
18591             return false;
18592         }
18593         
18594         if(typeof(this.parseDate(value)) == 'undefined'){
18595             return false;
18596         }
18597         
18598         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18599             return false;
18600         }      
18601         
18602         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18603             return false;
18604         } 
18605         
18606         
18607         return true;
18608     }
18609    
18610 });
18611
18612 Roo.apply(Roo.bootstrap.DateField,  {
18613     
18614     head : {
18615         tag: 'thead',
18616         cn: [
18617         {
18618             tag: 'tr',
18619             cn: [
18620             {
18621                 tag: 'th',
18622                 cls: 'prev',
18623                 html: '<i class="fa fa-arrow-left"/>'
18624             },
18625             {
18626                 tag: 'th',
18627                 cls: 'switch',
18628                 colspan: '5'
18629             },
18630             {
18631                 tag: 'th',
18632                 cls: 'next',
18633                 html: '<i class="fa fa-arrow-right"/>'
18634             }
18635
18636             ]
18637         }
18638         ]
18639     },
18640     
18641     content : {
18642         tag: 'tbody',
18643         cn: [
18644         {
18645             tag: 'tr',
18646             cn: [
18647             {
18648                 tag: 'td',
18649                 colspan: '7'
18650             }
18651             ]
18652         }
18653         ]
18654     },
18655     
18656     footer : {
18657         tag: 'tfoot',
18658         cn: [
18659         {
18660             tag: 'tr',
18661             cn: [
18662             {
18663                 tag: 'th',
18664                 colspan: '7',
18665                 cls: 'today'
18666             }
18667                     
18668             ]
18669         }
18670         ]
18671     },
18672     
18673     dates:{
18674         en: {
18675             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18676             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18677             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18678             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18679             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18680             today: "Today"
18681         }
18682     },
18683     
18684     modes: [
18685     {
18686         clsName: 'days',
18687         navFnc: 'Month',
18688         navStep: 1
18689     },
18690     {
18691         clsName: 'months',
18692         navFnc: 'FullYear',
18693         navStep: 1
18694     },
18695     {
18696         clsName: 'years',
18697         navFnc: 'FullYear',
18698         navStep: 10
18699     }]
18700 });
18701
18702 Roo.apply(Roo.bootstrap.DateField,  {
18703   
18704     template : {
18705         tag: 'div',
18706         cls: 'datepicker dropdown-menu roo-dynamic',
18707         cn: [
18708         {
18709             tag: 'div',
18710             cls: 'datepicker-days',
18711             cn: [
18712             {
18713                 tag: 'table',
18714                 cls: 'table-condensed',
18715                 cn:[
18716                 Roo.bootstrap.DateField.head,
18717                 {
18718                     tag: 'tbody'
18719                 },
18720                 Roo.bootstrap.DateField.footer
18721                 ]
18722             }
18723             ]
18724         },
18725         {
18726             tag: 'div',
18727             cls: 'datepicker-months',
18728             cn: [
18729             {
18730                 tag: 'table',
18731                 cls: 'table-condensed',
18732                 cn:[
18733                 Roo.bootstrap.DateField.head,
18734                 Roo.bootstrap.DateField.content,
18735                 Roo.bootstrap.DateField.footer
18736                 ]
18737             }
18738             ]
18739         },
18740         {
18741             tag: 'div',
18742             cls: 'datepicker-years',
18743             cn: [
18744             {
18745                 tag: 'table',
18746                 cls: 'table-condensed',
18747                 cn:[
18748                 Roo.bootstrap.DateField.head,
18749                 Roo.bootstrap.DateField.content,
18750                 Roo.bootstrap.DateField.footer
18751                 ]
18752             }
18753             ]
18754         }
18755         ]
18756     }
18757 });
18758
18759  
18760
18761  /*
18762  * - LGPL
18763  *
18764  * TimeField
18765  * 
18766  */
18767
18768 /**
18769  * @class Roo.bootstrap.TimeField
18770  * @extends Roo.bootstrap.Input
18771  * Bootstrap DateField class
18772  * 
18773  * 
18774  * @constructor
18775  * Create a new TimeField
18776  * @param {Object} config The config object
18777  */
18778
18779 Roo.bootstrap.TimeField = function(config){
18780     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18781     this.addEvents({
18782             /**
18783              * @event show
18784              * Fires when this field show.
18785              * @param {Roo.bootstrap.DateField} thisthis
18786              * @param {Mixed} date The date value
18787              */
18788             show : true,
18789             /**
18790              * @event show
18791              * Fires when this field hide.
18792              * @param {Roo.bootstrap.DateField} this
18793              * @param {Mixed} date The date value
18794              */
18795             hide : true,
18796             /**
18797              * @event select
18798              * Fires when select a date.
18799              * @param {Roo.bootstrap.DateField} this
18800              * @param {Mixed} date The date value
18801              */
18802             select : true
18803         });
18804 };
18805
18806 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18807     
18808     /**
18809      * @cfg {String} format
18810      * The default time format string which can be overriden for localization support.  The format must be
18811      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18812      */
18813     format : "H:i",
18814        
18815     onRender: function(ct, position)
18816     {
18817         
18818         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18819                 
18820         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18821         
18822         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18823         
18824         this.pop = this.picker().select('>.datepicker-time',true).first();
18825         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18826         
18827         this.picker().on('mousedown', this.onMousedown, this);
18828         this.picker().on('click', this.onClick, this);
18829         
18830         this.picker().addClass('datepicker-dropdown');
18831     
18832         this.fillTime();
18833         this.update();
18834             
18835         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18836         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18837         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18838         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18839         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18840         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18841
18842     },
18843     
18844     fireKey: function(e){
18845         if (!this.picker().isVisible()){
18846             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18847                 this.show();
18848             }
18849             return;
18850         }
18851
18852         e.preventDefault();
18853         
18854         switch(e.keyCode){
18855             case 27: // escape
18856                 this.hide();
18857                 break;
18858             case 37: // left
18859             case 39: // right
18860                 this.onTogglePeriod();
18861                 break;
18862             case 38: // up
18863                 this.onIncrementMinutes();
18864                 break;
18865             case 40: // down
18866                 this.onDecrementMinutes();
18867                 break;
18868             case 13: // enter
18869             case 9: // tab
18870                 this.setTime();
18871                 break;
18872         }
18873     },
18874     
18875     onClick: function(e) {
18876         e.stopPropagation();
18877         e.preventDefault();
18878     },
18879     
18880     picker : function()
18881     {
18882         return this.el.select('.datepicker', true).first();
18883     },
18884     
18885     fillTime: function()
18886     {    
18887         var time = this.pop.select('tbody', true).first();
18888         
18889         time.dom.innerHTML = '';
18890         
18891         time.createChild({
18892             tag: 'tr',
18893             cn: [
18894                 {
18895                     tag: 'td',
18896                     cn: [
18897                         {
18898                             tag: 'a',
18899                             href: '#',
18900                             cls: 'btn',
18901                             cn: [
18902                                 {
18903                                     tag: 'span',
18904                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18905                                 }
18906                             ]
18907                         } 
18908                     ]
18909                 },
18910                 {
18911                     tag: 'td',
18912                     cls: 'separator'
18913                 },
18914                 {
18915                     tag: 'td',
18916                     cn: [
18917                         {
18918                             tag: 'a',
18919                             href: '#',
18920                             cls: 'btn',
18921                             cn: [
18922                                 {
18923                                     tag: 'span',
18924                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18925                                 }
18926                             ]
18927                         }
18928                     ]
18929                 },
18930                 {
18931                     tag: 'td',
18932                     cls: 'separator'
18933                 }
18934             ]
18935         });
18936         
18937         time.createChild({
18938             tag: 'tr',
18939             cn: [
18940                 {
18941                     tag: 'td',
18942                     cn: [
18943                         {
18944                             tag: 'span',
18945                             cls: 'timepicker-hour',
18946                             html: '00'
18947                         }  
18948                     ]
18949                 },
18950                 {
18951                     tag: 'td',
18952                     cls: 'separator',
18953                     html: ':'
18954                 },
18955                 {
18956                     tag: 'td',
18957                     cn: [
18958                         {
18959                             tag: 'span',
18960                             cls: 'timepicker-minute',
18961                             html: '00'
18962                         }  
18963                     ]
18964                 },
18965                 {
18966                     tag: 'td',
18967                     cls: 'separator'
18968                 },
18969                 {
18970                     tag: 'td',
18971                     cn: [
18972                         {
18973                             tag: 'button',
18974                             type: 'button',
18975                             cls: 'btn btn-primary period',
18976                             html: 'AM'
18977                             
18978                         }
18979                     ]
18980                 }
18981             ]
18982         });
18983         
18984         time.createChild({
18985             tag: 'tr',
18986             cn: [
18987                 {
18988                     tag: 'td',
18989                     cn: [
18990                         {
18991                             tag: 'a',
18992                             href: '#',
18993                             cls: 'btn',
18994                             cn: [
18995                                 {
18996                                     tag: 'span',
18997                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18998                                 }
18999                             ]
19000                         }
19001                     ]
19002                 },
19003                 {
19004                     tag: 'td',
19005                     cls: 'separator'
19006                 },
19007                 {
19008                     tag: 'td',
19009                     cn: [
19010                         {
19011                             tag: 'a',
19012                             href: '#',
19013                             cls: 'btn',
19014                             cn: [
19015                                 {
19016                                     tag: 'span',
19017                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19018                                 }
19019                             ]
19020                         }
19021                     ]
19022                 },
19023                 {
19024                     tag: 'td',
19025                     cls: 'separator'
19026                 }
19027             ]
19028         });
19029         
19030     },
19031     
19032     update: function()
19033     {
19034         
19035         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19036         
19037         this.fill();
19038     },
19039     
19040     fill: function() 
19041     {
19042         var hours = this.time.getHours();
19043         var minutes = this.time.getMinutes();
19044         var period = 'AM';
19045         
19046         if(hours > 11){
19047             period = 'PM';
19048         }
19049         
19050         if(hours == 0){
19051             hours = 12;
19052         }
19053         
19054         
19055         if(hours > 12){
19056             hours = hours - 12;
19057         }
19058         
19059         if(hours < 10){
19060             hours = '0' + hours;
19061         }
19062         
19063         if(minutes < 10){
19064             minutes = '0' + minutes;
19065         }
19066         
19067         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19068         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19069         this.pop.select('button', true).first().dom.innerHTML = period;
19070         
19071     },
19072     
19073     place: function()
19074     {   
19075         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19076         
19077         var cls = ['bottom'];
19078         
19079         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19080             cls.pop();
19081             cls.push('top');
19082         }
19083         
19084         cls.push('right');
19085         
19086         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19087             cls.pop();
19088             cls.push('left');
19089         }
19090         
19091         this.picker().addClass(cls.join('-'));
19092         
19093         var _this = this;
19094         
19095         Roo.each(cls, function(c){
19096             if(c == 'bottom'){
19097                 _this.picker().setTop(_this.inputEl().getHeight());
19098                 return;
19099             }
19100             if(c == 'top'){
19101                 _this.picker().setTop(0 - _this.picker().getHeight());
19102                 return;
19103             }
19104             
19105             if(c == 'left'){
19106                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19107                 return;
19108             }
19109             if(c == 'right'){
19110                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19111                 return;
19112             }
19113         });
19114         
19115     },
19116   
19117     onFocus : function()
19118     {
19119         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19120         this.show();
19121     },
19122     
19123     onBlur : function()
19124     {
19125         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19126         this.hide();
19127     },
19128     
19129     show : function()
19130     {
19131         this.picker().show();
19132         this.pop.show();
19133         this.update();
19134         this.place();
19135         
19136         this.fireEvent('show', this, this.date);
19137     },
19138     
19139     hide : function()
19140     {
19141         this.picker().hide();
19142         this.pop.hide();
19143         
19144         this.fireEvent('hide', this, this.date);
19145     },
19146     
19147     setTime : function()
19148     {
19149         this.hide();
19150         this.setValue(this.time.format(this.format));
19151         
19152         this.fireEvent('select', this, this.date);
19153         
19154         
19155     },
19156     
19157     onMousedown: function(e){
19158         e.stopPropagation();
19159         e.preventDefault();
19160     },
19161     
19162     onIncrementHours: function()
19163     {
19164         Roo.log('onIncrementHours');
19165         this.time = this.time.add(Date.HOUR, 1);
19166         this.update();
19167         
19168     },
19169     
19170     onDecrementHours: function()
19171     {
19172         Roo.log('onDecrementHours');
19173         this.time = this.time.add(Date.HOUR, -1);
19174         this.update();
19175     },
19176     
19177     onIncrementMinutes: function()
19178     {
19179         Roo.log('onIncrementMinutes');
19180         this.time = this.time.add(Date.MINUTE, 1);
19181         this.update();
19182     },
19183     
19184     onDecrementMinutes: function()
19185     {
19186         Roo.log('onDecrementMinutes');
19187         this.time = this.time.add(Date.MINUTE, -1);
19188         this.update();
19189     },
19190     
19191     onTogglePeriod: function()
19192     {
19193         Roo.log('onTogglePeriod');
19194         this.time = this.time.add(Date.HOUR, 12);
19195         this.update();
19196     }
19197     
19198    
19199 });
19200
19201 Roo.apply(Roo.bootstrap.TimeField,  {
19202     
19203     content : {
19204         tag: 'tbody',
19205         cn: [
19206             {
19207                 tag: 'tr',
19208                 cn: [
19209                 {
19210                     tag: 'td',
19211                     colspan: '7'
19212                 }
19213                 ]
19214             }
19215         ]
19216     },
19217     
19218     footer : {
19219         tag: 'tfoot',
19220         cn: [
19221             {
19222                 tag: 'tr',
19223                 cn: [
19224                 {
19225                     tag: 'th',
19226                     colspan: '7',
19227                     cls: '',
19228                     cn: [
19229                         {
19230                             tag: 'button',
19231                             cls: 'btn btn-info ok',
19232                             html: 'OK'
19233                         }
19234                     ]
19235                 }
19236
19237                 ]
19238             }
19239         ]
19240     }
19241 });
19242
19243 Roo.apply(Roo.bootstrap.TimeField,  {
19244   
19245     template : {
19246         tag: 'div',
19247         cls: 'datepicker dropdown-menu',
19248         cn: [
19249             {
19250                 tag: 'div',
19251                 cls: 'datepicker-time',
19252                 cn: [
19253                 {
19254                     tag: 'table',
19255                     cls: 'table-condensed',
19256                     cn:[
19257                     Roo.bootstrap.TimeField.content,
19258                     Roo.bootstrap.TimeField.footer
19259                     ]
19260                 }
19261                 ]
19262             }
19263         ]
19264     }
19265 });
19266
19267  
19268
19269  /*
19270  * - LGPL
19271  *
19272  * MonthField
19273  * 
19274  */
19275
19276 /**
19277  * @class Roo.bootstrap.MonthField
19278  * @extends Roo.bootstrap.Input
19279  * Bootstrap MonthField class
19280  * 
19281  * @cfg {String} language default en
19282  * 
19283  * @constructor
19284  * Create a new MonthField
19285  * @param {Object} config The config object
19286  */
19287
19288 Roo.bootstrap.MonthField = function(config){
19289     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19290     
19291     this.addEvents({
19292         /**
19293          * @event show
19294          * Fires when this field show.
19295          * @param {Roo.bootstrap.MonthField} this
19296          * @param {Mixed} date The date value
19297          */
19298         show : true,
19299         /**
19300          * @event show
19301          * Fires when this field hide.
19302          * @param {Roo.bootstrap.MonthField} this
19303          * @param {Mixed} date The date value
19304          */
19305         hide : true,
19306         /**
19307          * @event select
19308          * Fires when select a date.
19309          * @param {Roo.bootstrap.MonthField} this
19310          * @param {String} oldvalue The old value
19311          * @param {String} newvalue The new value
19312          */
19313         select : true
19314     });
19315 };
19316
19317 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19318     
19319     onRender: function(ct, position)
19320     {
19321         
19322         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19323         
19324         this.language = this.language || 'en';
19325         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19326         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19327         
19328         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19329         this.isInline = false;
19330         this.isInput = true;
19331         this.component = this.el.select('.add-on', true).first() || false;
19332         this.component = (this.component && this.component.length === 0) ? false : this.component;
19333         this.hasInput = this.component && this.inputEL().length;
19334         
19335         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19336         
19337         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19338         
19339         this.picker().on('mousedown', this.onMousedown, this);
19340         this.picker().on('click', this.onClick, this);
19341         
19342         this.picker().addClass('datepicker-dropdown');
19343         
19344         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19345             v.setStyle('width', '189px');
19346         });
19347         
19348         this.fillMonths();
19349         
19350         this.update();
19351         
19352         if(this.isInline) {
19353             this.show();
19354         }
19355         
19356     },
19357     
19358     setValue: function(v, suppressEvent)
19359     {   
19360         var o = this.getValue();
19361         
19362         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19363         
19364         this.update();
19365
19366         if(suppressEvent !== true){
19367             this.fireEvent('select', this, o, v);
19368         }
19369         
19370     },
19371     
19372     getValue: function()
19373     {
19374         return this.value;
19375     },
19376     
19377     onClick: function(e) 
19378     {
19379         e.stopPropagation();
19380         e.preventDefault();
19381         
19382         var target = e.getTarget();
19383         
19384         if(target.nodeName.toLowerCase() === 'i'){
19385             target = Roo.get(target).dom.parentNode;
19386         }
19387         
19388         var nodeName = target.nodeName;
19389         var className = target.className;
19390         var html = target.innerHTML;
19391         
19392         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19393             return;
19394         }
19395         
19396         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19397         
19398         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19399         
19400         this.hide();
19401                         
19402     },
19403     
19404     picker : function()
19405     {
19406         return this.pickerEl;
19407     },
19408     
19409     fillMonths: function()
19410     {    
19411         var i = 0;
19412         var months = this.picker().select('>.datepicker-months td', true).first();
19413         
19414         months.dom.innerHTML = '';
19415         
19416         while (i < 12) {
19417             var month = {
19418                 tag: 'span',
19419                 cls: 'month',
19420                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19421             };
19422             
19423             months.createChild(month);
19424         }
19425         
19426     },
19427     
19428     update: function()
19429     {
19430         var _this = this;
19431         
19432         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19433             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19434         }
19435         
19436         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19437             e.removeClass('active');
19438             
19439             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19440                 e.addClass('active');
19441             }
19442         })
19443     },
19444     
19445     place: function()
19446     {
19447         if(this.isInline) {
19448             return;
19449         }
19450         
19451         this.picker().removeClass(['bottom', 'top']);
19452         
19453         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19454             /*
19455              * place to the top of element!
19456              *
19457              */
19458             
19459             this.picker().addClass('top');
19460             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19461             
19462             return;
19463         }
19464         
19465         this.picker().addClass('bottom');
19466         
19467         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19468     },
19469     
19470     onFocus : function()
19471     {
19472         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19473         this.show();
19474     },
19475     
19476     onBlur : function()
19477     {
19478         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19479         
19480         var d = this.inputEl().getValue();
19481         
19482         this.setValue(d);
19483                 
19484         this.hide();
19485     },
19486     
19487     show : function()
19488     {
19489         this.picker().show();
19490         this.picker().select('>.datepicker-months', true).first().show();
19491         this.update();
19492         this.place();
19493         
19494         this.fireEvent('show', this, this.date);
19495     },
19496     
19497     hide : function()
19498     {
19499         if(this.isInline) {
19500             return;
19501         }
19502         this.picker().hide();
19503         this.fireEvent('hide', this, this.date);
19504         
19505     },
19506     
19507     onMousedown: function(e)
19508     {
19509         e.stopPropagation();
19510         e.preventDefault();
19511     },
19512     
19513     keyup: function(e)
19514     {
19515         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19516         this.update();
19517     },
19518
19519     fireKey: function(e)
19520     {
19521         if (!this.picker().isVisible()){
19522             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19523                 this.show();
19524             }
19525             return;
19526         }
19527         
19528         var dir;
19529         
19530         switch(e.keyCode){
19531             case 27: // escape
19532                 this.hide();
19533                 e.preventDefault();
19534                 break;
19535             case 37: // left
19536             case 39: // right
19537                 dir = e.keyCode == 37 ? -1 : 1;
19538                 
19539                 this.vIndex = this.vIndex + dir;
19540                 
19541                 if(this.vIndex < 0){
19542                     this.vIndex = 0;
19543                 }
19544                 
19545                 if(this.vIndex > 11){
19546                     this.vIndex = 11;
19547                 }
19548                 
19549                 if(isNaN(this.vIndex)){
19550                     this.vIndex = 0;
19551                 }
19552                 
19553                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19554                 
19555                 break;
19556             case 38: // up
19557             case 40: // down
19558                 
19559                 dir = e.keyCode == 38 ? -1 : 1;
19560                 
19561                 this.vIndex = this.vIndex + dir * 4;
19562                 
19563                 if(this.vIndex < 0){
19564                     this.vIndex = 0;
19565                 }
19566                 
19567                 if(this.vIndex > 11){
19568                     this.vIndex = 11;
19569                 }
19570                 
19571                 if(isNaN(this.vIndex)){
19572                     this.vIndex = 0;
19573                 }
19574                 
19575                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19576                 break;
19577                 
19578             case 13: // enter
19579                 
19580                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19581                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19582                 }
19583                 
19584                 this.hide();
19585                 e.preventDefault();
19586                 break;
19587             case 9: // tab
19588                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19589                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19590                 }
19591                 this.hide();
19592                 break;
19593             case 16: // shift
19594             case 17: // ctrl
19595             case 18: // alt
19596                 break;
19597             default :
19598                 this.hide();
19599                 
19600         }
19601     },
19602     
19603     remove: function() 
19604     {
19605         this.picker().remove();
19606     }
19607    
19608 });
19609
19610 Roo.apply(Roo.bootstrap.MonthField,  {
19611     
19612     content : {
19613         tag: 'tbody',
19614         cn: [
19615         {
19616             tag: 'tr',
19617             cn: [
19618             {
19619                 tag: 'td',
19620                 colspan: '7'
19621             }
19622             ]
19623         }
19624         ]
19625     },
19626     
19627     dates:{
19628         en: {
19629             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19630             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19631         }
19632     }
19633 });
19634
19635 Roo.apply(Roo.bootstrap.MonthField,  {
19636   
19637     template : {
19638         tag: 'div',
19639         cls: 'datepicker dropdown-menu roo-dynamic',
19640         cn: [
19641             {
19642                 tag: 'div',
19643                 cls: 'datepicker-months',
19644                 cn: [
19645                 {
19646                     tag: 'table',
19647                     cls: 'table-condensed',
19648                     cn:[
19649                         Roo.bootstrap.DateField.content
19650                     ]
19651                 }
19652                 ]
19653             }
19654         ]
19655     }
19656 });
19657
19658  
19659
19660  
19661  /*
19662  * - LGPL
19663  *
19664  * CheckBox
19665  * 
19666  */
19667
19668 /**
19669  * @class Roo.bootstrap.CheckBox
19670  * @extends Roo.bootstrap.Input
19671  * Bootstrap CheckBox class
19672  * 
19673  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19674  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19675  * @cfg {String} boxLabel The text that appears beside the checkbox
19676  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19677  * @cfg {Boolean} checked initnal the element
19678  * @cfg {Boolean} inline inline the element (default false)
19679  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19680  * 
19681  * @constructor
19682  * Create a new CheckBox
19683  * @param {Object} config The config object
19684  */
19685
19686 Roo.bootstrap.CheckBox = function(config){
19687     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19688    
19689     this.addEvents({
19690         /**
19691         * @event check
19692         * Fires when the element is checked or unchecked.
19693         * @param {Roo.bootstrap.CheckBox} this This input
19694         * @param {Boolean} checked The new checked value
19695         */
19696        check : true
19697     });
19698     
19699 };
19700
19701 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19702   
19703     inputType: 'checkbox',
19704     inputValue: 1,
19705     valueOff: 0,
19706     boxLabel: false,
19707     checked: false,
19708     weight : false,
19709     inline: false,
19710     
19711     getAutoCreate : function()
19712     {
19713         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19714         
19715         var id = Roo.id();
19716         
19717         var cfg = {};
19718         
19719         cfg.cls = 'form-group ' + this.inputType; //input-group
19720         
19721         if(this.inline){
19722             cfg.cls += ' ' + this.inputType + '-inline';
19723         }
19724         
19725         var input =  {
19726             tag: 'input',
19727             id : id,
19728             type : this.inputType,
19729             value : this.inputValue,
19730             cls : 'roo-' + this.inputType, //'form-box',
19731             placeholder : this.placeholder || ''
19732             
19733         };
19734         
19735         if(this.inputType != 'radio'){
19736             var hidden =  {
19737                 tag: 'input',
19738                 type : 'hidden',
19739                 cls : 'roo-hidden-value',
19740                 value : this.checked ? this.valueOff : this.inputValue
19741             };
19742         }
19743         
19744             
19745         if (this.weight) { // Validity check?
19746             cfg.cls += " " + this.inputType + "-" + this.weight;
19747         }
19748         
19749         if (this.disabled) {
19750             input.disabled=true;
19751         }
19752         
19753         if(this.checked){
19754             input.checked = this.checked;
19755             
19756         }
19757         
19758         
19759         if (this.name) {
19760             
19761             input.name = this.name;
19762             
19763             if(this.inputType != 'radio'){
19764                 hidden.name = this.name;
19765                 input.name = '_hidden_' + this.name;
19766             }
19767         }
19768         
19769         if (this.size) {
19770             input.cls += ' input-' + this.size;
19771         }
19772         
19773         var settings=this;
19774         
19775         ['xs','sm','md','lg'].map(function(size){
19776             if (settings[size]) {
19777                 cfg.cls += ' col-' + size + '-' + settings[size];
19778             }
19779         });
19780         
19781         var inputblock = input;
19782          
19783         if (this.before || this.after) {
19784             
19785             inputblock = {
19786                 cls : 'input-group',
19787                 cn :  [] 
19788             };
19789             
19790             if (this.before) {
19791                 inputblock.cn.push({
19792                     tag :'span',
19793                     cls : 'input-group-addon',
19794                     html : this.before
19795                 });
19796             }
19797             
19798             inputblock.cn.push(input);
19799             
19800             if(this.inputType != 'radio'){
19801                 inputblock.cn.push(hidden);
19802             }
19803             
19804             if (this.after) {
19805                 inputblock.cn.push({
19806                     tag :'span',
19807                     cls : 'input-group-addon',
19808                     html : this.after
19809                 });
19810             }
19811             
19812         }
19813         
19814         if (align ==='left' && this.fieldLabel.length) {
19815 //                Roo.log("left and has label");
19816                 cfg.cn = [
19817                     
19818                     {
19819                         tag: 'label',
19820                         'for' :  id,
19821                         cls : 'control-label col-md-' + this.labelWidth,
19822                         html : this.fieldLabel
19823                         
19824                     },
19825                     {
19826                         cls : "col-md-" + (12 - this.labelWidth), 
19827                         cn: [
19828                             inputblock
19829                         ]
19830                     }
19831                     
19832                 ];
19833         } else if ( this.fieldLabel.length) {
19834 //                Roo.log(" label");
19835                 cfg.cn = [
19836                    
19837                     {
19838                         tag: this.boxLabel ? 'span' : 'label',
19839                         'for': id,
19840                         cls: 'control-label box-input-label',
19841                         //cls : 'input-group-addon',
19842                         html : this.fieldLabel
19843                         
19844                     },
19845                     
19846                     inputblock
19847                     
19848                 ];
19849
19850         } else {
19851             
19852 //                Roo.log(" no label && no align");
19853                 cfg.cn = [  inputblock ] ;
19854                 
19855                 
19856         }
19857         
19858         if(this.boxLabel){
19859              var boxLabelCfg = {
19860                 tag: 'label',
19861                 //'for': id, // box label is handled by onclick - so no for...
19862                 cls: 'box-label',
19863                 html: this.boxLabel
19864             };
19865             
19866             if(this.tooltip){
19867                 boxLabelCfg.tooltip = this.tooltip;
19868             }
19869              
19870             cfg.cn.push(boxLabelCfg);
19871         }
19872         
19873         if(this.inputType != 'radio'){
19874             cfg.cn.push(hidden);
19875         }
19876         
19877         return cfg;
19878         
19879     },
19880     
19881     /**
19882      * return the real input element.
19883      */
19884     inputEl: function ()
19885     {
19886         return this.el.select('input.roo-' + this.inputType,true).first();
19887     },
19888     hiddenEl: function ()
19889     {
19890         return this.el.select('input.roo-hidden-value',true).first();
19891     },
19892     
19893     labelEl: function()
19894     {
19895         return this.el.select('label.control-label',true).first();
19896     },
19897     /* depricated... */
19898     
19899     label: function()
19900     {
19901         return this.labelEl();
19902     },
19903     
19904     boxLabelEl: function()
19905     {
19906         return this.el.select('label.box-label',true).first();
19907     },
19908     
19909     initEvents : function()
19910     {
19911 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19912         
19913         this.inputEl().on('click', this.onClick,  this);
19914         
19915         if (this.boxLabel) { 
19916             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19917         }
19918         
19919         this.startValue = this.getValue();
19920         
19921         if(this.groupId){
19922             Roo.bootstrap.CheckBox.register(this);
19923         }
19924     },
19925     
19926     onClick : function()
19927     {   
19928         this.setChecked(!this.checked);
19929     },
19930     
19931     setChecked : function(state,suppressEvent)
19932     {
19933         this.startValue = this.getValue();
19934
19935         if(this.inputType == 'radio'){
19936             
19937             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19938                 e.dom.checked = false;
19939             });
19940             
19941             this.inputEl().dom.checked = true;
19942             
19943             this.inputEl().dom.value = this.inputValue;
19944             
19945             if(suppressEvent !== true){
19946                 this.fireEvent('check', this, true);
19947             }
19948             
19949             this.validate();
19950             
19951             return;
19952         }
19953         
19954         this.checked = state;
19955         
19956         this.inputEl().dom.checked = state;
19957         
19958         
19959         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19960         
19961         if(suppressEvent !== true){
19962             this.fireEvent('check', this, state);
19963         }
19964         
19965         this.validate();
19966     },
19967     
19968     getValue : function()
19969     {
19970         if(this.inputType == 'radio'){
19971             return this.getGroupValue();
19972         }
19973         
19974         return this.hiddenEl().dom.value;
19975         
19976     },
19977     
19978     getGroupValue : function()
19979     {
19980         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19981             return '';
19982         }
19983         
19984         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19985     },
19986     
19987     setValue : function(v,suppressEvent)
19988     {
19989         if(this.inputType == 'radio'){
19990             this.setGroupValue(v, suppressEvent);
19991             return;
19992         }
19993         
19994         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19995         
19996         this.validate();
19997     },
19998     
19999     setGroupValue : function(v, suppressEvent)
20000     {
20001         this.startValue = this.getValue();
20002         
20003         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20004             e.dom.checked = false;
20005             
20006             if(e.dom.value == v){
20007                 e.dom.checked = true;
20008             }
20009         });
20010         
20011         if(suppressEvent !== true){
20012             this.fireEvent('check', this, true);
20013         }
20014
20015         this.validate();
20016         
20017         return;
20018     },
20019     
20020     validate : function()
20021     {
20022         if(
20023                 this.disabled || 
20024                 (this.inputType == 'radio' && this.validateRadio()) ||
20025                 (this.inputType == 'checkbox' && this.validateCheckbox())
20026         ){
20027             this.markValid();
20028             return true;
20029         }
20030         
20031         this.markInvalid();
20032         return false;
20033     },
20034     
20035     validateRadio : function()
20036     {
20037         if(this.allowBlank){
20038             return true;
20039         }
20040         
20041         var valid = false;
20042         
20043         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20044             if(!e.dom.checked){
20045                 return;
20046             }
20047             
20048             valid = true;
20049             
20050             return false;
20051         });
20052         
20053         return valid;
20054     },
20055     
20056     validateCheckbox : function()
20057     {
20058         if(!this.groupId){
20059             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20060         }
20061         
20062         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20063         
20064         if(!group){
20065             return false;
20066         }
20067         
20068         var r = false;
20069         
20070         for(var i in group){
20071             if(r){
20072                 break;
20073             }
20074             
20075             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20076         }
20077         
20078         return r;
20079     },
20080     
20081     /**
20082      * Mark this field as valid
20083      */
20084     markValid : function()
20085     {
20086         var _this = this;
20087         
20088         this.fireEvent('valid', this);
20089         
20090         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20091         
20092         if(this.groupId){
20093             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20094         }
20095         
20096         if(label){
20097             label.markValid();
20098         }
20099
20100         if(this.inputType == 'radio'){
20101             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20102                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20103                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20104             });
20105             
20106             return;
20107         }
20108         
20109         if(!this.groupId){
20110             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20111             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20112             return;
20113         }
20114         
20115         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20116             
20117         if(!group){
20118             return;
20119         }
20120         
20121         for(var i in group){
20122             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20123             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20124         }
20125     },
20126     
20127      /**
20128      * Mark this field as invalid
20129      * @param {String} msg The validation message
20130      */
20131     markInvalid : function(msg)
20132     {
20133         if(this.allowBlank){
20134             return;
20135         }
20136         
20137         var _this = this;
20138         
20139         this.fireEvent('invalid', this, msg);
20140         
20141         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20142         
20143         if(this.groupId){
20144             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20145         }
20146         
20147         if(label){
20148             label.markInvalid();
20149         }
20150             
20151         if(this.inputType == 'radio'){
20152             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20153                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20154                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20155             });
20156             
20157             return;
20158         }
20159         
20160         if(!this.groupId){
20161             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20162             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20163             return;
20164         }
20165         
20166         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20167         
20168         if(!group){
20169             return;
20170         }
20171         
20172         for(var i in group){
20173             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20174             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20175         }
20176         
20177     },
20178     
20179     clearInvalid : function()
20180     {
20181         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20182         
20183         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20184         
20185         if (label) {
20186             label.iconEl.removeClass(label.validClass);
20187             label.iconEl.removeClass(label.invalidClass);
20188         }
20189     },
20190     
20191     disable : function()
20192     {
20193         if(this.inputType != 'radio'){
20194             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20195             return;
20196         }
20197         
20198         var _this = this;
20199         
20200         if(this.rendered){
20201             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20202                 _this.getActionEl().addClass(this.disabledClass);
20203                 e.dom.disabled = true;
20204             });
20205         }
20206         
20207         this.disabled = true;
20208         this.fireEvent("disable", this);
20209         return this;
20210     },
20211
20212     enable : function()
20213     {
20214         if(this.inputType != 'radio'){
20215             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20216             return;
20217         }
20218         
20219         var _this = this;
20220         
20221         if(this.rendered){
20222             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20223                 _this.getActionEl().removeClass(this.disabledClass);
20224                 e.dom.disabled = false;
20225             });
20226         }
20227         
20228         this.disabled = false;
20229         this.fireEvent("enable", this);
20230         return this;
20231     }
20232
20233 });
20234
20235 Roo.apply(Roo.bootstrap.CheckBox, {
20236     
20237     groups: {},
20238     
20239      /**
20240     * register a CheckBox Group
20241     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20242     */
20243     register : function(checkbox)
20244     {
20245         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20246             this.groups[checkbox.groupId] = {};
20247         }
20248         
20249         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20250             return;
20251         }
20252         
20253         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20254         
20255     },
20256     /**
20257     * fetch a CheckBox Group based on the group ID
20258     * @param {string} the group ID
20259     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20260     */
20261     get: function(groupId) {
20262         if (typeof(this.groups[groupId]) == 'undefined') {
20263             return false;
20264         }
20265         
20266         return this.groups[groupId] ;
20267     }
20268     
20269     
20270 });
20271 /*
20272  * - LGPL
20273  *
20274  * RadioItem
20275  * 
20276  */
20277
20278 /**
20279  * @class Roo.bootstrap.Radio
20280  * @extends Roo.bootstrap.Component
20281  * Bootstrap Radio class
20282  * @cfg {String} boxLabel - the label associated
20283  * @cfg {String} value - the value of radio
20284  * 
20285  * @constructor
20286  * Create a new Radio
20287  * @param {Object} config The config object
20288  */
20289 Roo.bootstrap.Radio = function(config){
20290     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20291     
20292 };
20293
20294 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20295     
20296     boxLabel : '',
20297     
20298     value : '',
20299     
20300     getAutoCreate : function()
20301     {
20302         var cfg = {
20303             tag : 'div',
20304             cls : 'form-group radio',
20305             cn : [
20306                 {
20307                     tag : 'label',
20308                     cls : 'box-label',
20309                     html : this.boxLabel
20310                 }
20311             ]
20312         };
20313         
20314         return cfg;
20315     },
20316     
20317     initEvents : function() 
20318     {
20319         this.parent().register(this);
20320         
20321         this.el.on('click', this.onClick, this);
20322         
20323     },
20324     
20325     onClick : function()
20326     {
20327         this.setChecked(true);
20328     },
20329     
20330     setChecked : function(state, suppressEvent)
20331     {
20332         this.parent().setValue(this.value, suppressEvent);
20333         
20334     }
20335     
20336 });
20337  
20338
20339  //<script type="text/javascript">
20340
20341 /*
20342  * Based  Ext JS Library 1.1.1
20343  * Copyright(c) 2006-2007, Ext JS, LLC.
20344  * LGPL
20345  *
20346  */
20347  
20348 /**
20349  * @class Roo.HtmlEditorCore
20350  * @extends Roo.Component
20351  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20352  *
20353  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20354  */
20355
20356 Roo.HtmlEditorCore = function(config){
20357     
20358     
20359     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20360     
20361     
20362     this.addEvents({
20363         /**
20364          * @event initialize
20365          * Fires when the editor is fully initialized (including the iframe)
20366          * @param {Roo.HtmlEditorCore} this
20367          */
20368         initialize: true,
20369         /**
20370          * @event activate
20371          * Fires when the editor is first receives the focus. Any insertion must wait
20372          * until after this event.
20373          * @param {Roo.HtmlEditorCore} this
20374          */
20375         activate: true,
20376          /**
20377          * @event beforesync
20378          * Fires before the textarea is updated with content from the editor iframe. Return false
20379          * to cancel the sync.
20380          * @param {Roo.HtmlEditorCore} this
20381          * @param {String} html
20382          */
20383         beforesync: true,
20384          /**
20385          * @event beforepush
20386          * Fires before the iframe editor is updated with content from the textarea. Return false
20387          * to cancel the push.
20388          * @param {Roo.HtmlEditorCore} this
20389          * @param {String} html
20390          */
20391         beforepush: true,
20392          /**
20393          * @event sync
20394          * Fires when the textarea is updated with content from the editor iframe.
20395          * @param {Roo.HtmlEditorCore} this
20396          * @param {String} html
20397          */
20398         sync: true,
20399          /**
20400          * @event push
20401          * Fires when the iframe editor is updated with content from the textarea.
20402          * @param {Roo.HtmlEditorCore} this
20403          * @param {String} html
20404          */
20405         push: true,
20406         
20407         /**
20408          * @event editorevent
20409          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20410          * @param {Roo.HtmlEditorCore} this
20411          */
20412         editorevent: true
20413         
20414     });
20415     
20416     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20417     
20418     // defaults : white / black...
20419     this.applyBlacklists();
20420     
20421     
20422     
20423 };
20424
20425
20426 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20427
20428
20429      /**
20430      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20431      */
20432     
20433     owner : false,
20434     
20435      /**
20436      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20437      *                        Roo.resizable.
20438      */
20439     resizable : false,
20440      /**
20441      * @cfg {Number} height (in pixels)
20442      */   
20443     height: 300,
20444    /**
20445      * @cfg {Number} width (in pixels)
20446      */   
20447     width: 500,
20448     
20449     /**
20450      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20451      * 
20452      */
20453     stylesheets: false,
20454     
20455     // id of frame..
20456     frameId: false,
20457     
20458     // private properties
20459     validationEvent : false,
20460     deferHeight: true,
20461     initialized : false,
20462     activated : false,
20463     sourceEditMode : false,
20464     onFocus : Roo.emptyFn,
20465     iframePad:3,
20466     hideMode:'offsets',
20467     
20468     clearUp: true,
20469     
20470     // blacklist + whitelisted elements..
20471     black: false,
20472     white: false,
20473      
20474     
20475
20476     /**
20477      * Protected method that will not generally be called directly. It
20478      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20479      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20480      */
20481     getDocMarkup : function(){
20482         // body styles..
20483         var st = '';
20484         
20485         // inherit styels from page...?? 
20486         if (this.stylesheets === false) {
20487             
20488             Roo.get(document.head).select('style').each(function(node) {
20489                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20490             });
20491             
20492             Roo.get(document.head).select('link').each(function(node) { 
20493                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20494             });
20495             
20496         } else if (!this.stylesheets.length) {
20497                 // simple..
20498                 st = '<style type="text/css">' +
20499                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20500                    '</style>';
20501         } else { 
20502             
20503         }
20504         
20505         st +=  '<style type="text/css">' +
20506             'IMG { cursor: pointer } ' +
20507         '</style>';
20508
20509         
20510         return '<html><head>' + st  +
20511             //<style type="text/css">' +
20512             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20513             //'</style>' +
20514             ' </head><body class="roo-htmleditor-body"></body></html>';
20515     },
20516
20517     // private
20518     onRender : function(ct, position)
20519     {
20520         var _t = this;
20521         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20522         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20523         
20524         
20525         this.el.dom.style.border = '0 none';
20526         this.el.dom.setAttribute('tabIndex', -1);
20527         this.el.addClass('x-hidden hide');
20528         
20529         
20530         
20531         if(Roo.isIE){ // fix IE 1px bogus margin
20532             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20533         }
20534        
20535         
20536         this.frameId = Roo.id();
20537         
20538          
20539         
20540         var iframe = this.owner.wrap.createChild({
20541             tag: 'iframe',
20542             cls: 'form-control', // bootstrap..
20543             id: this.frameId,
20544             name: this.frameId,
20545             frameBorder : 'no',
20546             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20547         }, this.el
20548         );
20549         
20550         
20551         this.iframe = iframe.dom;
20552
20553          this.assignDocWin();
20554         
20555         this.doc.designMode = 'on';
20556        
20557         this.doc.open();
20558         this.doc.write(this.getDocMarkup());
20559         this.doc.close();
20560
20561         
20562         var task = { // must defer to wait for browser to be ready
20563             run : function(){
20564                 //console.log("run task?" + this.doc.readyState);
20565                 this.assignDocWin();
20566                 if(this.doc.body || this.doc.readyState == 'complete'){
20567                     try {
20568                         this.doc.designMode="on";
20569                     } catch (e) {
20570                         return;
20571                     }
20572                     Roo.TaskMgr.stop(task);
20573                     this.initEditor.defer(10, this);
20574                 }
20575             },
20576             interval : 10,
20577             duration: 10000,
20578             scope: this
20579         };
20580         Roo.TaskMgr.start(task);
20581
20582     },
20583
20584     // private
20585     onResize : function(w, h)
20586     {
20587          Roo.log('resize: ' +w + ',' + h );
20588         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20589         if(!this.iframe){
20590             return;
20591         }
20592         if(typeof w == 'number'){
20593             
20594             this.iframe.style.width = w + 'px';
20595         }
20596         if(typeof h == 'number'){
20597             
20598             this.iframe.style.height = h + 'px';
20599             if(this.doc){
20600                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20601             }
20602         }
20603         
20604     },
20605
20606     /**
20607      * Toggles the editor between standard and source edit mode.
20608      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20609      */
20610     toggleSourceEdit : function(sourceEditMode){
20611         
20612         this.sourceEditMode = sourceEditMode === true;
20613         
20614         if(this.sourceEditMode){
20615  
20616             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20617             
20618         }else{
20619             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20620             //this.iframe.className = '';
20621             this.deferFocus();
20622         }
20623         //this.setSize(this.owner.wrap.getSize());
20624         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20625     },
20626
20627     
20628   
20629
20630     /**
20631      * Protected method that will not generally be called directly. If you need/want
20632      * custom HTML cleanup, this is the method you should override.
20633      * @param {String} html The HTML to be cleaned
20634      * return {String} The cleaned HTML
20635      */
20636     cleanHtml : function(html){
20637         html = String(html);
20638         if(html.length > 5){
20639             if(Roo.isSafari){ // strip safari nonsense
20640                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20641             }
20642         }
20643         if(html == '&nbsp;'){
20644             html = '';
20645         }
20646         return html;
20647     },
20648
20649     /**
20650      * HTML Editor -> Textarea
20651      * Protected method that will not generally be called directly. Syncs the contents
20652      * of the editor iframe with the textarea.
20653      */
20654     syncValue : function(){
20655         if(this.initialized){
20656             var bd = (this.doc.body || this.doc.documentElement);
20657             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20658             var html = bd.innerHTML;
20659             if(Roo.isSafari){
20660                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20661                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20662                 if(m && m[1]){
20663                     html = '<div style="'+m[0]+'">' + html + '</div>';
20664                 }
20665             }
20666             html = this.cleanHtml(html);
20667             // fix up the special chars.. normaly like back quotes in word...
20668             // however we do not want to do this with chinese..
20669             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20670                 var cc = b.charCodeAt();
20671                 if (
20672                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20673                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20674                     (cc >= 0xf900 && cc < 0xfb00 )
20675                 ) {
20676                         return b;
20677                 }
20678                 return "&#"+cc+";" 
20679             });
20680             if(this.owner.fireEvent('beforesync', this, html) !== false){
20681                 this.el.dom.value = html;
20682                 this.owner.fireEvent('sync', this, html);
20683             }
20684         }
20685     },
20686
20687     /**
20688      * Protected method that will not generally be called directly. Pushes the value of the textarea
20689      * into the iframe editor.
20690      */
20691     pushValue : function(){
20692         if(this.initialized){
20693             var v = this.el.dom.value.trim();
20694             
20695 //            if(v.length < 1){
20696 //                v = '&#160;';
20697 //            }
20698             
20699             if(this.owner.fireEvent('beforepush', this, v) !== false){
20700                 var d = (this.doc.body || this.doc.documentElement);
20701                 d.innerHTML = v;
20702                 this.cleanUpPaste();
20703                 this.el.dom.value = d.innerHTML;
20704                 this.owner.fireEvent('push', this, v);
20705             }
20706         }
20707     },
20708
20709     // private
20710     deferFocus : function(){
20711         this.focus.defer(10, this);
20712     },
20713
20714     // doc'ed in Field
20715     focus : function(){
20716         if(this.win && !this.sourceEditMode){
20717             this.win.focus();
20718         }else{
20719             this.el.focus();
20720         }
20721     },
20722     
20723     assignDocWin: function()
20724     {
20725         var iframe = this.iframe;
20726         
20727          if(Roo.isIE){
20728             this.doc = iframe.contentWindow.document;
20729             this.win = iframe.contentWindow;
20730         } else {
20731 //            if (!Roo.get(this.frameId)) {
20732 //                return;
20733 //            }
20734 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20735 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20736             
20737             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20738                 return;
20739             }
20740             
20741             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20742             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20743         }
20744     },
20745     
20746     // private
20747     initEditor : function(){
20748         //console.log("INIT EDITOR");
20749         this.assignDocWin();
20750         
20751         
20752         
20753         this.doc.designMode="on";
20754         this.doc.open();
20755         this.doc.write(this.getDocMarkup());
20756         this.doc.close();
20757         
20758         var dbody = (this.doc.body || this.doc.documentElement);
20759         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20760         // this copies styles from the containing element into thsi one..
20761         // not sure why we need all of this..
20762         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20763         
20764         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20765         //ss['background-attachment'] = 'fixed'; // w3c
20766         dbody.bgProperties = 'fixed'; // ie
20767         //Roo.DomHelper.applyStyles(dbody, ss);
20768         Roo.EventManager.on(this.doc, {
20769             //'mousedown': this.onEditorEvent,
20770             'mouseup': this.onEditorEvent,
20771             'dblclick': this.onEditorEvent,
20772             'click': this.onEditorEvent,
20773             'keyup': this.onEditorEvent,
20774             buffer:100,
20775             scope: this
20776         });
20777         if(Roo.isGecko){
20778             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20779         }
20780         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20781             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20782         }
20783         this.initialized = true;
20784
20785         this.owner.fireEvent('initialize', this);
20786         this.pushValue();
20787     },
20788
20789     // private
20790     onDestroy : function(){
20791         
20792         
20793         
20794         if(this.rendered){
20795             
20796             //for (var i =0; i < this.toolbars.length;i++) {
20797             //    // fixme - ask toolbars for heights?
20798             //    this.toolbars[i].onDestroy();
20799            // }
20800             
20801             //this.wrap.dom.innerHTML = '';
20802             //this.wrap.remove();
20803         }
20804     },
20805
20806     // private
20807     onFirstFocus : function(){
20808         
20809         this.assignDocWin();
20810         
20811         
20812         this.activated = true;
20813          
20814     
20815         if(Roo.isGecko){ // prevent silly gecko errors
20816             this.win.focus();
20817             var s = this.win.getSelection();
20818             if(!s.focusNode || s.focusNode.nodeType != 3){
20819                 var r = s.getRangeAt(0);
20820                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20821                 r.collapse(true);
20822                 this.deferFocus();
20823             }
20824             try{
20825                 this.execCmd('useCSS', true);
20826                 this.execCmd('styleWithCSS', false);
20827             }catch(e){}
20828         }
20829         this.owner.fireEvent('activate', this);
20830     },
20831
20832     // private
20833     adjustFont: function(btn){
20834         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20835         //if(Roo.isSafari){ // safari
20836         //    adjust *= 2;
20837        // }
20838         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20839         if(Roo.isSafari){ // safari
20840             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20841             v =  (v < 10) ? 10 : v;
20842             v =  (v > 48) ? 48 : v;
20843             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20844             
20845         }
20846         
20847         
20848         v = Math.max(1, v+adjust);
20849         
20850         this.execCmd('FontSize', v  );
20851     },
20852
20853     onEditorEvent : function(e)
20854     {
20855         this.owner.fireEvent('editorevent', this, e);
20856       //  this.updateToolbar();
20857         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20858     },
20859
20860     insertTag : function(tg)
20861     {
20862         // could be a bit smarter... -> wrap the current selected tRoo..
20863         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20864             
20865             range = this.createRange(this.getSelection());
20866             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20867             wrappingNode.appendChild(range.extractContents());
20868             range.insertNode(wrappingNode);
20869
20870             return;
20871             
20872             
20873             
20874         }
20875         this.execCmd("formatblock",   tg);
20876         
20877     },
20878     
20879     insertText : function(txt)
20880     {
20881         
20882         
20883         var range = this.createRange();
20884         range.deleteContents();
20885                //alert(Sender.getAttribute('label'));
20886                
20887         range.insertNode(this.doc.createTextNode(txt));
20888     } ,
20889     
20890      
20891
20892     /**
20893      * Executes a Midas editor command on the editor document and performs necessary focus and
20894      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20895      * @param {String} cmd The Midas command
20896      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20897      */
20898     relayCmd : function(cmd, value){
20899         this.win.focus();
20900         this.execCmd(cmd, value);
20901         this.owner.fireEvent('editorevent', this);
20902         //this.updateToolbar();
20903         this.owner.deferFocus();
20904     },
20905
20906     /**
20907      * Executes a Midas editor command directly on the editor document.
20908      * For visual commands, you should use {@link #relayCmd} instead.
20909      * <b>This should only be called after the editor is initialized.</b>
20910      * @param {String} cmd The Midas command
20911      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20912      */
20913     execCmd : function(cmd, value){
20914         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20915         this.syncValue();
20916     },
20917  
20918  
20919    
20920     /**
20921      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20922      * to insert tRoo.
20923      * @param {String} text | dom node.. 
20924      */
20925     insertAtCursor : function(text)
20926     {
20927         
20928         
20929         
20930         if(!this.activated){
20931             return;
20932         }
20933         /*
20934         if(Roo.isIE){
20935             this.win.focus();
20936             var r = this.doc.selection.createRange();
20937             if(r){
20938                 r.collapse(true);
20939                 r.pasteHTML(text);
20940                 this.syncValue();
20941                 this.deferFocus();
20942             
20943             }
20944             return;
20945         }
20946         */
20947         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20948             this.win.focus();
20949             
20950             
20951             // from jquery ui (MIT licenced)
20952             var range, node;
20953             var win = this.win;
20954             
20955             if (win.getSelection && win.getSelection().getRangeAt) {
20956                 range = win.getSelection().getRangeAt(0);
20957                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20958                 range.insertNode(node);
20959             } else if (win.document.selection && win.document.selection.createRange) {
20960                 // no firefox support
20961                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20962                 win.document.selection.createRange().pasteHTML(txt);
20963             } else {
20964                 // no firefox support
20965                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20966                 this.execCmd('InsertHTML', txt);
20967             } 
20968             
20969             this.syncValue();
20970             
20971             this.deferFocus();
20972         }
20973     },
20974  // private
20975     mozKeyPress : function(e){
20976         if(e.ctrlKey){
20977             var c = e.getCharCode(), cmd;
20978           
20979             if(c > 0){
20980                 c = String.fromCharCode(c).toLowerCase();
20981                 switch(c){
20982                     case 'b':
20983                         cmd = 'bold';
20984                         break;
20985                     case 'i':
20986                         cmd = 'italic';
20987                         break;
20988                     
20989                     case 'u':
20990                         cmd = 'underline';
20991                         break;
20992                     
20993                     case 'v':
20994                         this.cleanUpPaste.defer(100, this);
20995                         return;
20996                         
20997                 }
20998                 if(cmd){
20999                     this.win.focus();
21000                     this.execCmd(cmd);
21001                     this.deferFocus();
21002                     e.preventDefault();
21003                 }
21004                 
21005             }
21006         }
21007     },
21008
21009     // private
21010     fixKeys : function(){ // load time branching for fastest keydown performance
21011         if(Roo.isIE){
21012             return function(e){
21013                 var k = e.getKey(), r;
21014                 if(k == e.TAB){
21015                     e.stopEvent();
21016                     r = this.doc.selection.createRange();
21017                     if(r){
21018                         r.collapse(true);
21019                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21020                         this.deferFocus();
21021                     }
21022                     return;
21023                 }
21024                 
21025                 if(k == e.ENTER){
21026                     r = this.doc.selection.createRange();
21027                     if(r){
21028                         var target = r.parentElement();
21029                         if(!target || target.tagName.toLowerCase() != 'li'){
21030                             e.stopEvent();
21031                             r.pasteHTML('<br />');
21032                             r.collapse(false);
21033                             r.select();
21034                         }
21035                     }
21036                 }
21037                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21038                     this.cleanUpPaste.defer(100, this);
21039                     return;
21040                 }
21041                 
21042                 
21043             };
21044         }else if(Roo.isOpera){
21045             return function(e){
21046                 var k = e.getKey();
21047                 if(k == e.TAB){
21048                     e.stopEvent();
21049                     this.win.focus();
21050                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21051                     this.deferFocus();
21052                 }
21053                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21054                     this.cleanUpPaste.defer(100, this);
21055                     return;
21056                 }
21057                 
21058             };
21059         }else if(Roo.isSafari){
21060             return function(e){
21061                 var k = e.getKey();
21062                 
21063                 if(k == e.TAB){
21064                     e.stopEvent();
21065                     this.execCmd('InsertText','\t');
21066                     this.deferFocus();
21067                     return;
21068                 }
21069                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21070                     this.cleanUpPaste.defer(100, this);
21071                     return;
21072                 }
21073                 
21074              };
21075         }
21076     }(),
21077     
21078     getAllAncestors: function()
21079     {
21080         var p = this.getSelectedNode();
21081         var a = [];
21082         if (!p) {
21083             a.push(p); // push blank onto stack..
21084             p = this.getParentElement();
21085         }
21086         
21087         
21088         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21089             a.push(p);
21090             p = p.parentNode;
21091         }
21092         a.push(this.doc.body);
21093         return a;
21094     },
21095     lastSel : false,
21096     lastSelNode : false,
21097     
21098     
21099     getSelection : function() 
21100     {
21101         this.assignDocWin();
21102         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21103     },
21104     
21105     getSelectedNode: function() 
21106     {
21107         // this may only work on Gecko!!!
21108         
21109         // should we cache this!!!!
21110         
21111         
21112         
21113          
21114         var range = this.createRange(this.getSelection()).cloneRange();
21115         
21116         if (Roo.isIE) {
21117             var parent = range.parentElement();
21118             while (true) {
21119                 var testRange = range.duplicate();
21120                 testRange.moveToElementText(parent);
21121                 if (testRange.inRange(range)) {
21122                     break;
21123                 }
21124                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21125                     break;
21126                 }
21127                 parent = parent.parentElement;
21128             }
21129             return parent;
21130         }
21131         
21132         // is ancestor a text element.
21133         var ac =  range.commonAncestorContainer;
21134         if (ac.nodeType == 3) {
21135             ac = ac.parentNode;
21136         }
21137         
21138         var ar = ac.childNodes;
21139          
21140         var nodes = [];
21141         var other_nodes = [];
21142         var has_other_nodes = false;
21143         for (var i=0;i<ar.length;i++) {
21144             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21145                 continue;
21146             }
21147             // fullly contained node.
21148             
21149             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21150                 nodes.push(ar[i]);
21151                 continue;
21152             }
21153             
21154             // probably selected..
21155             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21156                 other_nodes.push(ar[i]);
21157                 continue;
21158             }
21159             // outer..
21160             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21161                 continue;
21162             }
21163             
21164             
21165             has_other_nodes = true;
21166         }
21167         if (!nodes.length && other_nodes.length) {
21168             nodes= other_nodes;
21169         }
21170         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21171             return false;
21172         }
21173         
21174         return nodes[0];
21175     },
21176     createRange: function(sel)
21177     {
21178         // this has strange effects when using with 
21179         // top toolbar - not sure if it's a great idea.
21180         //this.editor.contentWindow.focus();
21181         if (typeof sel != "undefined") {
21182             try {
21183                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21184             } catch(e) {
21185                 return this.doc.createRange();
21186             }
21187         } else {
21188             return this.doc.createRange();
21189         }
21190     },
21191     getParentElement: function()
21192     {
21193         
21194         this.assignDocWin();
21195         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21196         
21197         var range = this.createRange(sel);
21198          
21199         try {
21200             var p = range.commonAncestorContainer;
21201             while (p.nodeType == 3) { // text node
21202                 p = p.parentNode;
21203             }
21204             return p;
21205         } catch (e) {
21206             return null;
21207         }
21208     
21209     },
21210     /***
21211      *
21212      * Range intersection.. the hard stuff...
21213      *  '-1' = before
21214      *  '0' = hits..
21215      *  '1' = after.
21216      *         [ -- selected range --- ]
21217      *   [fail]                        [fail]
21218      *
21219      *    basically..
21220      *      if end is before start or  hits it. fail.
21221      *      if start is after end or hits it fail.
21222      *
21223      *   if either hits (but other is outside. - then it's not 
21224      *   
21225      *    
21226      **/
21227     
21228     
21229     // @see http://www.thismuchiknow.co.uk/?p=64.
21230     rangeIntersectsNode : function(range, node)
21231     {
21232         var nodeRange = node.ownerDocument.createRange();
21233         try {
21234             nodeRange.selectNode(node);
21235         } catch (e) {
21236             nodeRange.selectNodeContents(node);
21237         }
21238     
21239         var rangeStartRange = range.cloneRange();
21240         rangeStartRange.collapse(true);
21241     
21242         var rangeEndRange = range.cloneRange();
21243         rangeEndRange.collapse(false);
21244     
21245         var nodeStartRange = nodeRange.cloneRange();
21246         nodeStartRange.collapse(true);
21247     
21248         var nodeEndRange = nodeRange.cloneRange();
21249         nodeEndRange.collapse(false);
21250     
21251         return rangeStartRange.compareBoundaryPoints(
21252                  Range.START_TO_START, nodeEndRange) == -1 &&
21253                rangeEndRange.compareBoundaryPoints(
21254                  Range.START_TO_START, nodeStartRange) == 1;
21255         
21256          
21257     },
21258     rangeCompareNode : function(range, node)
21259     {
21260         var nodeRange = node.ownerDocument.createRange();
21261         try {
21262             nodeRange.selectNode(node);
21263         } catch (e) {
21264             nodeRange.selectNodeContents(node);
21265         }
21266         
21267         
21268         range.collapse(true);
21269     
21270         nodeRange.collapse(true);
21271      
21272         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21273         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21274          
21275         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21276         
21277         var nodeIsBefore   =  ss == 1;
21278         var nodeIsAfter    = ee == -1;
21279         
21280         if (nodeIsBefore && nodeIsAfter) {
21281             return 0; // outer
21282         }
21283         if (!nodeIsBefore && nodeIsAfter) {
21284             return 1; //right trailed.
21285         }
21286         
21287         if (nodeIsBefore && !nodeIsAfter) {
21288             return 2;  // left trailed.
21289         }
21290         // fully contined.
21291         return 3;
21292     },
21293
21294     // private? - in a new class?
21295     cleanUpPaste :  function()
21296     {
21297         // cleans up the whole document..
21298         Roo.log('cleanuppaste');
21299         
21300         this.cleanUpChildren(this.doc.body);
21301         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21302         if (clean != this.doc.body.innerHTML) {
21303             this.doc.body.innerHTML = clean;
21304         }
21305         
21306     },
21307     
21308     cleanWordChars : function(input) {// change the chars to hex code
21309         var he = Roo.HtmlEditorCore;
21310         
21311         var output = input;
21312         Roo.each(he.swapCodes, function(sw) { 
21313             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21314             
21315             output = output.replace(swapper, sw[1]);
21316         });
21317         
21318         return output;
21319     },
21320     
21321     
21322     cleanUpChildren : function (n)
21323     {
21324         if (!n.childNodes.length) {
21325             return;
21326         }
21327         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21328            this.cleanUpChild(n.childNodes[i]);
21329         }
21330     },
21331     
21332     
21333         
21334     
21335     cleanUpChild : function (node)
21336     {
21337         var ed = this;
21338         //console.log(node);
21339         if (node.nodeName == "#text") {
21340             // clean up silly Windows -- stuff?
21341             return; 
21342         }
21343         if (node.nodeName == "#comment") {
21344             node.parentNode.removeChild(node);
21345             // clean up silly Windows -- stuff?
21346             return; 
21347         }
21348         var lcname = node.tagName.toLowerCase();
21349         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21350         // whitelist of tags..
21351         
21352         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21353             // remove node.
21354             node.parentNode.removeChild(node);
21355             return;
21356             
21357         }
21358         
21359         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21360         
21361         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21362         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21363         
21364         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21365         //    remove_keep_children = true;
21366         //}
21367         
21368         if (remove_keep_children) {
21369             this.cleanUpChildren(node);
21370             // inserts everything just before this node...
21371             while (node.childNodes.length) {
21372                 var cn = node.childNodes[0];
21373                 node.removeChild(cn);
21374                 node.parentNode.insertBefore(cn, node);
21375             }
21376             node.parentNode.removeChild(node);
21377             return;
21378         }
21379         
21380         if (!node.attributes || !node.attributes.length) {
21381             this.cleanUpChildren(node);
21382             return;
21383         }
21384         
21385         function cleanAttr(n,v)
21386         {
21387             
21388             if (v.match(/^\./) || v.match(/^\//)) {
21389                 return;
21390             }
21391             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21392                 return;
21393             }
21394             if (v.match(/^#/)) {
21395                 return;
21396             }
21397 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21398             node.removeAttribute(n);
21399             
21400         }
21401         
21402         var cwhite = this.cwhite;
21403         var cblack = this.cblack;
21404             
21405         function cleanStyle(n,v)
21406         {
21407             if (v.match(/expression/)) { //XSS?? should we even bother..
21408                 node.removeAttribute(n);
21409                 return;
21410             }
21411             
21412             var parts = v.split(/;/);
21413             var clean = [];
21414             
21415             Roo.each(parts, function(p) {
21416                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21417                 if (!p.length) {
21418                     return true;
21419                 }
21420                 var l = p.split(':').shift().replace(/\s+/g,'');
21421                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21422                 
21423                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21424 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21425                     //node.removeAttribute(n);
21426                     return true;
21427                 }
21428                 //Roo.log()
21429                 // only allow 'c whitelisted system attributes'
21430                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21431 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21432                     //node.removeAttribute(n);
21433                     return true;
21434                 }
21435                 
21436                 
21437                  
21438                 
21439                 clean.push(p);
21440                 return true;
21441             });
21442             if (clean.length) { 
21443                 node.setAttribute(n, clean.join(';'));
21444             } else {
21445                 node.removeAttribute(n);
21446             }
21447             
21448         }
21449         
21450         
21451         for (var i = node.attributes.length-1; i > -1 ; i--) {
21452             var a = node.attributes[i];
21453             //console.log(a);
21454             
21455             if (a.name.toLowerCase().substr(0,2)=='on')  {
21456                 node.removeAttribute(a.name);
21457                 continue;
21458             }
21459             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21460                 node.removeAttribute(a.name);
21461                 continue;
21462             }
21463             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21464                 cleanAttr(a.name,a.value); // fixme..
21465                 continue;
21466             }
21467             if (a.name == 'style') {
21468                 cleanStyle(a.name,a.value);
21469                 continue;
21470             }
21471             /// clean up MS crap..
21472             // tecnically this should be a list of valid class'es..
21473             
21474             
21475             if (a.name == 'class') {
21476                 if (a.value.match(/^Mso/)) {
21477                     node.className = '';
21478                 }
21479                 
21480                 if (a.value.match(/body/)) {
21481                     node.className = '';
21482                 }
21483                 continue;
21484             }
21485             
21486             // style cleanup!?
21487             // class cleanup?
21488             
21489         }
21490         
21491         
21492         this.cleanUpChildren(node);
21493         
21494         
21495     },
21496     
21497     /**
21498      * Clean up MS wordisms...
21499      */
21500     cleanWord : function(node)
21501     {
21502         
21503         
21504         if (!node) {
21505             this.cleanWord(this.doc.body);
21506             return;
21507         }
21508         if (node.nodeName == "#text") {
21509             // clean up silly Windows -- stuff?
21510             return; 
21511         }
21512         if (node.nodeName == "#comment") {
21513             node.parentNode.removeChild(node);
21514             // clean up silly Windows -- stuff?
21515             return; 
21516         }
21517         
21518         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21519             node.parentNode.removeChild(node);
21520             return;
21521         }
21522         
21523         // remove - but keep children..
21524         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21525             while (node.childNodes.length) {
21526                 var cn = node.childNodes[0];
21527                 node.removeChild(cn);
21528                 node.parentNode.insertBefore(cn, node);
21529             }
21530             node.parentNode.removeChild(node);
21531             this.iterateChildren(node, this.cleanWord);
21532             return;
21533         }
21534         // clean styles
21535         if (node.className.length) {
21536             
21537             var cn = node.className.split(/\W+/);
21538             var cna = [];
21539             Roo.each(cn, function(cls) {
21540                 if (cls.match(/Mso[a-zA-Z]+/)) {
21541                     return;
21542                 }
21543                 cna.push(cls);
21544             });
21545             node.className = cna.length ? cna.join(' ') : '';
21546             if (!cna.length) {
21547                 node.removeAttribute("class");
21548             }
21549         }
21550         
21551         if (node.hasAttribute("lang")) {
21552             node.removeAttribute("lang");
21553         }
21554         
21555         if (node.hasAttribute("style")) {
21556             
21557             var styles = node.getAttribute("style").split(";");
21558             var nstyle = [];
21559             Roo.each(styles, function(s) {
21560                 if (!s.match(/:/)) {
21561                     return;
21562                 }
21563                 var kv = s.split(":");
21564                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21565                     return;
21566                 }
21567                 // what ever is left... we allow.
21568                 nstyle.push(s);
21569             });
21570             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21571             if (!nstyle.length) {
21572                 node.removeAttribute('style');
21573             }
21574         }
21575         this.iterateChildren(node, this.cleanWord);
21576         
21577         
21578         
21579     },
21580     /**
21581      * iterateChildren of a Node, calling fn each time, using this as the scole..
21582      * @param {DomNode} node node to iterate children of.
21583      * @param {Function} fn method of this class to call on each item.
21584      */
21585     iterateChildren : function(node, fn)
21586     {
21587         if (!node.childNodes.length) {
21588                 return;
21589         }
21590         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21591            fn.call(this, node.childNodes[i])
21592         }
21593     },
21594     
21595     
21596     /**
21597      * cleanTableWidths.
21598      *
21599      * Quite often pasting from word etc.. results in tables with column and widths.
21600      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21601      *
21602      */
21603     cleanTableWidths : function(node)
21604     {
21605          
21606          
21607         if (!node) {
21608             this.cleanTableWidths(this.doc.body);
21609             return;
21610         }
21611         
21612         // ignore list...
21613         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21614             return; 
21615         }
21616         Roo.log(node.tagName);
21617         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21618             this.iterateChildren(node, this.cleanTableWidths);
21619             return;
21620         }
21621         if (node.hasAttribute('width')) {
21622             node.removeAttribute('width');
21623         }
21624         
21625          
21626         if (node.hasAttribute("style")) {
21627             // pretty basic...
21628             
21629             var styles = node.getAttribute("style").split(";");
21630             var nstyle = [];
21631             Roo.each(styles, function(s) {
21632                 if (!s.match(/:/)) {
21633                     return;
21634                 }
21635                 var kv = s.split(":");
21636                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21637                     return;
21638                 }
21639                 // what ever is left... we allow.
21640                 nstyle.push(s);
21641             });
21642             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21643             if (!nstyle.length) {
21644                 node.removeAttribute('style');
21645             }
21646         }
21647         
21648         this.iterateChildren(node, this.cleanTableWidths);
21649         
21650         
21651     },
21652     
21653     
21654     
21655     
21656     domToHTML : function(currentElement, depth, nopadtext) {
21657         
21658         depth = depth || 0;
21659         nopadtext = nopadtext || false;
21660     
21661         if (!currentElement) {
21662             return this.domToHTML(this.doc.body);
21663         }
21664         
21665         //Roo.log(currentElement);
21666         var j;
21667         var allText = false;
21668         var nodeName = currentElement.nodeName;
21669         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21670         
21671         if  (nodeName == '#text') {
21672             
21673             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21674         }
21675         
21676         
21677         var ret = '';
21678         if (nodeName != 'BODY') {
21679              
21680             var i = 0;
21681             // Prints the node tagName, such as <A>, <IMG>, etc
21682             if (tagName) {
21683                 var attr = [];
21684                 for(i = 0; i < currentElement.attributes.length;i++) {
21685                     // quoting?
21686                     var aname = currentElement.attributes.item(i).name;
21687                     if (!currentElement.attributes.item(i).value.length) {
21688                         continue;
21689                     }
21690                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21691                 }
21692                 
21693                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21694             } 
21695             else {
21696                 
21697                 // eack
21698             }
21699         } else {
21700             tagName = false;
21701         }
21702         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21703             return ret;
21704         }
21705         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21706             nopadtext = true;
21707         }
21708         
21709         
21710         // Traverse the tree
21711         i = 0;
21712         var currentElementChild = currentElement.childNodes.item(i);
21713         var allText = true;
21714         var innerHTML  = '';
21715         lastnode = '';
21716         while (currentElementChild) {
21717             // Formatting code (indent the tree so it looks nice on the screen)
21718             var nopad = nopadtext;
21719             if (lastnode == 'SPAN') {
21720                 nopad  = true;
21721             }
21722             // text
21723             if  (currentElementChild.nodeName == '#text') {
21724                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21725                 toadd = nopadtext ? toadd : toadd.trim();
21726                 if (!nopad && toadd.length > 80) {
21727                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21728                 }
21729                 innerHTML  += toadd;
21730                 
21731                 i++;
21732                 currentElementChild = currentElement.childNodes.item(i);
21733                 lastNode = '';
21734                 continue;
21735             }
21736             allText = false;
21737             
21738             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21739                 
21740             // Recursively traverse the tree structure of the child node
21741             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21742             lastnode = currentElementChild.nodeName;
21743             i++;
21744             currentElementChild=currentElement.childNodes.item(i);
21745         }
21746         
21747         ret += innerHTML;
21748         
21749         if (!allText) {
21750                 // The remaining code is mostly for formatting the tree
21751             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21752         }
21753         
21754         
21755         if (tagName) {
21756             ret+= "</"+tagName+">";
21757         }
21758         return ret;
21759         
21760     },
21761         
21762     applyBlacklists : function()
21763     {
21764         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21765         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21766         
21767         this.white = [];
21768         this.black = [];
21769         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21770             if (b.indexOf(tag) > -1) {
21771                 return;
21772             }
21773             this.white.push(tag);
21774             
21775         }, this);
21776         
21777         Roo.each(w, function(tag) {
21778             if (b.indexOf(tag) > -1) {
21779                 return;
21780             }
21781             if (this.white.indexOf(tag) > -1) {
21782                 return;
21783             }
21784             this.white.push(tag);
21785             
21786         }, this);
21787         
21788         
21789         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21790             if (w.indexOf(tag) > -1) {
21791                 return;
21792             }
21793             this.black.push(tag);
21794             
21795         }, this);
21796         
21797         Roo.each(b, function(tag) {
21798             if (w.indexOf(tag) > -1) {
21799                 return;
21800             }
21801             if (this.black.indexOf(tag) > -1) {
21802                 return;
21803             }
21804             this.black.push(tag);
21805             
21806         }, this);
21807         
21808         
21809         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21810         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21811         
21812         this.cwhite = [];
21813         this.cblack = [];
21814         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21815             if (b.indexOf(tag) > -1) {
21816                 return;
21817             }
21818             this.cwhite.push(tag);
21819             
21820         }, this);
21821         
21822         Roo.each(w, function(tag) {
21823             if (b.indexOf(tag) > -1) {
21824                 return;
21825             }
21826             if (this.cwhite.indexOf(tag) > -1) {
21827                 return;
21828             }
21829             this.cwhite.push(tag);
21830             
21831         }, this);
21832         
21833         
21834         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21835             if (w.indexOf(tag) > -1) {
21836                 return;
21837             }
21838             this.cblack.push(tag);
21839             
21840         }, this);
21841         
21842         Roo.each(b, function(tag) {
21843             if (w.indexOf(tag) > -1) {
21844                 return;
21845             }
21846             if (this.cblack.indexOf(tag) > -1) {
21847                 return;
21848             }
21849             this.cblack.push(tag);
21850             
21851         }, this);
21852     },
21853     
21854     setStylesheets : function(stylesheets)
21855     {
21856         if(typeof(stylesheets) == 'string'){
21857             Roo.get(this.iframe.contentDocument.head).createChild({
21858                 tag : 'link',
21859                 rel : 'stylesheet',
21860                 type : 'text/css',
21861                 href : stylesheets
21862             });
21863             
21864             return;
21865         }
21866         var _this = this;
21867      
21868         Roo.each(stylesheets, function(s) {
21869             if(!s.length){
21870                 return;
21871             }
21872             
21873             Roo.get(_this.iframe.contentDocument.head).createChild({
21874                 tag : 'link',
21875                 rel : 'stylesheet',
21876                 type : 'text/css',
21877                 href : s
21878             });
21879         });
21880
21881         
21882     },
21883     
21884     removeStylesheets : function()
21885     {
21886         var _this = this;
21887         
21888         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21889             s.remove();
21890         });
21891     }
21892     
21893     // hide stuff that is not compatible
21894     /**
21895      * @event blur
21896      * @hide
21897      */
21898     /**
21899      * @event change
21900      * @hide
21901      */
21902     /**
21903      * @event focus
21904      * @hide
21905      */
21906     /**
21907      * @event specialkey
21908      * @hide
21909      */
21910     /**
21911      * @cfg {String} fieldClass @hide
21912      */
21913     /**
21914      * @cfg {String} focusClass @hide
21915      */
21916     /**
21917      * @cfg {String} autoCreate @hide
21918      */
21919     /**
21920      * @cfg {String} inputType @hide
21921      */
21922     /**
21923      * @cfg {String} invalidClass @hide
21924      */
21925     /**
21926      * @cfg {String} invalidText @hide
21927      */
21928     /**
21929      * @cfg {String} msgFx @hide
21930      */
21931     /**
21932      * @cfg {String} validateOnBlur @hide
21933      */
21934 });
21935
21936 Roo.HtmlEditorCore.white = [
21937         'area', 'br', 'img', 'input', 'hr', 'wbr',
21938         
21939        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21940        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21941        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21942        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21943        'table',   'ul',         'xmp', 
21944        
21945        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21946       'thead',   'tr', 
21947      
21948       'dir', 'menu', 'ol', 'ul', 'dl',
21949        
21950       'embed',  'object'
21951 ];
21952
21953
21954 Roo.HtmlEditorCore.black = [
21955     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21956         'applet', // 
21957         'base',   'basefont', 'bgsound', 'blink',  'body', 
21958         'frame',  'frameset', 'head',    'html',   'ilayer', 
21959         'iframe', 'layer',  'link',     'meta',    'object',   
21960         'script', 'style' ,'title',  'xml' // clean later..
21961 ];
21962 Roo.HtmlEditorCore.clean = [
21963     'script', 'style', 'title', 'xml'
21964 ];
21965 Roo.HtmlEditorCore.remove = [
21966     'font'
21967 ];
21968 // attributes..
21969
21970 Roo.HtmlEditorCore.ablack = [
21971     'on'
21972 ];
21973     
21974 Roo.HtmlEditorCore.aclean = [ 
21975     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21976 ];
21977
21978 // protocols..
21979 Roo.HtmlEditorCore.pwhite= [
21980         'http',  'https',  'mailto'
21981 ];
21982
21983 // white listed style attributes.
21984 Roo.HtmlEditorCore.cwhite= [
21985       //  'text-align', /// default is to allow most things..
21986       
21987          
21988 //        'font-size'//??
21989 ];
21990
21991 // black listed style attributes.
21992 Roo.HtmlEditorCore.cblack= [
21993       //  'font-size' -- this can be set by the project 
21994 ];
21995
21996
21997 Roo.HtmlEditorCore.swapCodes   =[ 
21998     [    8211, "--" ], 
21999     [    8212, "--" ], 
22000     [    8216,  "'" ],  
22001     [    8217, "'" ],  
22002     [    8220, '"' ],  
22003     [    8221, '"' ],  
22004     [    8226, "*" ],  
22005     [    8230, "..." ]
22006 ]; 
22007
22008     /*
22009  * - LGPL
22010  *
22011  * HtmlEditor
22012  * 
22013  */
22014
22015 /**
22016  * @class Roo.bootstrap.HtmlEditor
22017  * @extends Roo.bootstrap.TextArea
22018  * Bootstrap HtmlEditor class
22019
22020  * @constructor
22021  * Create a new HtmlEditor
22022  * @param {Object} config The config object
22023  */
22024
22025 Roo.bootstrap.HtmlEditor = function(config){
22026     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22027     if (!this.toolbars) {
22028         this.toolbars = [];
22029     }
22030     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22031     this.addEvents({
22032             /**
22033              * @event initialize
22034              * Fires when the editor is fully initialized (including the iframe)
22035              * @param {HtmlEditor} this
22036              */
22037             initialize: true,
22038             /**
22039              * @event activate
22040              * Fires when the editor is first receives the focus. Any insertion must wait
22041              * until after this event.
22042              * @param {HtmlEditor} this
22043              */
22044             activate: true,
22045              /**
22046              * @event beforesync
22047              * Fires before the textarea is updated with content from the editor iframe. Return false
22048              * to cancel the sync.
22049              * @param {HtmlEditor} this
22050              * @param {String} html
22051              */
22052             beforesync: true,
22053              /**
22054              * @event beforepush
22055              * Fires before the iframe editor is updated with content from the textarea. Return false
22056              * to cancel the push.
22057              * @param {HtmlEditor} this
22058              * @param {String} html
22059              */
22060             beforepush: true,
22061              /**
22062              * @event sync
22063              * Fires when the textarea is updated with content from the editor iframe.
22064              * @param {HtmlEditor} this
22065              * @param {String} html
22066              */
22067             sync: true,
22068              /**
22069              * @event push
22070              * Fires when the iframe editor is updated with content from the textarea.
22071              * @param {HtmlEditor} this
22072              * @param {String} html
22073              */
22074             push: true,
22075              /**
22076              * @event editmodechange
22077              * Fires when the editor switches edit modes
22078              * @param {HtmlEditor} this
22079              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22080              */
22081             editmodechange: true,
22082             /**
22083              * @event editorevent
22084              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22085              * @param {HtmlEditor} this
22086              */
22087             editorevent: true,
22088             /**
22089              * @event firstfocus
22090              * Fires when on first focus - needed by toolbars..
22091              * @param {HtmlEditor} this
22092              */
22093             firstfocus: true,
22094             /**
22095              * @event autosave
22096              * Auto save the htmlEditor value as a file into Events
22097              * @param {HtmlEditor} this
22098              */
22099             autosave: true,
22100             /**
22101              * @event savedpreview
22102              * preview the saved version of htmlEditor
22103              * @param {HtmlEditor} this
22104              */
22105             savedpreview: true
22106         });
22107 };
22108
22109
22110 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22111     
22112     
22113       /**
22114      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22115      */
22116     toolbars : false,
22117    
22118      /**
22119      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22120      *                        Roo.resizable.
22121      */
22122     resizable : false,
22123      /**
22124      * @cfg {Number} height (in pixels)
22125      */   
22126     height: 300,
22127    /**
22128      * @cfg {Number} width (in pixels)
22129      */   
22130     width: false,
22131     
22132     /**
22133      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22134      * 
22135      */
22136     stylesheets: false,
22137     
22138     // id of frame..
22139     frameId: false,
22140     
22141     // private properties
22142     validationEvent : false,
22143     deferHeight: true,
22144     initialized : false,
22145     activated : false,
22146     
22147     onFocus : Roo.emptyFn,
22148     iframePad:3,
22149     hideMode:'offsets',
22150     
22151     
22152     tbContainer : false,
22153     
22154     toolbarContainer :function() {
22155         return this.wrap.select('.x-html-editor-tb',true).first();
22156     },
22157
22158     /**
22159      * Protected method that will not generally be called directly. It
22160      * is called when the editor creates its toolbar. Override this method if you need to
22161      * add custom toolbar buttons.
22162      * @param {HtmlEditor} editor
22163      */
22164     createToolbar : function(){
22165         
22166         Roo.log("create toolbars");
22167         
22168         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22169         this.toolbars[0].render(this.toolbarContainer());
22170         
22171         return;
22172         
22173 //        if (!editor.toolbars || !editor.toolbars.length) {
22174 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22175 //        }
22176 //        
22177 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22178 //            editor.toolbars[i] = Roo.factory(
22179 //                    typeof(editor.toolbars[i]) == 'string' ?
22180 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22181 //                Roo.bootstrap.HtmlEditor);
22182 //            editor.toolbars[i].init(editor);
22183 //        }
22184     },
22185
22186      
22187     // private
22188     onRender : function(ct, position)
22189     {
22190        // Roo.log("Call onRender: " + this.xtype);
22191         var _t = this;
22192         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22193       
22194         this.wrap = this.inputEl().wrap({
22195             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22196         });
22197         
22198         this.editorcore.onRender(ct, position);
22199          
22200         if (this.resizable) {
22201             this.resizeEl = new Roo.Resizable(this.wrap, {
22202                 pinned : true,
22203                 wrap: true,
22204                 dynamic : true,
22205                 minHeight : this.height,
22206                 height: this.height,
22207                 handles : this.resizable,
22208                 width: this.width,
22209                 listeners : {
22210                     resize : function(r, w, h) {
22211                         _t.onResize(w,h); // -something
22212                     }
22213                 }
22214             });
22215             
22216         }
22217         this.createToolbar(this);
22218        
22219         
22220         if(!this.width && this.resizable){
22221             this.setSize(this.wrap.getSize());
22222         }
22223         if (this.resizeEl) {
22224             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22225             // should trigger onReize..
22226         }
22227         
22228     },
22229
22230     // private
22231     onResize : function(w, h)
22232     {
22233         Roo.log('resize: ' +w + ',' + h );
22234         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22235         var ew = false;
22236         var eh = false;
22237         
22238         if(this.inputEl() ){
22239             if(typeof w == 'number'){
22240                 var aw = w - this.wrap.getFrameWidth('lr');
22241                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22242                 ew = aw;
22243             }
22244             if(typeof h == 'number'){
22245                  var tbh = -11;  // fixme it needs to tool bar size!
22246                 for (var i =0; i < this.toolbars.length;i++) {
22247                     // fixme - ask toolbars for heights?
22248                     tbh += this.toolbars[i].el.getHeight();
22249                     //if (this.toolbars[i].footer) {
22250                     //    tbh += this.toolbars[i].footer.el.getHeight();
22251                     //}
22252                 }
22253               
22254                 
22255                 
22256                 
22257                 
22258                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22259                 ah -= 5; // knock a few pixes off for look..
22260                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22261                 var eh = ah;
22262             }
22263         }
22264         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22265         this.editorcore.onResize(ew,eh);
22266         
22267     },
22268
22269     /**
22270      * Toggles the editor between standard and source edit mode.
22271      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22272      */
22273     toggleSourceEdit : function(sourceEditMode)
22274     {
22275         this.editorcore.toggleSourceEdit(sourceEditMode);
22276         
22277         if(this.editorcore.sourceEditMode){
22278             Roo.log('editor - showing textarea');
22279             
22280 //            Roo.log('in');
22281 //            Roo.log(this.syncValue());
22282             this.syncValue();
22283             this.inputEl().removeClass(['hide', 'x-hidden']);
22284             this.inputEl().dom.removeAttribute('tabIndex');
22285             this.inputEl().focus();
22286         }else{
22287             Roo.log('editor - hiding textarea');
22288 //            Roo.log('out')
22289 //            Roo.log(this.pushValue()); 
22290             this.pushValue();
22291             
22292             this.inputEl().addClass(['hide', 'x-hidden']);
22293             this.inputEl().dom.setAttribute('tabIndex', -1);
22294             //this.deferFocus();
22295         }
22296          
22297         if(this.resizable){
22298             this.setSize(this.wrap.getSize());
22299         }
22300         
22301         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22302     },
22303  
22304     // private (for BoxComponent)
22305     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22306
22307     // private (for BoxComponent)
22308     getResizeEl : function(){
22309         return this.wrap;
22310     },
22311
22312     // private (for BoxComponent)
22313     getPositionEl : function(){
22314         return this.wrap;
22315     },
22316
22317     // private
22318     initEvents : function(){
22319         this.originalValue = this.getValue();
22320     },
22321
22322 //    /**
22323 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22324 //     * @method
22325 //     */
22326 //    markInvalid : Roo.emptyFn,
22327 //    /**
22328 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22329 //     * @method
22330 //     */
22331 //    clearInvalid : Roo.emptyFn,
22332
22333     setValue : function(v){
22334         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22335         this.editorcore.pushValue();
22336     },
22337
22338      
22339     // private
22340     deferFocus : function(){
22341         this.focus.defer(10, this);
22342     },
22343
22344     // doc'ed in Field
22345     focus : function(){
22346         this.editorcore.focus();
22347         
22348     },
22349       
22350
22351     // private
22352     onDestroy : function(){
22353         
22354         
22355         
22356         if(this.rendered){
22357             
22358             for (var i =0; i < this.toolbars.length;i++) {
22359                 // fixme - ask toolbars for heights?
22360                 this.toolbars[i].onDestroy();
22361             }
22362             
22363             this.wrap.dom.innerHTML = '';
22364             this.wrap.remove();
22365         }
22366     },
22367
22368     // private
22369     onFirstFocus : function(){
22370         //Roo.log("onFirstFocus");
22371         this.editorcore.onFirstFocus();
22372          for (var i =0; i < this.toolbars.length;i++) {
22373             this.toolbars[i].onFirstFocus();
22374         }
22375         
22376     },
22377     
22378     // private
22379     syncValue : function()
22380     {   
22381         this.editorcore.syncValue();
22382     },
22383     
22384     pushValue : function()
22385     {   
22386         this.editorcore.pushValue();
22387     }
22388      
22389     
22390     // hide stuff that is not compatible
22391     /**
22392      * @event blur
22393      * @hide
22394      */
22395     /**
22396      * @event change
22397      * @hide
22398      */
22399     /**
22400      * @event focus
22401      * @hide
22402      */
22403     /**
22404      * @event specialkey
22405      * @hide
22406      */
22407     /**
22408      * @cfg {String} fieldClass @hide
22409      */
22410     /**
22411      * @cfg {String} focusClass @hide
22412      */
22413     /**
22414      * @cfg {String} autoCreate @hide
22415      */
22416     /**
22417      * @cfg {String} inputType @hide
22418      */
22419     /**
22420      * @cfg {String} invalidClass @hide
22421      */
22422     /**
22423      * @cfg {String} invalidText @hide
22424      */
22425     /**
22426      * @cfg {String} msgFx @hide
22427      */
22428     /**
22429      * @cfg {String} validateOnBlur @hide
22430      */
22431 });
22432  
22433     
22434    
22435    
22436    
22437       
22438 Roo.namespace('Roo.bootstrap.htmleditor');
22439 /**
22440  * @class Roo.bootstrap.HtmlEditorToolbar1
22441  * Basic Toolbar
22442  * 
22443  * Usage:
22444  *
22445  new Roo.bootstrap.HtmlEditor({
22446     ....
22447     toolbars : [
22448         new Roo.bootstrap.HtmlEditorToolbar1({
22449             disable : { fonts: 1 , format: 1, ..., ... , ...],
22450             btns : [ .... ]
22451         })
22452     }
22453      
22454  * 
22455  * @cfg {Object} disable List of elements to disable..
22456  * @cfg {Array} btns List of additional buttons.
22457  * 
22458  * 
22459  * NEEDS Extra CSS? 
22460  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22461  */
22462  
22463 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22464 {
22465     
22466     Roo.apply(this, config);
22467     
22468     // default disabled, based on 'good practice'..
22469     this.disable = this.disable || {};
22470     Roo.applyIf(this.disable, {
22471         fontSize : true,
22472         colors : true,
22473         specialElements : true
22474     });
22475     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22476     
22477     this.editor = config.editor;
22478     this.editorcore = config.editor.editorcore;
22479     
22480     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22481     
22482     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22483     // dont call parent... till later.
22484 }
22485 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22486      
22487     bar : true,
22488     
22489     editor : false,
22490     editorcore : false,
22491     
22492     
22493     formats : [
22494         "p" ,  
22495         "h1","h2","h3","h4","h5","h6", 
22496         "pre", "code", 
22497         "abbr", "acronym", "address", "cite", "samp", "var",
22498         'div','span'
22499     ],
22500     
22501     onRender : function(ct, position)
22502     {
22503        // Roo.log("Call onRender: " + this.xtype);
22504         
22505        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22506        Roo.log(this.el);
22507        this.el.dom.style.marginBottom = '0';
22508        var _this = this;
22509        var editorcore = this.editorcore;
22510        var editor= this.editor;
22511        
22512        var children = [];
22513        var btn = function(id,cmd , toggle, handler){
22514        
22515             var  event = toggle ? 'toggle' : 'click';
22516        
22517             var a = {
22518                 size : 'sm',
22519                 xtype: 'Button',
22520                 xns: Roo.bootstrap,
22521                 glyphicon : id,
22522                 cmd : id || cmd,
22523                 enableToggle:toggle !== false,
22524                 //html : 'submit'
22525                 pressed : toggle ? false : null,
22526                 listeners : {}
22527             };
22528             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22529                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22530             };
22531             children.push(a);
22532             return a;
22533        }
22534         
22535         var style = {
22536                 xtype: 'Button',
22537                 size : 'sm',
22538                 xns: Roo.bootstrap,
22539                 glyphicon : 'font',
22540                 //html : 'submit'
22541                 menu : {
22542                     xtype: 'Menu',
22543                     xns: Roo.bootstrap,
22544                     items:  []
22545                 }
22546         };
22547         Roo.each(this.formats, function(f) {
22548             style.menu.items.push({
22549                 xtype :'MenuItem',
22550                 xns: Roo.bootstrap,
22551                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22552                 tagname : f,
22553                 listeners : {
22554                     click : function()
22555                     {
22556                         editorcore.insertTag(this.tagname);
22557                         editor.focus();
22558                     }
22559                 }
22560                 
22561             });
22562         });
22563          children.push(style);   
22564             
22565             
22566         btn('bold',false,true);
22567         btn('italic',false,true);
22568         btn('align-left', 'justifyleft',true);
22569         btn('align-center', 'justifycenter',true);
22570         btn('align-right' , 'justifyright',true);
22571         btn('link', false, false, function(btn) {
22572             //Roo.log("create link?");
22573             var url = prompt(this.createLinkText, this.defaultLinkValue);
22574             if(url && url != 'http:/'+'/'){
22575                 this.editorcore.relayCmd('createlink', url);
22576             }
22577         }),
22578         btn('list','insertunorderedlist',true);
22579         btn('pencil', false,true, function(btn){
22580                 Roo.log(this);
22581                 
22582                 this.toggleSourceEdit(btn.pressed);
22583         });
22584         /*
22585         var cog = {
22586                 xtype: 'Button',
22587                 size : 'sm',
22588                 xns: Roo.bootstrap,
22589                 glyphicon : 'cog',
22590                 //html : 'submit'
22591                 menu : {
22592                     xtype: 'Menu',
22593                     xns: Roo.bootstrap,
22594                     items:  []
22595                 }
22596         };
22597         
22598         cog.menu.items.push({
22599             xtype :'MenuItem',
22600             xns: Roo.bootstrap,
22601             html : Clean styles,
22602             tagname : f,
22603             listeners : {
22604                 click : function()
22605                 {
22606                     editorcore.insertTag(this.tagname);
22607                     editor.focus();
22608                 }
22609             }
22610             
22611         });
22612        */
22613         
22614          
22615        this.xtype = 'NavSimplebar';
22616         
22617         for(var i=0;i< children.length;i++) {
22618             
22619             this.buttons.add(this.addxtypeChild(children[i]));
22620             
22621         }
22622         
22623         editor.on('editorevent', this.updateToolbar, this);
22624     },
22625     onBtnClick : function(id)
22626     {
22627        this.editorcore.relayCmd(id);
22628        this.editorcore.focus();
22629     },
22630     
22631     /**
22632      * Protected method that will not generally be called directly. It triggers
22633      * a toolbar update by reading the markup state of the current selection in the editor.
22634      */
22635     updateToolbar: function(){
22636
22637         if(!this.editorcore.activated){
22638             this.editor.onFirstFocus(); // is this neeed?
22639             return;
22640         }
22641
22642         var btns = this.buttons; 
22643         var doc = this.editorcore.doc;
22644         btns.get('bold').setActive(doc.queryCommandState('bold'));
22645         btns.get('italic').setActive(doc.queryCommandState('italic'));
22646         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22647         
22648         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22649         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22650         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22651         
22652         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22653         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22654          /*
22655         
22656         var ans = this.editorcore.getAllAncestors();
22657         if (this.formatCombo) {
22658             
22659             
22660             var store = this.formatCombo.store;
22661             this.formatCombo.setValue("");
22662             for (var i =0; i < ans.length;i++) {
22663                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22664                     // select it..
22665                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22666                     break;
22667                 }
22668             }
22669         }
22670         
22671         
22672         
22673         // hides menus... - so this cant be on a menu...
22674         Roo.bootstrap.MenuMgr.hideAll();
22675         */
22676         Roo.bootstrap.MenuMgr.hideAll();
22677         //this.editorsyncValue();
22678     },
22679     onFirstFocus: function() {
22680         this.buttons.each(function(item){
22681            item.enable();
22682         });
22683     },
22684     toggleSourceEdit : function(sourceEditMode){
22685         
22686           
22687         if(sourceEditMode){
22688             Roo.log("disabling buttons");
22689            this.buttons.each( function(item){
22690                 if(item.cmd != 'pencil'){
22691                     item.disable();
22692                 }
22693             });
22694           
22695         }else{
22696             Roo.log("enabling buttons");
22697             if(this.editorcore.initialized){
22698                 this.buttons.each( function(item){
22699                     item.enable();
22700                 });
22701             }
22702             
22703         }
22704         Roo.log("calling toggole on editor");
22705         // tell the editor that it's been pressed..
22706         this.editor.toggleSourceEdit(sourceEditMode);
22707        
22708     }
22709 });
22710
22711
22712
22713
22714
22715 /**
22716  * @class Roo.bootstrap.Table.AbstractSelectionModel
22717  * @extends Roo.util.Observable
22718  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22719  * implemented by descendant classes.  This class should not be directly instantiated.
22720  * @constructor
22721  */
22722 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22723     this.locked = false;
22724     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22725 };
22726
22727
22728 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22729     /** @ignore Called by the grid automatically. Do not call directly. */
22730     init : function(grid){
22731         this.grid = grid;
22732         this.initEvents();
22733     },
22734
22735     /**
22736      * Locks the selections.
22737      */
22738     lock : function(){
22739         this.locked = true;
22740     },
22741
22742     /**
22743      * Unlocks the selections.
22744      */
22745     unlock : function(){
22746         this.locked = false;
22747     },
22748
22749     /**
22750      * Returns true if the selections are locked.
22751      * @return {Boolean}
22752      */
22753     isLocked : function(){
22754         return this.locked;
22755     }
22756 });
22757 /**
22758  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22759  * @class Roo.bootstrap.Table.RowSelectionModel
22760  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22761  * It supports multiple selections and keyboard selection/navigation. 
22762  * @constructor
22763  * @param {Object} config
22764  */
22765
22766 Roo.bootstrap.Table.RowSelectionModel = function(config){
22767     Roo.apply(this, config);
22768     this.selections = new Roo.util.MixedCollection(false, function(o){
22769         return o.id;
22770     });
22771
22772     this.last = false;
22773     this.lastActive = false;
22774
22775     this.addEvents({
22776         /**
22777              * @event selectionchange
22778              * Fires when the selection changes
22779              * @param {SelectionModel} this
22780              */
22781             "selectionchange" : true,
22782         /**
22783              * @event afterselectionchange
22784              * Fires after the selection changes (eg. by key press or clicking)
22785              * @param {SelectionModel} this
22786              */
22787             "afterselectionchange" : true,
22788         /**
22789              * @event beforerowselect
22790              * Fires when a row is selected being selected, return false to cancel.
22791              * @param {SelectionModel} this
22792              * @param {Number} rowIndex The selected index
22793              * @param {Boolean} keepExisting False if other selections will be cleared
22794              */
22795             "beforerowselect" : true,
22796         /**
22797              * @event rowselect
22798              * Fires when a row is selected.
22799              * @param {SelectionModel} this
22800              * @param {Number} rowIndex The selected index
22801              * @param {Roo.data.Record} r The record
22802              */
22803             "rowselect" : true,
22804         /**
22805              * @event rowdeselect
22806              * Fires when a row is deselected.
22807              * @param {SelectionModel} this
22808              * @param {Number} rowIndex The selected index
22809              */
22810         "rowdeselect" : true
22811     });
22812     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22813     this.locked = false;
22814  };
22815
22816 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22817     /**
22818      * @cfg {Boolean} singleSelect
22819      * True to allow selection of only one row at a time (defaults to false)
22820      */
22821     singleSelect : false,
22822
22823     // private
22824     initEvents : function()
22825     {
22826
22827         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22828         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22829         //}else{ // allow click to work like normal
22830          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22831         //}
22832         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22833         this.grid.on("rowclick", this.handleMouseDown, this);
22834         
22835         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22836             "up" : function(e){
22837                 if(!e.shiftKey){
22838                     this.selectPrevious(e.shiftKey);
22839                 }else if(this.last !== false && this.lastActive !== false){
22840                     var last = this.last;
22841                     this.selectRange(this.last,  this.lastActive-1);
22842                     this.grid.getView().focusRow(this.lastActive);
22843                     if(last !== false){
22844                         this.last = last;
22845                     }
22846                 }else{
22847                     this.selectFirstRow();
22848                 }
22849                 this.fireEvent("afterselectionchange", this);
22850             },
22851             "down" : function(e){
22852                 if(!e.shiftKey){
22853                     this.selectNext(e.shiftKey);
22854                 }else if(this.last !== false && this.lastActive !== false){
22855                     var last = this.last;
22856                     this.selectRange(this.last,  this.lastActive+1);
22857                     this.grid.getView().focusRow(this.lastActive);
22858                     if(last !== false){
22859                         this.last = last;
22860                     }
22861                 }else{
22862                     this.selectFirstRow();
22863                 }
22864                 this.fireEvent("afterselectionchange", this);
22865             },
22866             scope: this
22867         });
22868         this.grid.store.on('load', function(){
22869             this.selections.clear();
22870         },this);
22871         /*
22872         var view = this.grid.view;
22873         view.on("refresh", this.onRefresh, this);
22874         view.on("rowupdated", this.onRowUpdated, this);
22875         view.on("rowremoved", this.onRemove, this);
22876         */
22877     },
22878
22879     // private
22880     onRefresh : function()
22881     {
22882         var ds = this.grid.store, i, v = this.grid.view;
22883         var s = this.selections;
22884         s.each(function(r){
22885             if((i = ds.indexOfId(r.id)) != -1){
22886                 v.onRowSelect(i);
22887             }else{
22888                 s.remove(r);
22889             }
22890         });
22891     },
22892
22893     // private
22894     onRemove : function(v, index, r){
22895         this.selections.remove(r);
22896     },
22897
22898     // private
22899     onRowUpdated : function(v, index, r){
22900         if(this.isSelected(r)){
22901             v.onRowSelect(index);
22902         }
22903     },
22904
22905     /**
22906      * Select records.
22907      * @param {Array} records The records to select
22908      * @param {Boolean} keepExisting (optional) True to keep existing selections
22909      */
22910     selectRecords : function(records, keepExisting)
22911     {
22912         if(!keepExisting){
22913             this.clearSelections();
22914         }
22915             var ds = this.grid.store;
22916         for(var i = 0, len = records.length; i < len; i++){
22917             this.selectRow(ds.indexOf(records[i]), true);
22918         }
22919     },
22920
22921     /**
22922      * Gets the number of selected rows.
22923      * @return {Number}
22924      */
22925     getCount : function(){
22926         return this.selections.length;
22927     },
22928
22929     /**
22930      * Selects the first row in the grid.
22931      */
22932     selectFirstRow : function(){
22933         this.selectRow(0);
22934     },
22935
22936     /**
22937      * Select the last row.
22938      * @param {Boolean} keepExisting (optional) True to keep existing selections
22939      */
22940     selectLastRow : function(keepExisting){
22941         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22942         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22943     },
22944
22945     /**
22946      * Selects the row immediately following the last selected row.
22947      * @param {Boolean} keepExisting (optional) True to keep existing selections
22948      */
22949     selectNext : function(keepExisting)
22950     {
22951             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22952             this.selectRow(this.last+1, keepExisting);
22953             this.grid.getView().focusRow(this.last);
22954         }
22955     },
22956
22957     /**
22958      * Selects the row that precedes the last selected row.
22959      * @param {Boolean} keepExisting (optional) True to keep existing selections
22960      */
22961     selectPrevious : function(keepExisting){
22962         if(this.last){
22963             this.selectRow(this.last-1, keepExisting);
22964             this.grid.getView().focusRow(this.last);
22965         }
22966     },
22967
22968     /**
22969      * Returns the selected records
22970      * @return {Array} Array of selected records
22971      */
22972     getSelections : function(){
22973         return [].concat(this.selections.items);
22974     },
22975
22976     /**
22977      * Returns the first selected record.
22978      * @return {Record}
22979      */
22980     getSelected : function(){
22981         return this.selections.itemAt(0);
22982     },
22983
22984
22985     /**
22986      * Clears all selections.
22987      */
22988     clearSelections : function(fast)
22989     {
22990         if(this.locked) {
22991             return;
22992         }
22993         if(fast !== true){
22994                 var ds = this.grid.store;
22995             var s = this.selections;
22996             s.each(function(r){
22997                 this.deselectRow(ds.indexOfId(r.id));
22998             }, this);
22999             s.clear();
23000         }else{
23001             this.selections.clear();
23002         }
23003         this.last = false;
23004     },
23005
23006
23007     /**
23008      * Selects all rows.
23009      */
23010     selectAll : function(){
23011         if(this.locked) {
23012             return;
23013         }
23014         this.selections.clear();
23015         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23016             this.selectRow(i, true);
23017         }
23018     },
23019
23020     /**
23021      * Returns True if there is a selection.
23022      * @return {Boolean}
23023      */
23024     hasSelection : function(){
23025         return this.selections.length > 0;
23026     },
23027
23028     /**
23029      * Returns True if the specified row is selected.
23030      * @param {Number/Record} record The record or index of the record to check
23031      * @return {Boolean}
23032      */
23033     isSelected : function(index){
23034             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23035         return (r && this.selections.key(r.id) ? true : false);
23036     },
23037
23038     /**
23039      * Returns True if the specified record id is selected.
23040      * @param {String} id The id of record to check
23041      * @return {Boolean}
23042      */
23043     isIdSelected : function(id){
23044         return (this.selections.key(id) ? true : false);
23045     },
23046
23047
23048     // private
23049     handleMouseDBClick : function(e, t){
23050         
23051     },
23052     // private
23053     handleMouseDown : function(e, t)
23054     {
23055             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23056         if(this.isLocked() || rowIndex < 0 ){
23057             return;
23058         };
23059         if(e.shiftKey && this.last !== false){
23060             var last = this.last;
23061             this.selectRange(last, rowIndex, e.ctrlKey);
23062             this.last = last; // reset the last
23063             t.focus();
23064     
23065         }else{
23066             var isSelected = this.isSelected(rowIndex);
23067             //Roo.log("select row:" + rowIndex);
23068             if(isSelected){
23069                 this.deselectRow(rowIndex);
23070             } else {
23071                         this.selectRow(rowIndex, true);
23072             }
23073     
23074             /*
23075                 if(e.button !== 0 && isSelected){
23076                 alert('rowIndex 2: ' + rowIndex);
23077                     view.focusRow(rowIndex);
23078                 }else if(e.ctrlKey && isSelected){
23079                     this.deselectRow(rowIndex);
23080                 }else if(!isSelected){
23081                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23082                     view.focusRow(rowIndex);
23083                 }
23084             */
23085         }
23086         this.fireEvent("afterselectionchange", this);
23087     },
23088     // private
23089     handleDragableRowClick :  function(grid, rowIndex, e) 
23090     {
23091         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23092             this.selectRow(rowIndex, false);
23093             grid.view.focusRow(rowIndex);
23094              this.fireEvent("afterselectionchange", this);
23095         }
23096     },
23097     
23098     /**
23099      * Selects multiple rows.
23100      * @param {Array} rows Array of the indexes of the row to select
23101      * @param {Boolean} keepExisting (optional) True to keep existing selections
23102      */
23103     selectRows : function(rows, keepExisting){
23104         if(!keepExisting){
23105             this.clearSelections();
23106         }
23107         for(var i = 0, len = rows.length; i < len; i++){
23108             this.selectRow(rows[i], true);
23109         }
23110     },
23111
23112     /**
23113      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23114      * @param {Number} startRow The index of the first row in the range
23115      * @param {Number} endRow The index of the last row in the range
23116      * @param {Boolean} keepExisting (optional) True to retain existing selections
23117      */
23118     selectRange : function(startRow, endRow, keepExisting){
23119         if(this.locked) {
23120             return;
23121         }
23122         if(!keepExisting){
23123             this.clearSelections();
23124         }
23125         if(startRow <= endRow){
23126             for(var i = startRow; i <= endRow; i++){
23127                 this.selectRow(i, true);
23128             }
23129         }else{
23130             for(var i = startRow; i >= endRow; i--){
23131                 this.selectRow(i, true);
23132             }
23133         }
23134     },
23135
23136     /**
23137      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23138      * @param {Number} startRow The index of the first row in the range
23139      * @param {Number} endRow The index of the last row in the range
23140      */
23141     deselectRange : function(startRow, endRow, preventViewNotify){
23142         if(this.locked) {
23143             return;
23144         }
23145         for(var i = startRow; i <= endRow; i++){
23146             this.deselectRow(i, preventViewNotify);
23147         }
23148     },
23149
23150     /**
23151      * Selects a row.
23152      * @param {Number} row The index of the row to select
23153      * @param {Boolean} keepExisting (optional) True to keep existing selections
23154      */
23155     selectRow : function(index, keepExisting, preventViewNotify)
23156     {
23157             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23158             return;
23159         }
23160         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23161             if(!keepExisting || this.singleSelect){
23162                 this.clearSelections();
23163             }
23164             
23165             var r = this.grid.store.getAt(index);
23166             //console.log('selectRow - record id :' + r.id);
23167             
23168             this.selections.add(r);
23169             this.last = this.lastActive = index;
23170             if(!preventViewNotify){
23171                 var proxy = new Roo.Element(
23172                                 this.grid.getRowDom(index)
23173                 );
23174                 proxy.addClass('bg-info info');
23175             }
23176             this.fireEvent("rowselect", this, index, r);
23177             this.fireEvent("selectionchange", this);
23178         }
23179     },
23180
23181     /**
23182      * Deselects a row.
23183      * @param {Number} row The index of the row to deselect
23184      */
23185     deselectRow : function(index, preventViewNotify)
23186     {
23187         if(this.locked) {
23188             return;
23189         }
23190         if(this.last == index){
23191             this.last = false;
23192         }
23193         if(this.lastActive == index){
23194             this.lastActive = false;
23195         }
23196         
23197         var r = this.grid.store.getAt(index);
23198         if (!r) {
23199             return;
23200         }
23201         
23202         this.selections.remove(r);
23203         //.console.log('deselectRow - record id :' + r.id);
23204         if(!preventViewNotify){
23205         
23206             var proxy = new Roo.Element(
23207                 this.grid.getRowDom(index)
23208             );
23209             proxy.removeClass('bg-info info');
23210         }
23211         this.fireEvent("rowdeselect", this, index);
23212         this.fireEvent("selectionchange", this);
23213     },
23214
23215     // private
23216     restoreLast : function(){
23217         if(this._last){
23218             this.last = this._last;
23219         }
23220     },
23221
23222     // private
23223     acceptsNav : function(row, col, cm){
23224         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23225     },
23226
23227     // private
23228     onEditorKey : function(field, e){
23229         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23230         if(k == e.TAB){
23231             e.stopEvent();
23232             ed.completeEdit();
23233             if(e.shiftKey){
23234                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23235             }else{
23236                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23237             }
23238         }else if(k == e.ENTER && !e.ctrlKey){
23239             e.stopEvent();
23240             ed.completeEdit();
23241             if(e.shiftKey){
23242                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23243             }else{
23244                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23245             }
23246         }else if(k == e.ESC){
23247             ed.cancelEdit();
23248         }
23249         if(newCell){
23250             g.startEditing(newCell[0], newCell[1]);
23251         }
23252     }
23253 });
23254 /*
23255  * Based on:
23256  * Ext JS Library 1.1.1
23257  * Copyright(c) 2006-2007, Ext JS, LLC.
23258  *
23259  * Originally Released Under LGPL - original licence link has changed is not relivant.
23260  *
23261  * Fork - LGPL
23262  * <script type="text/javascript">
23263  */
23264  
23265 /**
23266  * @class Roo.bootstrap.PagingToolbar
23267  * @extends Roo.bootstrap.NavSimplebar
23268  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23269  * @constructor
23270  * Create a new PagingToolbar
23271  * @param {Object} config The config object
23272  * @param {Roo.data.Store} store
23273  */
23274 Roo.bootstrap.PagingToolbar = function(config)
23275 {
23276     // old args format still supported... - xtype is prefered..
23277         // created from xtype...
23278     
23279     this.ds = config.dataSource;
23280     
23281     if (config.store && !this.ds) {
23282         this.store= Roo.factory(config.store, Roo.data);
23283         this.ds = this.store;
23284         this.ds.xmodule = this.xmodule || false;
23285     }
23286     
23287     this.toolbarItems = [];
23288     if (config.items) {
23289         this.toolbarItems = config.items;
23290     }
23291     
23292     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23293     
23294     this.cursor = 0;
23295     
23296     if (this.ds) { 
23297         this.bind(this.ds);
23298     }
23299     
23300     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23301     
23302 };
23303
23304 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23305     /**
23306      * @cfg {Roo.data.Store} dataSource
23307      * The underlying data store providing the paged data
23308      */
23309     /**
23310      * @cfg {String/HTMLElement/Element} container
23311      * container The id or element that will contain the toolbar
23312      */
23313     /**
23314      * @cfg {Boolean} displayInfo
23315      * True to display the displayMsg (defaults to false)
23316      */
23317     /**
23318      * @cfg {Number} pageSize
23319      * The number of records to display per page (defaults to 20)
23320      */
23321     pageSize: 20,
23322     /**
23323      * @cfg {String} displayMsg
23324      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23325      */
23326     displayMsg : 'Displaying {0} - {1} of {2}',
23327     /**
23328      * @cfg {String} emptyMsg
23329      * The message to display when no records are found (defaults to "No data to display")
23330      */
23331     emptyMsg : 'No data to display',
23332     /**
23333      * Customizable piece of the default paging text (defaults to "Page")
23334      * @type String
23335      */
23336     beforePageText : "Page",
23337     /**
23338      * Customizable piece of the default paging text (defaults to "of %0")
23339      * @type String
23340      */
23341     afterPageText : "of {0}",
23342     /**
23343      * Customizable piece of the default paging text (defaults to "First Page")
23344      * @type String
23345      */
23346     firstText : "First Page",
23347     /**
23348      * Customizable piece of the default paging text (defaults to "Previous Page")
23349      * @type String
23350      */
23351     prevText : "Previous Page",
23352     /**
23353      * Customizable piece of the default paging text (defaults to "Next Page")
23354      * @type String
23355      */
23356     nextText : "Next Page",
23357     /**
23358      * Customizable piece of the default paging text (defaults to "Last Page")
23359      * @type String
23360      */
23361     lastText : "Last Page",
23362     /**
23363      * Customizable piece of the default paging text (defaults to "Refresh")
23364      * @type String
23365      */
23366     refreshText : "Refresh",
23367
23368     buttons : false,
23369     // private
23370     onRender : function(ct, position) 
23371     {
23372         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23373         this.navgroup.parentId = this.id;
23374         this.navgroup.onRender(this.el, null);
23375         // add the buttons to the navgroup
23376         
23377         if(this.displayInfo){
23378             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23379             this.displayEl = this.el.select('.x-paging-info', true).first();
23380 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23381 //            this.displayEl = navel.el.select('span',true).first();
23382         }
23383         
23384         var _this = this;
23385         
23386         if(this.buttons){
23387             Roo.each(_this.buttons, function(e){ // this might need to use render????
23388                Roo.factory(e).onRender(_this.el, null);
23389             });
23390         }
23391             
23392         Roo.each(_this.toolbarItems, function(e) {
23393             _this.navgroup.addItem(e);
23394         });
23395         
23396         
23397         this.first = this.navgroup.addItem({
23398             tooltip: this.firstText,
23399             cls: "prev",
23400             icon : 'fa fa-backward',
23401             disabled: true,
23402             preventDefault: true,
23403             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23404         });
23405         
23406         this.prev =  this.navgroup.addItem({
23407             tooltip: this.prevText,
23408             cls: "prev",
23409             icon : 'fa fa-step-backward',
23410             disabled: true,
23411             preventDefault: true,
23412             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23413         });
23414     //this.addSeparator();
23415         
23416         
23417         var field = this.navgroup.addItem( {
23418             tagtype : 'span',
23419             cls : 'x-paging-position',
23420             
23421             html : this.beforePageText  +
23422                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23423                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23424          } ); //?? escaped?
23425         
23426         this.field = field.el.select('input', true).first();
23427         this.field.on("keydown", this.onPagingKeydown, this);
23428         this.field.on("focus", function(){this.dom.select();});
23429     
23430     
23431         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23432         //this.field.setHeight(18);
23433         //this.addSeparator();
23434         this.next = this.navgroup.addItem({
23435             tooltip: this.nextText,
23436             cls: "next",
23437             html : ' <i class="fa fa-step-forward">',
23438             disabled: true,
23439             preventDefault: true,
23440             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23441         });
23442         this.last = this.navgroup.addItem({
23443             tooltip: this.lastText,
23444             icon : 'fa fa-forward',
23445             cls: "next",
23446             disabled: true,
23447             preventDefault: true,
23448             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23449         });
23450     //this.addSeparator();
23451         this.loading = this.navgroup.addItem({
23452             tooltip: this.refreshText,
23453             icon: 'fa fa-refresh',
23454             preventDefault: true,
23455             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23456         });
23457         
23458     },
23459
23460     // private
23461     updateInfo : function(){
23462         if(this.displayEl){
23463             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23464             var msg = count == 0 ?
23465                 this.emptyMsg :
23466                 String.format(
23467                     this.displayMsg,
23468                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23469                 );
23470             this.displayEl.update(msg);
23471         }
23472     },
23473
23474     // private
23475     onLoad : function(ds, r, o){
23476        this.cursor = o.params ? o.params.start : 0;
23477        var d = this.getPageData(),
23478             ap = d.activePage,
23479             ps = d.pages;
23480         
23481        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23482        this.field.dom.value = ap;
23483        this.first.setDisabled(ap == 1);
23484        this.prev.setDisabled(ap == 1);
23485        this.next.setDisabled(ap == ps);
23486        this.last.setDisabled(ap == ps);
23487        this.loading.enable();
23488        this.updateInfo();
23489     },
23490
23491     // private
23492     getPageData : function(){
23493         var total = this.ds.getTotalCount();
23494         return {
23495             total : total,
23496             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23497             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23498         };
23499     },
23500
23501     // private
23502     onLoadError : function(){
23503         this.loading.enable();
23504     },
23505
23506     // private
23507     onPagingKeydown : function(e){
23508         var k = e.getKey();
23509         var d = this.getPageData();
23510         if(k == e.RETURN){
23511             var v = this.field.dom.value, pageNum;
23512             if(!v || isNaN(pageNum = parseInt(v, 10))){
23513                 this.field.dom.value = d.activePage;
23514                 return;
23515             }
23516             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23517             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23518             e.stopEvent();
23519         }
23520         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))
23521         {
23522           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23523           this.field.dom.value = pageNum;
23524           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23525           e.stopEvent();
23526         }
23527         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23528         {
23529           var v = this.field.dom.value, pageNum; 
23530           var increment = (e.shiftKey) ? 10 : 1;
23531           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23532                 increment *= -1;
23533           }
23534           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23535             this.field.dom.value = d.activePage;
23536             return;
23537           }
23538           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23539           {
23540             this.field.dom.value = parseInt(v, 10) + increment;
23541             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23542             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23543           }
23544           e.stopEvent();
23545         }
23546     },
23547
23548     // private
23549     beforeLoad : function(){
23550         if(this.loading){
23551             this.loading.disable();
23552         }
23553     },
23554
23555     // private
23556     onClick : function(which){
23557         
23558         var ds = this.ds;
23559         if (!ds) {
23560             return;
23561         }
23562         
23563         switch(which){
23564             case "first":
23565                 ds.load({params:{start: 0, limit: this.pageSize}});
23566             break;
23567             case "prev":
23568                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23569             break;
23570             case "next":
23571                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23572             break;
23573             case "last":
23574                 var total = ds.getTotalCount();
23575                 var extra = total % this.pageSize;
23576                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23577                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23578             break;
23579             case "refresh":
23580                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23581             break;
23582         }
23583     },
23584
23585     /**
23586      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23587      * @param {Roo.data.Store} store The data store to unbind
23588      */
23589     unbind : function(ds){
23590         ds.un("beforeload", this.beforeLoad, this);
23591         ds.un("load", this.onLoad, this);
23592         ds.un("loadexception", this.onLoadError, this);
23593         ds.un("remove", this.updateInfo, this);
23594         ds.un("add", this.updateInfo, this);
23595         this.ds = undefined;
23596     },
23597
23598     /**
23599      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23600      * @param {Roo.data.Store} store The data store to bind
23601      */
23602     bind : function(ds){
23603         ds.on("beforeload", this.beforeLoad, this);
23604         ds.on("load", this.onLoad, this);
23605         ds.on("loadexception", this.onLoadError, this);
23606         ds.on("remove", this.updateInfo, this);
23607         ds.on("add", this.updateInfo, this);
23608         this.ds = ds;
23609     }
23610 });/*
23611  * - LGPL
23612  *
23613  * element
23614  * 
23615  */
23616
23617 /**
23618  * @class Roo.bootstrap.MessageBar
23619  * @extends Roo.bootstrap.Component
23620  * Bootstrap MessageBar class
23621  * @cfg {String} html contents of the MessageBar
23622  * @cfg {String} weight (info | success | warning | danger) default info
23623  * @cfg {String} beforeClass insert the bar before the given class
23624  * @cfg {Boolean} closable (true | false) default false
23625  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23626  * 
23627  * @constructor
23628  * Create a new Element
23629  * @param {Object} config The config object
23630  */
23631
23632 Roo.bootstrap.MessageBar = function(config){
23633     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23634 };
23635
23636 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23637     
23638     html: '',
23639     weight: 'info',
23640     closable: false,
23641     fixed: false,
23642     beforeClass: 'bootstrap-sticky-wrap',
23643     
23644     getAutoCreate : function(){
23645         
23646         var cfg = {
23647             tag: 'div',
23648             cls: 'alert alert-dismissable alert-' + this.weight,
23649             cn: [
23650                 {
23651                     tag: 'span',
23652                     cls: 'message',
23653                     html: this.html || ''
23654                 }
23655             ]
23656         };
23657         
23658         if(this.fixed){
23659             cfg.cls += ' alert-messages-fixed';
23660         }
23661         
23662         if(this.closable){
23663             cfg.cn.push({
23664                 tag: 'button',
23665                 cls: 'close',
23666                 html: 'x'
23667             });
23668         }
23669         
23670         return cfg;
23671     },
23672     
23673     onRender : function(ct, position)
23674     {
23675         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23676         
23677         if(!this.el){
23678             var cfg = Roo.apply({},  this.getAutoCreate());
23679             cfg.id = Roo.id();
23680             
23681             if (this.cls) {
23682                 cfg.cls += ' ' + this.cls;
23683             }
23684             if (this.style) {
23685                 cfg.style = this.style;
23686             }
23687             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23688             
23689             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23690         }
23691         
23692         this.el.select('>button.close').on('click', this.hide, this);
23693         
23694     },
23695     
23696     show : function()
23697     {
23698         if (!this.rendered) {
23699             this.render();
23700         }
23701         
23702         this.el.show();
23703         
23704         this.fireEvent('show', this);
23705         
23706     },
23707     
23708     hide : function()
23709     {
23710         if (!this.rendered) {
23711             this.render();
23712         }
23713         
23714         this.el.hide();
23715         
23716         this.fireEvent('hide', this);
23717     },
23718     
23719     update : function()
23720     {
23721 //        var e = this.el.dom.firstChild;
23722 //        
23723 //        if(this.closable){
23724 //            e = e.nextSibling;
23725 //        }
23726 //        
23727 //        e.data = this.html || '';
23728
23729         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23730     }
23731    
23732 });
23733
23734  
23735
23736      /*
23737  * - LGPL
23738  *
23739  * Graph
23740  * 
23741  */
23742
23743
23744 /**
23745  * @class Roo.bootstrap.Graph
23746  * @extends Roo.bootstrap.Component
23747  * Bootstrap Graph class
23748 > Prameters
23749  -sm {number} sm 4
23750  -md {number} md 5
23751  @cfg {String} graphtype  bar | vbar | pie
23752  @cfg {number} g_x coodinator | centre x (pie)
23753  @cfg {number} g_y coodinator | centre y (pie)
23754  @cfg {number} g_r radius (pie)
23755  @cfg {number} g_height height of the chart (respected by all elements in the set)
23756  @cfg {number} g_width width of the chart (respected by all elements in the set)
23757  @cfg {Object} title The title of the chart
23758     
23759  -{Array}  values
23760  -opts (object) options for the chart 
23761      o {
23762      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23763      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23764      o vgutter (number)
23765      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.
23766      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23767      o to
23768      o stretch (boolean)
23769      o }
23770  -opts (object) options for the pie
23771      o{
23772      o cut
23773      o startAngle (number)
23774      o endAngle (number)
23775      } 
23776  *
23777  * @constructor
23778  * Create a new Input
23779  * @param {Object} config The config object
23780  */
23781
23782 Roo.bootstrap.Graph = function(config){
23783     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23784     
23785     this.addEvents({
23786         // img events
23787         /**
23788          * @event click
23789          * The img click event for the img.
23790          * @param {Roo.EventObject} e
23791          */
23792         "click" : true
23793     });
23794 };
23795
23796 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23797     
23798     sm: 4,
23799     md: 5,
23800     graphtype: 'bar',
23801     g_height: 250,
23802     g_width: 400,
23803     g_x: 50,
23804     g_y: 50,
23805     g_r: 30,
23806     opts:{
23807         //g_colors: this.colors,
23808         g_type: 'soft',
23809         g_gutter: '20%'
23810
23811     },
23812     title : false,
23813
23814     getAutoCreate : function(){
23815         
23816         var cfg = {
23817             tag: 'div',
23818             html : null
23819         };
23820         
23821         
23822         return  cfg;
23823     },
23824
23825     onRender : function(ct,position){
23826         
23827         
23828         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23829         
23830         if (typeof(Raphael) == 'undefined') {
23831             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23832             return;
23833         }
23834         
23835         this.raphael = Raphael(this.el.dom);
23836         
23837                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23838                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23839                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23840                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23841                 /*
23842                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23843                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23844                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23845                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23846                 
23847                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23848                 r.barchart(330, 10, 300, 220, data1);
23849                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23850                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23851                 */
23852                 
23853                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23854                 // r.barchart(30, 30, 560, 250,  xdata, {
23855                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23856                 //     axis : "0 0 1 1",
23857                 //     axisxlabels :  xdata
23858                 //     //yvalues : cols,
23859                    
23860                 // });
23861 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23862 //        
23863 //        this.load(null,xdata,{
23864 //                axis : "0 0 1 1",
23865 //                axisxlabels :  xdata
23866 //                });
23867
23868     },
23869
23870     load : function(graphtype,xdata,opts)
23871     {
23872         this.raphael.clear();
23873         if(!graphtype) {
23874             graphtype = this.graphtype;
23875         }
23876         if(!opts){
23877             opts = this.opts;
23878         }
23879         var r = this.raphael,
23880             fin = function () {
23881                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23882             },
23883             fout = function () {
23884                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23885             },
23886             pfin = function() {
23887                 this.sector.stop();
23888                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23889
23890                 if (this.label) {
23891                     this.label[0].stop();
23892                     this.label[0].attr({ r: 7.5 });
23893                     this.label[1].attr({ "font-weight": 800 });
23894                 }
23895             },
23896             pfout = function() {
23897                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23898
23899                 if (this.label) {
23900                     this.label[0].animate({ r: 5 }, 500, "bounce");
23901                     this.label[1].attr({ "font-weight": 400 });
23902                 }
23903             };
23904
23905         switch(graphtype){
23906             case 'bar':
23907                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23908                 break;
23909             case 'hbar':
23910                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23911                 break;
23912             case 'pie':
23913 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23914 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23915 //            
23916                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23917                 
23918                 break;
23919
23920         }
23921         
23922         if(this.title){
23923             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23924         }
23925         
23926     },
23927     
23928     setTitle: function(o)
23929     {
23930         this.title = o;
23931     },
23932     
23933     initEvents: function() {
23934         
23935         if(!this.href){
23936             this.el.on('click', this.onClick, this);
23937         }
23938     },
23939     
23940     onClick : function(e)
23941     {
23942         Roo.log('img onclick');
23943         this.fireEvent('click', this, e);
23944     }
23945    
23946 });
23947
23948  
23949 /*
23950  * - LGPL
23951  *
23952  * numberBox
23953  * 
23954  */
23955 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23956
23957 /**
23958  * @class Roo.bootstrap.dash.NumberBox
23959  * @extends Roo.bootstrap.Component
23960  * Bootstrap NumberBox class
23961  * @cfg {String} headline Box headline
23962  * @cfg {String} content Box content
23963  * @cfg {String} icon Box icon
23964  * @cfg {String} footer Footer text
23965  * @cfg {String} fhref Footer href
23966  * 
23967  * @constructor
23968  * Create a new NumberBox
23969  * @param {Object} config The config object
23970  */
23971
23972
23973 Roo.bootstrap.dash.NumberBox = function(config){
23974     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23975     
23976 };
23977
23978 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23979     
23980     headline : '',
23981     content : '',
23982     icon : '',
23983     footer : '',
23984     fhref : '',
23985     ficon : '',
23986     
23987     getAutoCreate : function(){
23988         
23989         var cfg = {
23990             tag : 'div',
23991             cls : 'small-box ',
23992             cn : [
23993                 {
23994                     tag : 'div',
23995                     cls : 'inner',
23996                     cn :[
23997                         {
23998                             tag : 'h3',
23999                             cls : 'roo-headline',
24000                             html : this.headline
24001                         },
24002                         {
24003                             tag : 'p',
24004                             cls : 'roo-content',
24005                             html : this.content
24006                         }
24007                     ]
24008                 }
24009             ]
24010         };
24011         
24012         if(this.icon){
24013             cfg.cn.push({
24014                 tag : 'div',
24015                 cls : 'icon',
24016                 cn :[
24017                     {
24018                         tag : 'i',
24019                         cls : 'ion ' + this.icon
24020                     }
24021                 ]
24022             });
24023         }
24024         
24025         if(this.footer){
24026             var footer = {
24027                 tag : 'a',
24028                 cls : 'small-box-footer',
24029                 href : this.fhref || '#',
24030                 html : this.footer
24031             };
24032             
24033             cfg.cn.push(footer);
24034             
24035         }
24036         
24037         return  cfg;
24038     },
24039
24040     onRender : function(ct,position){
24041         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24042
24043
24044        
24045                 
24046     },
24047
24048     setHeadline: function (value)
24049     {
24050         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24051     },
24052     
24053     setFooter: function (value, href)
24054     {
24055         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24056         
24057         if(href){
24058             this.el.select('a.small-box-footer',true).first().attr('href', href);
24059         }
24060         
24061     },
24062
24063     setContent: function (value)
24064     {
24065         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24066     },
24067
24068     initEvents: function() 
24069     {   
24070         
24071     }
24072     
24073 });
24074
24075  
24076 /*
24077  * - LGPL
24078  *
24079  * TabBox
24080  * 
24081  */
24082 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24083
24084 /**
24085  * @class Roo.bootstrap.dash.TabBox
24086  * @extends Roo.bootstrap.Component
24087  * Bootstrap TabBox class
24088  * @cfg {String} title Title of the TabBox
24089  * @cfg {String} icon Icon of the TabBox
24090  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24091  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24092  * 
24093  * @constructor
24094  * Create a new TabBox
24095  * @param {Object} config The config object
24096  */
24097
24098
24099 Roo.bootstrap.dash.TabBox = function(config){
24100     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24101     this.addEvents({
24102         // raw events
24103         /**
24104          * @event addpane
24105          * When a pane is added
24106          * @param {Roo.bootstrap.dash.TabPane} pane
24107          */
24108         "addpane" : true,
24109         /**
24110          * @event activatepane
24111          * When a pane is activated
24112          * @param {Roo.bootstrap.dash.TabPane} pane
24113          */
24114         "activatepane" : true
24115         
24116          
24117     });
24118     
24119     this.panes = [];
24120 };
24121
24122 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24123
24124     title : '',
24125     icon : false,
24126     showtabs : true,
24127     tabScrollable : false,
24128     
24129     getChildContainer : function()
24130     {
24131         return this.el.select('.tab-content', true).first();
24132     },
24133     
24134     getAutoCreate : function(){
24135         
24136         var header = {
24137             tag: 'li',
24138             cls: 'pull-left header',
24139             html: this.title,
24140             cn : []
24141         };
24142         
24143         if(this.icon){
24144             header.cn.push({
24145                 tag: 'i',
24146                 cls: 'fa ' + this.icon
24147             });
24148         }
24149         
24150         var h = {
24151             tag: 'ul',
24152             cls: 'nav nav-tabs pull-right',
24153             cn: [
24154                 header
24155             ]
24156         };
24157         
24158         if(this.tabScrollable){
24159             h = {
24160                 tag: 'div',
24161                 cls: 'tab-header',
24162                 cn: [
24163                     {
24164                         tag: 'ul',
24165                         cls: 'nav nav-tabs pull-right',
24166                         cn: [
24167                             header
24168                         ]
24169                     }
24170                 ]
24171             };
24172         }
24173         
24174         var cfg = {
24175             tag: 'div',
24176             cls: 'nav-tabs-custom',
24177             cn: [
24178                 h,
24179                 {
24180                     tag: 'div',
24181                     cls: 'tab-content no-padding',
24182                     cn: []
24183                 }
24184             ]
24185         };
24186
24187         return  cfg;
24188     },
24189     initEvents : function()
24190     {
24191         //Roo.log('add add pane handler');
24192         this.on('addpane', this.onAddPane, this);
24193     },
24194      /**
24195      * Updates the box title
24196      * @param {String} html to set the title to.
24197      */
24198     setTitle : function(value)
24199     {
24200         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24201     },
24202     onAddPane : function(pane)
24203     {
24204         this.panes.push(pane);
24205         //Roo.log('addpane');
24206         //Roo.log(pane);
24207         // tabs are rendere left to right..
24208         if(!this.showtabs){
24209             return;
24210         }
24211         
24212         var ctr = this.el.select('.nav-tabs', true).first();
24213          
24214          
24215         var existing = ctr.select('.nav-tab',true);
24216         var qty = existing.getCount();;
24217         
24218         
24219         var tab = ctr.createChild({
24220             tag : 'li',
24221             cls : 'nav-tab' + (qty ? '' : ' active'),
24222             cn : [
24223                 {
24224                     tag : 'a',
24225                     href:'#',
24226                     html : pane.title
24227                 }
24228             ]
24229         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24230         pane.tab = tab;
24231         
24232         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24233         if (!qty) {
24234             pane.el.addClass('active');
24235         }
24236         
24237                 
24238     },
24239     onTabClick : function(ev,un,ob,pane)
24240     {
24241         //Roo.log('tab - prev default');
24242         ev.preventDefault();
24243         
24244         
24245         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24246         pane.tab.addClass('active');
24247         //Roo.log(pane.title);
24248         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24249         // technically we should have a deactivate event.. but maybe add later.
24250         // and it should not de-activate the selected tab...
24251         this.fireEvent('activatepane', pane);
24252         pane.el.addClass('active');
24253         pane.fireEvent('activate');
24254         
24255         
24256     },
24257     
24258     getActivePane : function()
24259     {
24260         var r = false;
24261         Roo.each(this.panes, function(p) {
24262             if(p.el.hasClass('active')){
24263                 r = p;
24264                 return false;
24265             }
24266             
24267             return;
24268         });
24269         
24270         return r;
24271     }
24272     
24273     
24274 });
24275
24276  
24277 /*
24278  * - LGPL
24279  *
24280  * Tab pane
24281  * 
24282  */
24283 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24284 /**
24285  * @class Roo.bootstrap.TabPane
24286  * @extends Roo.bootstrap.Component
24287  * Bootstrap TabPane class
24288  * @cfg {Boolean} active (false | true) Default false
24289  * @cfg {String} title title of panel
24290
24291  * 
24292  * @constructor
24293  * Create a new TabPane
24294  * @param {Object} config The config object
24295  */
24296
24297 Roo.bootstrap.dash.TabPane = function(config){
24298     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24299     
24300     this.addEvents({
24301         // raw events
24302         /**
24303          * @event activate
24304          * When a pane is activated
24305          * @param {Roo.bootstrap.dash.TabPane} pane
24306          */
24307         "activate" : true
24308          
24309     });
24310 };
24311
24312 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24313     
24314     active : false,
24315     title : '',
24316     
24317     // the tabBox that this is attached to.
24318     tab : false,
24319      
24320     getAutoCreate : function() 
24321     {
24322         var cfg = {
24323             tag: 'div',
24324             cls: 'tab-pane'
24325         };
24326         
24327         if(this.active){
24328             cfg.cls += ' active';
24329         }
24330         
24331         return cfg;
24332     },
24333     initEvents  : function()
24334     {
24335         //Roo.log('trigger add pane handler');
24336         this.parent().fireEvent('addpane', this)
24337     },
24338     
24339      /**
24340      * Updates the tab title 
24341      * @param {String} html to set the title to.
24342      */
24343     setTitle: function(str)
24344     {
24345         if (!this.tab) {
24346             return;
24347         }
24348         this.title = str;
24349         this.tab.select('a', true).first().dom.innerHTML = str;
24350         
24351     }
24352     
24353     
24354     
24355 });
24356
24357  
24358
24359
24360  /*
24361  * - LGPL
24362  *
24363  * menu
24364  * 
24365  */
24366 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24367
24368 /**
24369  * @class Roo.bootstrap.menu.Menu
24370  * @extends Roo.bootstrap.Component
24371  * Bootstrap Menu class - container for Menu
24372  * @cfg {String} html Text of the menu
24373  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24374  * @cfg {String} icon Font awesome icon
24375  * @cfg {String} pos Menu align to (top | bottom) default bottom
24376  * 
24377  * 
24378  * @constructor
24379  * Create a new Menu
24380  * @param {Object} config The config object
24381  */
24382
24383
24384 Roo.bootstrap.menu.Menu = function(config){
24385     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24386     
24387     this.addEvents({
24388         /**
24389          * @event beforeshow
24390          * Fires before this menu is displayed
24391          * @param {Roo.bootstrap.menu.Menu} this
24392          */
24393         beforeshow : true,
24394         /**
24395          * @event beforehide
24396          * Fires before this menu is hidden
24397          * @param {Roo.bootstrap.menu.Menu} this
24398          */
24399         beforehide : true,
24400         /**
24401          * @event show
24402          * Fires after this menu is displayed
24403          * @param {Roo.bootstrap.menu.Menu} this
24404          */
24405         show : true,
24406         /**
24407          * @event hide
24408          * Fires after this menu is hidden
24409          * @param {Roo.bootstrap.menu.Menu} this
24410          */
24411         hide : true,
24412         /**
24413          * @event click
24414          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24415          * @param {Roo.bootstrap.menu.Menu} this
24416          * @param {Roo.EventObject} e
24417          */
24418         click : true
24419     });
24420     
24421 };
24422
24423 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24424     
24425     submenu : false,
24426     html : '',
24427     weight : 'default',
24428     icon : false,
24429     pos : 'bottom',
24430     
24431     
24432     getChildContainer : function() {
24433         if(this.isSubMenu){
24434             return this.el;
24435         }
24436         
24437         return this.el.select('ul.dropdown-menu', true).first();  
24438     },
24439     
24440     getAutoCreate : function()
24441     {
24442         var text = [
24443             {
24444                 tag : 'span',
24445                 cls : 'roo-menu-text',
24446                 html : this.html
24447             }
24448         ];
24449         
24450         if(this.icon){
24451             text.unshift({
24452                 tag : 'i',
24453                 cls : 'fa ' + this.icon
24454             })
24455         }
24456         
24457         
24458         var cfg = {
24459             tag : 'div',
24460             cls : 'btn-group',
24461             cn : [
24462                 {
24463                     tag : 'button',
24464                     cls : 'dropdown-button btn btn-' + this.weight,
24465                     cn : text
24466                 },
24467                 {
24468                     tag : 'button',
24469                     cls : 'dropdown-toggle btn btn-' + this.weight,
24470                     cn : [
24471                         {
24472                             tag : 'span',
24473                             cls : 'caret'
24474                         }
24475                     ]
24476                 },
24477                 {
24478                     tag : 'ul',
24479                     cls : 'dropdown-menu'
24480                 }
24481             ]
24482             
24483         };
24484         
24485         if(this.pos == 'top'){
24486             cfg.cls += ' dropup';
24487         }
24488         
24489         if(this.isSubMenu){
24490             cfg = {
24491                 tag : 'ul',
24492                 cls : 'dropdown-menu'
24493             }
24494         }
24495         
24496         return cfg;
24497     },
24498     
24499     onRender : function(ct, position)
24500     {
24501         this.isSubMenu = ct.hasClass('dropdown-submenu');
24502         
24503         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24504     },
24505     
24506     initEvents : function() 
24507     {
24508         if(this.isSubMenu){
24509             return;
24510         }
24511         
24512         this.hidden = true;
24513         
24514         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24515         this.triggerEl.on('click', this.onTriggerPress, this);
24516         
24517         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24518         this.buttonEl.on('click', this.onClick, this);
24519         
24520     },
24521     
24522     list : function()
24523     {
24524         if(this.isSubMenu){
24525             return this.el;
24526         }
24527         
24528         return this.el.select('ul.dropdown-menu', true).first();
24529     },
24530     
24531     onClick : function(e)
24532     {
24533         this.fireEvent("click", this, e);
24534     },
24535     
24536     onTriggerPress  : function(e)
24537     {   
24538         if (this.isVisible()) {
24539             this.hide();
24540         } else {
24541             this.show();
24542         }
24543     },
24544     
24545     isVisible : function(){
24546         return !this.hidden;
24547     },
24548     
24549     show : function()
24550     {
24551         this.fireEvent("beforeshow", this);
24552         
24553         this.hidden = false;
24554         this.el.addClass('open');
24555         
24556         Roo.get(document).on("mouseup", this.onMouseUp, this);
24557         
24558         this.fireEvent("show", this);
24559         
24560         
24561     },
24562     
24563     hide : function()
24564     {
24565         this.fireEvent("beforehide", this);
24566         
24567         this.hidden = true;
24568         this.el.removeClass('open');
24569         
24570         Roo.get(document).un("mouseup", this.onMouseUp);
24571         
24572         this.fireEvent("hide", this);
24573     },
24574     
24575     onMouseUp : function()
24576     {
24577         this.hide();
24578     }
24579     
24580 });
24581
24582  
24583  /*
24584  * - LGPL
24585  *
24586  * menu item
24587  * 
24588  */
24589 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24590
24591 /**
24592  * @class Roo.bootstrap.menu.Item
24593  * @extends Roo.bootstrap.Component
24594  * Bootstrap MenuItem class
24595  * @cfg {Boolean} submenu (true | false) default false
24596  * @cfg {String} html text of the item
24597  * @cfg {String} href the link
24598  * @cfg {Boolean} disable (true | false) default false
24599  * @cfg {Boolean} preventDefault (true | false) default true
24600  * @cfg {String} icon Font awesome icon
24601  * @cfg {String} pos Submenu align to (left | right) default right 
24602  * 
24603  * 
24604  * @constructor
24605  * Create a new Item
24606  * @param {Object} config The config object
24607  */
24608
24609
24610 Roo.bootstrap.menu.Item = function(config){
24611     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24612     this.addEvents({
24613         /**
24614          * @event mouseover
24615          * Fires when the mouse is hovering over this menu
24616          * @param {Roo.bootstrap.menu.Item} this
24617          * @param {Roo.EventObject} e
24618          */
24619         mouseover : true,
24620         /**
24621          * @event mouseout
24622          * Fires when the mouse exits this menu
24623          * @param {Roo.bootstrap.menu.Item} this
24624          * @param {Roo.EventObject} e
24625          */
24626         mouseout : true,
24627         // raw events
24628         /**
24629          * @event click
24630          * The raw click event for the entire grid.
24631          * @param {Roo.EventObject} e
24632          */
24633         click : true
24634     });
24635 };
24636
24637 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24638     
24639     submenu : false,
24640     href : '',
24641     html : '',
24642     preventDefault: true,
24643     disable : false,
24644     icon : false,
24645     pos : 'right',
24646     
24647     getAutoCreate : function()
24648     {
24649         var text = [
24650             {
24651                 tag : 'span',
24652                 cls : 'roo-menu-item-text',
24653                 html : this.html
24654             }
24655         ];
24656         
24657         if(this.icon){
24658             text.unshift({
24659                 tag : 'i',
24660                 cls : 'fa ' + this.icon
24661             })
24662         }
24663         
24664         var cfg = {
24665             tag : 'li',
24666             cn : [
24667                 {
24668                     tag : 'a',
24669                     href : this.href || '#',
24670                     cn : text
24671                 }
24672             ]
24673         };
24674         
24675         if(this.disable){
24676             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24677         }
24678         
24679         if(this.submenu){
24680             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24681             
24682             if(this.pos == 'left'){
24683                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24684             }
24685         }
24686         
24687         return cfg;
24688     },
24689     
24690     initEvents : function() 
24691     {
24692         this.el.on('mouseover', this.onMouseOver, this);
24693         this.el.on('mouseout', this.onMouseOut, this);
24694         
24695         this.el.select('a', true).first().on('click', this.onClick, this);
24696         
24697     },
24698     
24699     onClick : function(e)
24700     {
24701         if(this.preventDefault){
24702             e.preventDefault();
24703         }
24704         
24705         this.fireEvent("click", this, e);
24706     },
24707     
24708     onMouseOver : function(e)
24709     {
24710         if(this.submenu && this.pos == 'left'){
24711             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24712         }
24713         
24714         this.fireEvent("mouseover", this, e);
24715     },
24716     
24717     onMouseOut : function(e)
24718     {
24719         this.fireEvent("mouseout", this, e);
24720     }
24721 });
24722
24723  
24724
24725  /*
24726  * - LGPL
24727  *
24728  * menu separator
24729  * 
24730  */
24731 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24732
24733 /**
24734  * @class Roo.bootstrap.menu.Separator
24735  * @extends Roo.bootstrap.Component
24736  * Bootstrap Separator class
24737  * 
24738  * @constructor
24739  * Create a new Separator
24740  * @param {Object} config The config object
24741  */
24742
24743
24744 Roo.bootstrap.menu.Separator = function(config){
24745     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24746 };
24747
24748 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24749     
24750     getAutoCreate : function(){
24751         var cfg = {
24752             tag : 'li',
24753             cls: 'divider'
24754         };
24755         
24756         return cfg;
24757     }
24758    
24759 });
24760
24761  
24762
24763  /*
24764  * - LGPL
24765  *
24766  * Tooltip
24767  * 
24768  */
24769
24770 /**
24771  * @class Roo.bootstrap.Tooltip
24772  * Bootstrap Tooltip class
24773  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24774  * to determine which dom element triggers the tooltip.
24775  * 
24776  * It needs to add support for additional attributes like tooltip-position
24777  * 
24778  * @constructor
24779  * Create a new Toolti
24780  * @param {Object} config The config object
24781  */
24782
24783 Roo.bootstrap.Tooltip = function(config){
24784     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24785     
24786     this.alignment = Roo.bootstrap.Tooltip.alignment;
24787     
24788     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
24789         this.alignment = config.alignment;
24790     }
24791     
24792 };
24793
24794 Roo.apply(Roo.bootstrap.Tooltip, {
24795     /**
24796      * @function init initialize tooltip monitoring.
24797      * @static
24798      */
24799     currentEl : false,
24800     currentTip : false,
24801     currentRegion : false,
24802     
24803     //  init : delay?
24804     
24805     init : function()
24806     {
24807         Roo.get(document).on('mouseover', this.enter ,this);
24808         Roo.get(document).on('mouseout', this.leave, this);
24809          
24810         
24811         this.currentTip = new Roo.bootstrap.Tooltip();
24812     },
24813     
24814     enter : function(ev)
24815     {
24816         var dom = ev.getTarget();
24817         
24818         //Roo.log(['enter',dom]);
24819         var el = Roo.fly(dom);
24820         if (this.currentEl) {
24821             //Roo.log(dom);
24822             //Roo.log(this.currentEl);
24823             //Roo.log(this.currentEl.contains(dom));
24824             if (this.currentEl == el) {
24825                 return;
24826             }
24827             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24828                 return;
24829             }
24830
24831         }
24832         
24833         if (this.currentTip.el) {
24834             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24835         }    
24836         //Roo.log(ev);
24837         
24838         if(!el || el.dom == document){
24839             return;
24840         }
24841         
24842         var bindEl = el;
24843         
24844         // you can not look for children, as if el is the body.. then everythign is the child..
24845         if (!el.attr('tooltip')) { //
24846             if (!el.select("[tooltip]").elements.length) {
24847                 return;
24848             }
24849             // is the mouse over this child...?
24850             bindEl = el.select("[tooltip]").first();
24851             var xy = ev.getXY();
24852             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24853                 //Roo.log("not in region.");
24854                 return;
24855             }
24856             //Roo.log("child element over..");
24857             
24858         }
24859         this.currentEl = bindEl;
24860         this.currentTip.bind(bindEl);
24861         this.currentRegion = Roo.lib.Region.getRegion(dom);
24862         this.currentTip.enter();
24863         
24864     },
24865     leave : function(ev)
24866     {
24867         var dom = ev.getTarget();
24868         //Roo.log(['leave',dom]);
24869         if (!this.currentEl) {
24870             return;
24871         }
24872         
24873         
24874         if (dom != this.currentEl.dom) {
24875             return;
24876         }
24877         var xy = ev.getXY();
24878         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24879             return;
24880         }
24881         // only activate leave if mouse cursor is outside... bounding box..
24882         
24883         
24884         
24885         
24886         if (this.currentTip) {
24887             this.currentTip.leave();
24888         }
24889         //Roo.log('clear currentEl');
24890         this.currentEl = false;
24891         
24892         
24893     },
24894     alignment : {
24895         'left' : ['r-l', [-2,0], 'right'],
24896         'right' : ['l-r', [2,0], 'left'],
24897         'bottom' : ['t-b', [0,2], 'top'],
24898         'top' : [ 'b-t', [0,-2], 'bottom']
24899     }
24900     
24901 });
24902
24903
24904 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24905     
24906     
24907     bindEl : false,
24908     
24909     delay : null, // can be { show : 300 , hide: 500}
24910     
24911     timeout : null,
24912     
24913     hoverState : null, //???
24914     
24915     placement : 'bottom', 
24916     
24917     alignment : false,
24918     
24919     getAutoCreate : function(){
24920     
24921         var cfg = {
24922            cls : 'tooltip',
24923            role : 'tooltip',
24924            cn : [
24925                 {
24926                     cls : 'tooltip-arrow'
24927                 },
24928                 {
24929                     cls : 'tooltip-inner'
24930                 }
24931            ]
24932         };
24933         
24934         return cfg;
24935     },
24936     bind : function(el)
24937     {
24938         this.bindEl = el;
24939     },
24940       
24941     
24942     enter : function () {
24943        
24944         if (this.timeout != null) {
24945             clearTimeout(this.timeout);
24946         }
24947         
24948         this.hoverState = 'in';
24949          //Roo.log("enter - show");
24950         if (!this.delay || !this.delay.show) {
24951             this.show();
24952             return;
24953         }
24954         var _t = this;
24955         this.timeout = setTimeout(function () {
24956             if (_t.hoverState == 'in') {
24957                 _t.show();
24958             }
24959         }, this.delay.show);
24960     },
24961     leave : function()
24962     {
24963         clearTimeout(this.timeout);
24964     
24965         this.hoverState = 'out';
24966          if (!this.delay || !this.delay.hide) {
24967             this.hide();
24968             return;
24969         }
24970        
24971         var _t = this;
24972         this.timeout = setTimeout(function () {
24973             //Roo.log("leave - timeout");
24974             
24975             if (_t.hoverState == 'out') {
24976                 _t.hide();
24977                 Roo.bootstrap.Tooltip.currentEl = false;
24978             }
24979         }, delay);
24980     },
24981     
24982     show : function (msg)
24983     {
24984         if (!this.el) {
24985             this.render(document.body);
24986         }
24987         // set content.
24988         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24989         
24990         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24991         
24992         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24993         
24994         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24995         
24996         var placement = typeof this.placement == 'function' ?
24997             this.placement.call(this, this.el, on_el) :
24998             this.placement;
24999             
25000         var autoToken = /\s?auto?\s?/i;
25001         var autoPlace = autoToken.test(placement);
25002         if (autoPlace) {
25003             placement = placement.replace(autoToken, '') || 'top';
25004         }
25005         
25006         //this.el.detach()
25007         //this.el.setXY([0,0]);
25008         this.el.show();
25009         //this.el.dom.style.display='block';
25010         
25011         //this.el.appendTo(on_el);
25012         
25013         var p = this.getPosition();
25014         var box = this.el.getBox();
25015         
25016         if (autoPlace) {
25017             // fixme..
25018         }
25019         
25020         var align = this.alignment[placement];
25021         
25022         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25023         
25024         if(placement == 'top' || placement == 'bottom'){
25025             if(xy[0] < 0){
25026                 placement = 'right';
25027             }
25028             
25029             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25030                 placement = 'left';
25031             }
25032             
25033             var scroll = Roo.select('body', true).first().getScroll();
25034             
25035             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25036                 placement = 'top';
25037             }
25038             
25039         }
25040         
25041         this.el.alignTo(this.bindEl, align[0],align[1]);
25042         //var arrow = this.el.select('.arrow',true).first();
25043         //arrow.set(align[2], 
25044         
25045         this.el.addClass(placement);
25046         
25047         this.el.addClass('in fade');
25048         
25049         this.hoverState = null;
25050         
25051         if (this.el.hasClass('fade')) {
25052             // fade it?
25053         }
25054         
25055     },
25056     hide : function()
25057     {
25058          
25059         if (!this.el) {
25060             return;
25061         }
25062         //this.el.setXY([0,0]);
25063         this.el.removeClass('in');
25064         //this.el.hide();
25065         
25066     }
25067     
25068 });
25069  
25070
25071  /*
25072  * - LGPL
25073  *
25074  * Location Picker
25075  * 
25076  */
25077
25078 /**
25079  * @class Roo.bootstrap.LocationPicker
25080  * @extends Roo.bootstrap.Component
25081  * Bootstrap LocationPicker class
25082  * @cfg {Number} latitude Position when init default 0
25083  * @cfg {Number} longitude Position when init default 0
25084  * @cfg {Number} zoom default 15
25085  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25086  * @cfg {Boolean} mapTypeControl default false
25087  * @cfg {Boolean} disableDoubleClickZoom default false
25088  * @cfg {Boolean} scrollwheel default true
25089  * @cfg {Boolean} streetViewControl default false
25090  * @cfg {Number} radius default 0
25091  * @cfg {String} locationName
25092  * @cfg {Boolean} draggable default true
25093  * @cfg {Boolean} enableAutocomplete default false
25094  * @cfg {Boolean} enableReverseGeocode default true
25095  * @cfg {String} markerTitle
25096  * 
25097  * @constructor
25098  * Create a new LocationPicker
25099  * @param {Object} config The config object
25100  */
25101
25102
25103 Roo.bootstrap.LocationPicker = function(config){
25104     
25105     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25106     
25107     this.addEvents({
25108         /**
25109          * @event initial
25110          * Fires when the picker initialized.
25111          * @param {Roo.bootstrap.LocationPicker} this
25112          * @param {Google Location} location
25113          */
25114         initial : true,
25115         /**
25116          * @event positionchanged
25117          * Fires when the picker position changed.
25118          * @param {Roo.bootstrap.LocationPicker} this
25119          * @param {Google Location} location
25120          */
25121         positionchanged : true,
25122         /**
25123          * @event resize
25124          * Fires when the map resize.
25125          * @param {Roo.bootstrap.LocationPicker} this
25126          */
25127         resize : true,
25128         /**
25129          * @event show
25130          * Fires when the map show.
25131          * @param {Roo.bootstrap.LocationPicker} this
25132          */
25133         show : true,
25134         /**
25135          * @event hide
25136          * Fires when the map hide.
25137          * @param {Roo.bootstrap.LocationPicker} this
25138          */
25139         hide : true,
25140         /**
25141          * @event mapClick
25142          * Fires when click the map.
25143          * @param {Roo.bootstrap.LocationPicker} this
25144          * @param {Map event} e
25145          */
25146         mapClick : true,
25147         /**
25148          * @event mapRightClick
25149          * Fires when right click the map.
25150          * @param {Roo.bootstrap.LocationPicker} this
25151          * @param {Map event} e
25152          */
25153         mapRightClick : true,
25154         /**
25155          * @event markerClick
25156          * Fires when click the marker.
25157          * @param {Roo.bootstrap.LocationPicker} this
25158          * @param {Map event} e
25159          */
25160         markerClick : true,
25161         /**
25162          * @event markerRightClick
25163          * Fires when right click the marker.
25164          * @param {Roo.bootstrap.LocationPicker} this
25165          * @param {Map event} e
25166          */
25167         markerRightClick : true,
25168         /**
25169          * @event OverlayViewDraw
25170          * Fires when OverlayView Draw
25171          * @param {Roo.bootstrap.LocationPicker} this
25172          */
25173         OverlayViewDraw : true,
25174         /**
25175          * @event OverlayViewOnAdd
25176          * Fires when OverlayView Draw
25177          * @param {Roo.bootstrap.LocationPicker} this
25178          */
25179         OverlayViewOnAdd : true,
25180         /**
25181          * @event OverlayViewOnRemove
25182          * Fires when OverlayView Draw
25183          * @param {Roo.bootstrap.LocationPicker} this
25184          */
25185         OverlayViewOnRemove : true,
25186         /**
25187          * @event OverlayViewShow
25188          * Fires when OverlayView Draw
25189          * @param {Roo.bootstrap.LocationPicker} this
25190          * @param {Pixel} cpx
25191          */
25192         OverlayViewShow : true,
25193         /**
25194          * @event OverlayViewHide
25195          * Fires when OverlayView Draw
25196          * @param {Roo.bootstrap.LocationPicker} this
25197          */
25198         OverlayViewHide : true,
25199         /**
25200          * @event loadexception
25201          * Fires when load google lib failed.
25202          * @param {Roo.bootstrap.LocationPicker} this
25203          */
25204         loadexception : true
25205     });
25206         
25207 };
25208
25209 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25210     
25211     gMapContext: false,
25212     
25213     latitude: 0,
25214     longitude: 0,
25215     zoom: 15,
25216     mapTypeId: false,
25217     mapTypeControl: false,
25218     disableDoubleClickZoom: false,
25219     scrollwheel: true,
25220     streetViewControl: false,
25221     radius: 0,
25222     locationName: '',
25223     draggable: true,
25224     enableAutocomplete: false,
25225     enableReverseGeocode: true,
25226     markerTitle: '',
25227     
25228     getAutoCreate: function()
25229     {
25230
25231         var cfg = {
25232             tag: 'div',
25233             cls: 'roo-location-picker'
25234         };
25235         
25236         return cfg
25237     },
25238     
25239     initEvents: function(ct, position)
25240     {       
25241         if(!this.el.getWidth() || this.isApplied()){
25242             return;
25243         }
25244         
25245         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25246         
25247         this.initial();
25248     },
25249     
25250     initial: function()
25251     {
25252         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25253             this.fireEvent('loadexception', this);
25254             return;
25255         }
25256         
25257         if(!this.mapTypeId){
25258             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25259         }
25260         
25261         this.gMapContext = this.GMapContext();
25262         
25263         this.initOverlayView();
25264         
25265         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25266         
25267         var _this = this;
25268                 
25269         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25270             _this.setPosition(_this.gMapContext.marker.position);
25271         });
25272         
25273         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25274             _this.fireEvent('mapClick', this, event);
25275             
25276         });
25277
25278         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25279             _this.fireEvent('mapRightClick', this, event);
25280             
25281         });
25282         
25283         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25284             _this.fireEvent('markerClick', this, event);
25285             
25286         });
25287
25288         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25289             _this.fireEvent('markerRightClick', this, event);
25290             
25291         });
25292         
25293         this.setPosition(this.gMapContext.location);
25294         
25295         this.fireEvent('initial', this, this.gMapContext.location);
25296     },
25297     
25298     initOverlayView: function()
25299     {
25300         var _this = this;
25301         
25302         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25303             
25304             draw: function()
25305             {
25306                 _this.fireEvent('OverlayViewDraw', _this);
25307             },
25308             
25309             onAdd: function()
25310             {
25311                 _this.fireEvent('OverlayViewOnAdd', _this);
25312             },
25313             
25314             onRemove: function()
25315             {
25316                 _this.fireEvent('OverlayViewOnRemove', _this);
25317             },
25318             
25319             show: function(cpx)
25320             {
25321                 _this.fireEvent('OverlayViewShow', _this, cpx);
25322             },
25323             
25324             hide: function()
25325             {
25326                 _this.fireEvent('OverlayViewHide', _this);
25327             }
25328             
25329         });
25330     },
25331     
25332     fromLatLngToContainerPixel: function(event)
25333     {
25334         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25335     },
25336     
25337     isApplied: function() 
25338     {
25339         return this.getGmapContext() == false ? false : true;
25340     },
25341     
25342     getGmapContext: function() 
25343     {
25344         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25345     },
25346     
25347     GMapContext: function() 
25348     {
25349         var position = new google.maps.LatLng(this.latitude, this.longitude);
25350         
25351         var _map = new google.maps.Map(this.el.dom, {
25352             center: position,
25353             zoom: this.zoom,
25354             mapTypeId: this.mapTypeId,
25355             mapTypeControl: this.mapTypeControl,
25356             disableDoubleClickZoom: this.disableDoubleClickZoom,
25357             scrollwheel: this.scrollwheel,
25358             streetViewControl: this.streetViewControl,
25359             locationName: this.locationName,
25360             draggable: this.draggable,
25361             enableAutocomplete: this.enableAutocomplete,
25362             enableReverseGeocode: this.enableReverseGeocode
25363         });
25364         
25365         var _marker = new google.maps.Marker({
25366             position: position,
25367             map: _map,
25368             title: this.markerTitle,
25369             draggable: this.draggable
25370         });
25371         
25372         return {
25373             map: _map,
25374             marker: _marker,
25375             circle: null,
25376             location: position,
25377             radius: this.radius,
25378             locationName: this.locationName,
25379             addressComponents: {
25380                 formatted_address: null,
25381                 addressLine1: null,
25382                 addressLine2: null,
25383                 streetName: null,
25384                 streetNumber: null,
25385                 city: null,
25386                 district: null,
25387                 state: null,
25388                 stateOrProvince: null
25389             },
25390             settings: this,
25391             domContainer: this.el.dom,
25392             geodecoder: new google.maps.Geocoder()
25393         };
25394     },
25395     
25396     drawCircle: function(center, radius, options) 
25397     {
25398         if (this.gMapContext.circle != null) {
25399             this.gMapContext.circle.setMap(null);
25400         }
25401         if (radius > 0) {
25402             radius *= 1;
25403             options = Roo.apply({}, options, {
25404                 strokeColor: "#0000FF",
25405                 strokeOpacity: .35,
25406                 strokeWeight: 2,
25407                 fillColor: "#0000FF",
25408                 fillOpacity: .2
25409             });
25410             
25411             options.map = this.gMapContext.map;
25412             options.radius = radius;
25413             options.center = center;
25414             this.gMapContext.circle = new google.maps.Circle(options);
25415             return this.gMapContext.circle;
25416         }
25417         
25418         return null;
25419     },
25420     
25421     setPosition: function(location) 
25422     {
25423         this.gMapContext.location = location;
25424         this.gMapContext.marker.setPosition(location);
25425         this.gMapContext.map.panTo(location);
25426         this.drawCircle(location, this.gMapContext.radius, {});
25427         
25428         var _this = this;
25429         
25430         if (this.gMapContext.settings.enableReverseGeocode) {
25431             this.gMapContext.geodecoder.geocode({
25432                 latLng: this.gMapContext.location
25433             }, function(results, status) {
25434                 
25435                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25436                     _this.gMapContext.locationName = results[0].formatted_address;
25437                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25438                     
25439                     _this.fireEvent('positionchanged', this, location);
25440                 }
25441             });
25442             
25443             return;
25444         }
25445         
25446         this.fireEvent('positionchanged', this, location);
25447     },
25448     
25449     resize: function()
25450     {
25451         google.maps.event.trigger(this.gMapContext.map, "resize");
25452         
25453         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25454         
25455         this.fireEvent('resize', this);
25456     },
25457     
25458     setPositionByLatLng: function(latitude, longitude)
25459     {
25460         this.setPosition(new google.maps.LatLng(latitude, longitude));
25461     },
25462     
25463     getCurrentPosition: function() 
25464     {
25465         return {
25466             latitude: this.gMapContext.location.lat(),
25467             longitude: this.gMapContext.location.lng()
25468         };
25469     },
25470     
25471     getAddressName: function() 
25472     {
25473         return this.gMapContext.locationName;
25474     },
25475     
25476     getAddressComponents: function() 
25477     {
25478         return this.gMapContext.addressComponents;
25479     },
25480     
25481     address_component_from_google_geocode: function(address_components) 
25482     {
25483         var result = {};
25484         
25485         for (var i = 0; i < address_components.length; i++) {
25486             var component = address_components[i];
25487             if (component.types.indexOf("postal_code") >= 0) {
25488                 result.postalCode = component.short_name;
25489             } else if (component.types.indexOf("street_number") >= 0) {
25490                 result.streetNumber = component.short_name;
25491             } else if (component.types.indexOf("route") >= 0) {
25492                 result.streetName = component.short_name;
25493             } else if (component.types.indexOf("neighborhood") >= 0) {
25494                 result.city = component.short_name;
25495             } else if (component.types.indexOf("locality") >= 0) {
25496                 result.city = component.short_name;
25497             } else if (component.types.indexOf("sublocality") >= 0) {
25498                 result.district = component.short_name;
25499             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25500                 result.stateOrProvince = component.short_name;
25501             } else if (component.types.indexOf("country") >= 0) {
25502                 result.country = component.short_name;
25503             }
25504         }
25505         
25506         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25507         result.addressLine2 = "";
25508         return result;
25509     },
25510     
25511     setZoomLevel: function(zoom)
25512     {
25513         this.gMapContext.map.setZoom(zoom);
25514     },
25515     
25516     show: function()
25517     {
25518         if(!this.el){
25519             return;
25520         }
25521         
25522         this.el.show();
25523         
25524         this.resize();
25525         
25526         this.fireEvent('show', this);
25527     },
25528     
25529     hide: function()
25530     {
25531         if(!this.el){
25532             return;
25533         }
25534         
25535         this.el.hide();
25536         
25537         this.fireEvent('hide', this);
25538     }
25539     
25540 });
25541
25542 Roo.apply(Roo.bootstrap.LocationPicker, {
25543     
25544     OverlayView : function(map, options)
25545     {
25546         options = options || {};
25547         
25548         this.setMap(map);
25549     }
25550     
25551     
25552 });/*
25553  * - LGPL
25554  *
25555  * Alert
25556  * 
25557  */
25558
25559 /**
25560  * @class Roo.bootstrap.Alert
25561  * @extends Roo.bootstrap.Component
25562  * Bootstrap Alert class
25563  * @cfg {String} title The title of alert
25564  * @cfg {String} html The content of alert
25565  * @cfg {String} weight (  success | info | warning | danger )
25566  * @cfg {String} faicon font-awesomeicon
25567  * 
25568  * @constructor
25569  * Create a new alert
25570  * @param {Object} config The config object
25571  */
25572
25573
25574 Roo.bootstrap.Alert = function(config){
25575     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25576     
25577 };
25578
25579 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25580     
25581     title: '',
25582     html: '',
25583     weight: false,
25584     faicon: false,
25585     
25586     getAutoCreate : function()
25587     {
25588         
25589         var cfg = {
25590             tag : 'div',
25591             cls : 'alert',
25592             cn : [
25593                 {
25594                     tag : 'i',
25595                     cls : 'roo-alert-icon'
25596                     
25597                 },
25598                 {
25599                     tag : 'b',
25600                     cls : 'roo-alert-title',
25601                     html : this.title
25602                 },
25603                 {
25604                     tag : 'span',
25605                     cls : 'roo-alert-text',
25606                     html : this.html
25607                 }
25608             ]
25609         };
25610         
25611         if(this.faicon){
25612             cfg.cn[0].cls += ' fa ' + this.faicon;
25613         }
25614         
25615         if(this.weight){
25616             cfg.cls += ' alert-' + this.weight;
25617         }
25618         
25619         return cfg;
25620     },
25621     
25622     initEvents: function() 
25623     {
25624         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25625     },
25626     
25627     setTitle : function(str)
25628     {
25629         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25630     },
25631     
25632     setText : function(str)
25633     {
25634         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25635     },
25636     
25637     setWeight : function(weight)
25638     {
25639         if(this.weight){
25640             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25641         }
25642         
25643         this.weight = weight;
25644         
25645         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25646     },
25647     
25648     setIcon : function(icon)
25649     {
25650         if(this.faicon){
25651             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25652         }
25653         
25654         this.faicon = icon;
25655         
25656         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25657     },
25658     
25659     hide: function() 
25660     {
25661         this.el.hide();   
25662     },
25663     
25664     show: function() 
25665     {  
25666         this.el.show();   
25667     }
25668     
25669 });
25670
25671  
25672 /*
25673 * Licence: LGPL
25674 */
25675
25676 /**
25677  * @class Roo.bootstrap.UploadCropbox
25678  * @extends Roo.bootstrap.Component
25679  * Bootstrap UploadCropbox class
25680  * @cfg {String} emptyText show when image has been loaded
25681  * @cfg {String} rotateNotify show when image too small to rotate
25682  * @cfg {Number} errorTimeout default 3000
25683  * @cfg {Number} minWidth default 300
25684  * @cfg {Number} minHeight default 300
25685  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25686  * @cfg {Boolean} isDocument (true|false) default false
25687  * @cfg {String} url action url
25688  * @cfg {String} paramName default 'imageUpload'
25689  * @cfg {String} method default POST
25690  * @cfg {Boolean} loadMask (true|false) default true
25691  * @cfg {Boolean} loadingText default 'Loading...'
25692  * 
25693  * @constructor
25694  * Create a new UploadCropbox
25695  * @param {Object} config The config object
25696  */
25697
25698 Roo.bootstrap.UploadCropbox = function(config){
25699     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25700     
25701     this.addEvents({
25702         /**
25703          * @event beforeselectfile
25704          * Fire before select file
25705          * @param {Roo.bootstrap.UploadCropbox} this
25706          */
25707         "beforeselectfile" : true,
25708         /**
25709          * @event initial
25710          * Fire after initEvent
25711          * @param {Roo.bootstrap.UploadCropbox} this
25712          */
25713         "initial" : true,
25714         /**
25715          * @event crop
25716          * Fire after initEvent
25717          * @param {Roo.bootstrap.UploadCropbox} this
25718          * @param {String} data
25719          */
25720         "crop" : true,
25721         /**
25722          * @event prepare
25723          * Fire when preparing the file data
25724          * @param {Roo.bootstrap.UploadCropbox} this
25725          * @param {Object} file
25726          */
25727         "prepare" : true,
25728         /**
25729          * @event exception
25730          * Fire when get exception
25731          * @param {Roo.bootstrap.UploadCropbox} this
25732          * @param {XMLHttpRequest} xhr
25733          */
25734         "exception" : true,
25735         /**
25736          * @event beforeloadcanvas
25737          * Fire before load the canvas
25738          * @param {Roo.bootstrap.UploadCropbox} this
25739          * @param {String} src
25740          */
25741         "beforeloadcanvas" : true,
25742         /**
25743          * @event trash
25744          * Fire when trash image
25745          * @param {Roo.bootstrap.UploadCropbox} this
25746          */
25747         "trash" : true,
25748         /**
25749          * @event download
25750          * Fire when download the image
25751          * @param {Roo.bootstrap.UploadCropbox} this
25752          */
25753         "download" : true,
25754         /**
25755          * @event footerbuttonclick
25756          * Fire when footerbuttonclick
25757          * @param {Roo.bootstrap.UploadCropbox} this
25758          * @param {String} type
25759          */
25760         "footerbuttonclick" : true,
25761         /**
25762          * @event resize
25763          * Fire when resize
25764          * @param {Roo.bootstrap.UploadCropbox} this
25765          */
25766         "resize" : true,
25767         /**
25768          * @event rotate
25769          * Fire when rotate the image
25770          * @param {Roo.bootstrap.UploadCropbox} this
25771          * @param {String} pos
25772          */
25773         "rotate" : true,
25774         /**
25775          * @event inspect
25776          * Fire when inspect the file
25777          * @param {Roo.bootstrap.UploadCropbox} this
25778          * @param {Object} file
25779          */
25780         "inspect" : true,
25781         /**
25782          * @event upload
25783          * Fire when xhr upload the file
25784          * @param {Roo.bootstrap.UploadCropbox} this
25785          * @param {Object} data
25786          */
25787         "upload" : true,
25788         /**
25789          * @event arrange
25790          * Fire when arrange the file data
25791          * @param {Roo.bootstrap.UploadCropbox} this
25792          * @param {Object} formData
25793          */
25794         "arrange" : true
25795     });
25796     
25797     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25798 };
25799
25800 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25801     
25802     emptyText : 'Click to upload image',
25803     rotateNotify : 'Image is too small to rotate',
25804     errorTimeout : 3000,
25805     scale : 0,
25806     baseScale : 1,
25807     rotate : 0,
25808     dragable : false,
25809     pinching : false,
25810     mouseX : 0,
25811     mouseY : 0,
25812     cropData : false,
25813     minWidth : 300,
25814     minHeight : 300,
25815     file : false,
25816     exif : {},
25817     baseRotate : 1,
25818     cropType : 'image/jpeg',
25819     buttons : false,
25820     canvasLoaded : false,
25821     isDocument : false,
25822     method : 'POST',
25823     paramName : 'imageUpload',
25824     loadMask : true,
25825     loadingText : 'Loading...',
25826     maskEl : false,
25827     
25828     getAutoCreate : function()
25829     {
25830         var cfg = {
25831             tag : 'div',
25832             cls : 'roo-upload-cropbox',
25833             cn : [
25834                 {
25835                     tag : 'input',
25836                     cls : 'roo-upload-cropbox-selector',
25837                     type : 'file'
25838                 },
25839                 {
25840                     tag : 'div',
25841                     cls : 'roo-upload-cropbox-body',
25842                     style : 'cursor:pointer',
25843                     cn : [
25844                         {
25845                             tag : 'div',
25846                             cls : 'roo-upload-cropbox-preview'
25847                         },
25848                         {
25849                             tag : 'div',
25850                             cls : 'roo-upload-cropbox-thumb'
25851                         },
25852                         {
25853                             tag : 'div',
25854                             cls : 'roo-upload-cropbox-empty-notify',
25855                             html : this.emptyText
25856                         },
25857                         {
25858                             tag : 'div',
25859                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25860                             html : this.rotateNotify
25861                         }
25862                     ]
25863                 },
25864                 {
25865                     tag : 'div',
25866                     cls : 'roo-upload-cropbox-footer',
25867                     cn : {
25868                         tag : 'div',
25869                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25870                         cn : []
25871                     }
25872                 }
25873             ]
25874         };
25875         
25876         return cfg;
25877     },
25878     
25879     onRender : function(ct, position)
25880     {
25881         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25882         
25883         if (this.buttons.length) {
25884             
25885             Roo.each(this.buttons, function(bb) {
25886                 
25887                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25888                 
25889                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25890                 
25891             }, this);
25892         }
25893         
25894         if(this.loadMask){
25895             this.maskEl = this.el;
25896         }
25897     },
25898     
25899     initEvents : function()
25900     {
25901         this.urlAPI = (window.createObjectURL && window) || 
25902                                 (window.URL && URL.revokeObjectURL && URL) || 
25903                                 (window.webkitURL && webkitURL);
25904                         
25905         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25906         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25907         
25908         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25909         this.selectorEl.hide();
25910         
25911         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25912         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25913         
25914         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25915         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25916         this.thumbEl.hide();
25917         
25918         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25919         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25920         
25921         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25922         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25923         this.errorEl.hide();
25924         
25925         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25926         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25927         this.footerEl.hide();
25928         
25929         this.setThumbBoxSize();
25930         
25931         this.bind();
25932         
25933         this.resize();
25934         
25935         this.fireEvent('initial', this);
25936     },
25937
25938     bind : function()
25939     {
25940         var _this = this;
25941         
25942         window.addEventListener("resize", function() { _this.resize(); } );
25943         
25944         this.bodyEl.on('click', this.beforeSelectFile, this);
25945         
25946         if(Roo.isTouch){
25947             this.bodyEl.on('touchstart', this.onTouchStart, this);
25948             this.bodyEl.on('touchmove', this.onTouchMove, this);
25949             this.bodyEl.on('touchend', this.onTouchEnd, this);
25950         }
25951         
25952         if(!Roo.isTouch){
25953             this.bodyEl.on('mousedown', this.onMouseDown, this);
25954             this.bodyEl.on('mousemove', this.onMouseMove, this);
25955             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25956             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25957             Roo.get(document).on('mouseup', this.onMouseUp, this);
25958         }
25959         
25960         this.selectorEl.on('change', this.onFileSelected, this);
25961     },
25962     
25963     reset : function()
25964     {    
25965         this.scale = 0;
25966         this.baseScale = 1;
25967         this.rotate = 0;
25968         this.baseRotate = 1;
25969         this.dragable = false;
25970         this.pinching = false;
25971         this.mouseX = 0;
25972         this.mouseY = 0;
25973         this.cropData = false;
25974         this.notifyEl.dom.innerHTML = this.emptyText;
25975         
25976         this.selectorEl.dom.value = '';
25977         
25978     },
25979     
25980     resize : function()
25981     {
25982         if(this.fireEvent('resize', this) != false){
25983             this.setThumbBoxPosition();
25984             this.setCanvasPosition();
25985         }
25986     },
25987     
25988     onFooterButtonClick : function(e, el, o, type)
25989     {
25990         switch (type) {
25991             case 'rotate-left' :
25992                 this.onRotateLeft(e);
25993                 break;
25994             case 'rotate-right' :
25995                 this.onRotateRight(e);
25996                 break;
25997             case 'picture' :
25998                 this.beforeSelectFile(e);
25999                 break;
26000             case 'trash' :
26001                 this.trash(e);
26002                 break;
26003             case 'crop' :
26004                 this.crop(e);
26005                 break;
26006             case 'download' :
26007                 this.download(e);
26008                 break;
26009             default :
26010                 break;
26011         }
26012         
26013         this.fireEvent('footerbuttonclick', this, type);
26014     },
26015     
26016     beforeSelectFile : function(e)
26017     {
26018         e.preventDefault();
26019         
26020         if(this.fireEvent('beforeselectfile', this) != false){
26021             this.selectorEl.dom.click();
26022         }
26023     },
26024     
26025     onFileSelected : function(e)
26026     {
26027         e.preventDefault();
26028         
26029         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26030             return;
26031         }
26032         
26033         var file = this.selectorEl.dom.files[0];
26034         
26035         if(this.fireEvent('inspect', this, file) != false){
26036             this.prepare(file);
26037         }
26038         
26039     },
26040     
26041     trash : function(e)
26042     {
26043         this.fireEvent('trash', this);
26044     },
26045     
26046     download : function(e)
26047     {
26048         this.fireEvent('download', this);
26049     },
26050     
26051     loadCanvas : function(src)
26052     {   
26053         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26054             
26055             this.reset();
26056             
26057             this.imageEl = document.createElement('img');
26058             
26059             var _this = this;
26060             
26061             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26062             
26063             this.imageEl.src = src;
26064         }
26065     },
26066     
26067     onLoadCanvas : function()
26068     {   
26069         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26070         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26071         
26072         this.bodyEl.un('click', this.beforeSelectFile, this);
26073         
26074         this.notifyEl.hide();
26075         this.thumbEl.show();
26076         this.footerEl.show();
26077         
26078         this.baseRotateLevel();
26079         
26080         if(this.isDocument){
26081             this.setThumbBoxSize();
26082         }
26083         
26084         this.setThumbBoxPosition();
26085         
26086         this.baseScaleLevel();
26087         
26088         this.draw();
26089         
26090         this.resize();
26091         
26092         this.canvasLoaded = true;
26093         
26094         if(this.loadMask){
26095             this.maskEl.unmask();
26096         }
26097         
26098     },
26099     
26100     setCanvasPosition : function()
26101     {   
26102         if(!this.canvasEl){
26103             return;
26104         }
26105         
26106         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26107         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26108         
26109         this.previewEl.setLeft(pw);
26110         this.previewEl.setTop(ph);
26111         
26112     },
26113     
26114     onMouseDown : function(e)
26115     {   
26116         e.stopEvent();
26117         
26118         this.dragable = true;
26119         this.pinching = false;
26120         
26121         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26122             this.dragable = false;
26123             return;
26124         }
26125         
26126         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26127         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26128         
26129     },
26130     
26131     onMouseMove : function(e)
26132     {   
26133         e.stopEvent();
26134         
26135         if(!this.canvasLoaded){
26136             return;
26137         }
26138         
26139         if (!this.dragable){
26140             return;
26141         }
26142         
26143         var minX = Math.ceil(this.thumbEl.getLeft(true));
26144         var minY = Math.ceil(this.thumbEl.getTop(true));
26145         
26146         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26147         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26148         
26149         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26150         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26151         
26152         x = x - this.mouseX;
26153         y = y - this.mouseY;
26154         
26155         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26156         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26157         
26158         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26159         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26160         
26161         this.previewEl.setLeft(bgX);
26162         this.previewEl.setTop(bgY);
26163         
26164         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26165         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26166     },
26167     
26168     onMouseUp : function(e)
26169     {   
26170         e.stopEvent();
26171         
26172         this.dragable = false;
26173     },
26174     
26175     onMouseWheel : function(e)
26176     {   
26177         e.stopEvent();
26178         
26179         this.startScale = this.scale;
26180         
26181         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26182         
26183         if(!this.zoomable()){
26184             this.scale = this.startScale;
26185             return;
26186         }
26187         
26188         this.draw();
26189         
26190         return;
26191     },
26192     
26193     zoomable : function()
26194     {
26195         var minScale = this.thumbEl.getWidth() / this.minWidth;
26196         
26197         if(this.minWidth < this.minHeight){
26198             minScale = this.thumbEl.getHeight() / this.minHeight;
26199         }
26200         
26201         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26202         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26203         
26204         if(
26205                 this.isDocument &&
26206                 (this.rotate == 0 || this.rotate == 180) && 
26207                 (
26208                     width > this.imageEl.OriginWidth || 
26209                     height > this.imageEl.OriginHeight ||
26210                     (width < this.minWidth && height < this.minHeight)
26211                 )
26212         ){
26213             return false;
26214         }
26215         
26216         if(
26217                 this.isDocument &&
26218                 (this.rotate == 90 || this.rotate == 270) && 
26219                 (
26220                     width > this.imageEl.OriginWidth || 
26221                     height > this.imageEl.OriginHeight ||
26222                     (width < this.minHeight && height < this.minWidth)
26223                 )
26224         ){
26225             return false;
26226         }
26227         
26228         if(
26229                 !this.isDocument &&
26230                 (this.rotate == 0 || this.rotate == 180) && 
26231                 (
26232                     width < this.minWidth || 
26233                     width > this.imageEl.OriginWidth || 
26234                     height < this.minHeight || 
26235                     height > this.imageEl.OriginHeight
26236                 )
26237         ){
26238             return false;
26239         }
26240         
26241         if(
26242                 !this.isDocument &&
26243                 (this.rotate == 90 || this.rotate == 270) && 
26244                 (
26245                     width < this.minHeight || 
26246                     width > this.imageEl.OriginWidth || 
26247                     height < this.minWidth || 
26248                     height > this.imageEl.OriginHeight
26249                 )
26250         ){
26251             return false;
26252         }
26253         
26254         return true;
26255         
26256     },
26257     
26258     onRotateLeft : function(e)
26259     {   
26260         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26261             
26262             var minScale = this.thumbEl.getWidth() / this.minWidth;
26263             
26264             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26265             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26266             
26267             this.startScale = this.scale;
26268             
26269             while (this.getScaleLevel() < minScale){
26270             
26271                 this.scale = this.scale + 1;
26272                 
26273                 if(!this.zoomable()){
26274                     break;
26275                 }
26276                 
26277                 if(
26278                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26279                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26280                 ){
26281                     continue;
26282                 }
26283                 
26284                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26285
26286                 this.draw();
26287                 
26288                 return;
26289             }
26290             
26291             this.scale = this.startScale;
26292             
26293             this.onRotateFail();
26294             
26295             return false;
26296         }
26297         
26298         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26299
26300         if(this.isDocument){
26301             this.setThumbBoxSize();
26302             this.setThumbBoxPosition();
26303             this.setCanvasPosition();
26304         }
26305         
26306         this.draw();
26307         
26308         this.fireEvent('rotate', this, 'left');
26309         
26310     },
26311     
26312     onRotateRight : function(e)
26313     {
26314         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26315             
26316             var minScale = this.thumbEl.getWidth() / this.minWidth;
26317         
26318             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26319             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26320             
26321             this.startScale = this.scale;
26322             
26323             while (this.getScaleLevel() < minScale){
26324             
26325                 this.scale = this.scale + 1;
26326                 
26327                 if(!this.zoomable()){
26328                     break;
26329                 }
26330                 
26331                 if(
26332                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26333                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26334                 ){
26335                     continue;
26336                 }
26337                 
26338                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26339
26340                 this.draw();
26341                 
26342                 return;
26343             }
26344             
26345             this.scale = this.startScale;
26346             
26347             this.onRotateFail();
26348             
26349             return false;
26350         }
26351         
26352         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26353
26354         if(this.isDocument){
26355             this.setThumbBoxSize();
26356             this.setThumbBoxPosition();
26357             this.setCanvasPosition();
26358         }
26359         
26360         this.draw();
26361         
26362         this.fireEvent('rotate', this, 'right');
26363     },
26364     
26365     onRotateFail : function()
26366     {
26367         this.errorEl.show(true);
26368         
26369         var _this = this;
26370         
26371         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26372     },
26373     
26374     draw : function()
26375     {
26376         this.previewEl.dom.innerHTML = '';
26377         
26378         var canvasEl = document.createElement("canvas");
26379         
26380         var contextEl = canvasEl.getContext("2d");
26381         
26382         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26383         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26384         var center = this.imageEl.OriginWidth / 2;
26385         
26386         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26387             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26388             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26389             center = this.imageEl.OriginHeight / 2;
26390         }
26391         
26392         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26393         
26394         contextEl.translate(center, center);
26395         contextEl.rotate(this.rotate * Math.PI / 180);
26396
26397         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26398         
26399         this.canvasEl = document.createElement("canvas");
26400         
26401         this.contextEl = this.canvasEl.getContext("2d");
26402         
26403         switch (this.rotate) {
26404             case 0 :
26405                 
26406                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26407                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26408                 
26409                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26410                 
26411                 break;
26412             case 90 : 
26413                 
26414                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26415                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26416                 
26417                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26418                     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);
26419                     break;
26420                 }
26421                 
26422                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26423                 
26424                 break;
26425             case 180 :
26426                 
26427                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26428                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26429                 
26430                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26431                     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);
26432                     break;
26433                 }
26434                 
26435                 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);
26436                 
26437                 break;
26438             case 270 :
26439                 
26440                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26441                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26442         
26443                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26444                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26445                     break;
26446                 }
26447                 
26448                 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);
26449                 
26450                 break;
26451             default : 
26452                 break;
26453         }
26454         
26455         this.previewEl.appendChild(this.canvasEl);
26456         
26457         this.setCanvasPosition();
26458     },
26459     
26460     crop : function()
26461     {
26462         if(!this.canvasLoaded){
26463             return;
26464         }
26465         
26466         var imageCanvas = document.createElement("canvas");
26467         
26468         var imageContext = imageCanvas.getContext("2d");
26469         
26470         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26471         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26472         
26473         var center = imageCanvas.width / 2;
26474         
26475         imageContext.translate(center, center);
26476         
26477         imageContext.rotate(this.rotate * Math.PI / 180);
26478         
26479         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26480         
26481         var canvas = document.createElement("canvas");
26482         
26483         var context = canvas.getContext("2d");
26484                 
26485         canvas.width = this.minWidth;
26486         canvas.height = this.minHeight;
26487
26488         switch (this.rotate) {
26489             case 0 :
26490                 
26491                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26492                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26493                 
26494                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26495                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26496                 
26497                 var targetWidth = this.minWidth - 2 * x;
26498                 var targetHeight = this.minHeight - 2 * y;
26499                 
26500                 var scale = 1;
26501                 
26502                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26503                     scale = targetWidth / width;
26504                 }
26505                 
26506                 if(x > 0 && y == 0){
26507                     scale = targetHeight / height;
26508                 }
26509                 
26510                 if(x > 0 && y > 0){
26511                     scale = targetWidth / width;
26512                     
26513                     if(width < height){
26514                         scale = targetHeight / height;
26515                     }
26516                 }
26517                 
26518                 context.scale(scale, scale);
26519                 
26520                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26521                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26522
26523                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26524                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26525
26526                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26527                 
26528                 break;
26529             case 90 : 
26530                 
26531                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26532                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26533                 
26534                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26535                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26536                 
26537                 var targetWidth = this.minWidth - 2 * x;
26538                 var targetHeight = this.minHeight - 2 * y;
26539                 
26540                 var scale = 1;
26541                 
26542                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26543                     scale = targetWidth / width;
26544                 }
26545                 
26546                 if(x > 0 && y == 0){
26547                     scale = targetHeight / height;
26548                 }
26549                 
26550                 if(x > 0 && y > 0){
26551                     scale = targetWidth / width;
26552                     
26553                     if(width < height){
26554                         scale = targetHeight / height;
26555                     }
26556                 }
26557                 
26558                 context.scale(scale, scale);
26559                 
26560                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26561                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26562
26563                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26564                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26565                 
26566                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26567                 
26568                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26569                 
26570                 break;
26571             case 180 :
26572                 
26573                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26574                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26575                 
26576                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26577                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26578                 
26579                 var targetWidth = this.minWidth - 2 * x;
26580                 var targetHeight = this.minHeight - 2 * y;
26581                 
26582                 var scale = 1;
26583                 
26584                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26585                     scale = targetWidth / width;
26586                 }
26587                 
26588                 if(x > 0 && y == 0){
26589                     scale = targetHeight / height;
26590                 }
26591                 
26592                 if(x > 0 && y > 0){
26593                     scale = targetWidth / width;
26594                     
26595                     if(width < height){
26596                         scale = targetHeight / height;
26597                     }
26598                 }
26599                 
26600                 context.scale(scale, scale);
26601                 
26602                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26603                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26604
26605                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26606                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26607
26608                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26609                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26610                 
26611                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26612                 
26613                 break;
26614             case 270 :
26615                 
26616                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26617                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26618                 
26619                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26620                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26621                 
26622                 var targetWidth = this.minWidth - 2 * x;
26623                 var targetHeight = this.minHeight - 2 * y;
26624                 
26625                 var scale = 1;
26626                 
26627                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26628                     scale = targetWidth / width;
26629                 }
26630                 
26631                 if(x > 0 && y == 0){
26632                     scale = targetHeight / height;
26633                 }
26634                 
26635                 if(x > 0 && y > 0){
26636                     scale = targetWidth / width;
26637                     
26638                     if(width < height){
26639                         scale = targetHeight / height;
26640                     }
26641                 }
26642                 
26643                 context.scale(scale, scale);
26644                 
26645                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26646                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26647
26648                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26649                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26650                 
26651                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26652                 
26653                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26654                 
26655                 break;
26656             default : 
26657                 break;
26658         }
26659         
26660         this.cropData = canvas.toDataURL(this.cropType);
26661         
26662         if(this.fireEvent('crop', this, this.cropData) !== false){
26663             this.process(this.file, this.cropData);
26664         }
26665         
26666         return;
26667         
26668     },
26669     
26670     setThumbBoxSize : function()
26671     {
26672         var width, height;
26673         
26674         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26675             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26676             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26677             
26678             this.minWidth = width;
26679             this.minHeight = height;
26680             
26681             if(this.rotate == 90 || this.rotate == 270){
26682                 this.minWidth = height;
26683                 this.minHeight = width;
26684             }
26685         }
26686         
26687         height = 300;
26688         width = Math.ceil(this.minWidth * height / this.minHeight);
26689         
26690         if(this.minWidth > this.minHeight){
26691             width = 300;
26692             height = Math.ceil(this.minHeight * width / this.minWidth);
26693         }
26694         
26695         this.thumbEl.setStyle({
26696             width : width + 'px',
26697             height : height + 'px'
26698         });
26699
26700         return;
26701             
26702     },
26703     
26704     setThumbBoxPosition : function()
26705     {
26706         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26707         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26708         
26709         this.thumbEl.setLeft(x);
26710         this.thumbEl.setTop(y);
26711         
26712     },
26713     
26714     baseRotateLevel : function()
26715     {
26716         this.baseRotate = 1;
26717         
26718         if(
26719                 typeof(this.exif) != 'undefined' &&
26720                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26721                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26722         ){
26723             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26724         }
26725         
26726         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26727         
26728     },
26729     
26730     baseScaleLevel : function()
26731     {
26732         var width, height;
26733         
26734         if(this.isDocument){
26735             
26736             if(this.baseRotate == 6 || this.baseRotate == 8){
26737             
26738                 height = this.thumbEl.getHeight();
26739                 this.baseScale = height / this.imageEl.OriginWidth;
26740
26741                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26742                     width = this.thumbEl.getWidth();
26743                     this.baseScale = width / this.imageEl.OriginHeight;
26744                 }
26745
26746                 return;
26747             }
26748
26749             height = this.thumbEl.getHeight();
26750             this.baseScale = height / this.imageEl.OriginHeight;
26751
26752             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26753                 width = this.thumbEl.getWidth();
26754                 this.baseScale = width / this.imageEl.OriginWidth;
26755             }
26756
26757             return;
26758         }
26759         
26760         if(this.baseRotate == 6 || this.baseRotate == 8){
26761             
26762             width = this.thumbEl.getHeight();
26763             this.baseScale = width / this.imageEl.OriginHeight;
26764             
26765             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26766                 height = this.thumbEl.getWidth();
26767                 this.baseScale = height / this.imageEl.OriginHeight;
26768             }
26769             
26770             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26771                 height = this.thumbEl.getWidth();
26772                 this.baseScale = height / this.imageEl.OriginHeight;
26773                 
26774                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26775                     width = this.thumbEl.getHeight();
26776                     this.baseScale = width / this.imageEl.OriginWidth;
26777                 }
26778             }
26779             
26780             return;
26781         }
26782         
26783         width = this.thumbEl.getWidth();
26784         this.baseScale = width / this.imageEl.OriginWidth;
26785         
26786         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26787             height = this.thumbEl.getHeight();
26788             this.baseScale = height / this.imageEl.OriginHeight;
26789         }
26790         
26791         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26792             
26793             height = this.thumbEl.getHeight();
26794             this.baseScale = height / this.imageEl.OriginHeight;
26795             
26796             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26797                 width = this.thumbEl.getWidth();
26798                 this.baseScale = width / this.imageEl.OriginWidth;
26799             }
26800             
26801         }
26802         
26803         return;
26804     },
26805     
26806     getScaleLevel : function()
26807     {
26808         return this.baseScale * Math.pow(1.1, this.scale);
26809     },
26810     
26811     onTouchStart : function(e)
26812     {
26813         if(!this.canvasLoaded){
26814             this.beforeSelectFile(e);
26815             return;
26816         }
26817         
26818         var touches = e.browserEvent.touches;
26819         
26820         if(!touches){
26821             return;
26822         }
26823         
26824         if(touches.length == 1){
26825             this.onMouseDown(e);
26826             return;
26827         }
26828         
26829         if(touches.length != 2){
26830             return;
26831         }
26832         
26833         var coords = [];
26834         
26835         for(var i = 0, finger; finger = touches[i]; i++){
26836             coords.push(finger.pageX, finger.pageY);
26837         }
26838         
26839         var x = Math.pow(coords[0] - coords[2], 2);
26840         var y = Math.pow(coords[1] - coords[3], 2);
26841         
26842         this.startDistance = Math.sqrt(x + y);
26843         
26844         this.startScale = this.scale;
26845         
26846         this.pinching = true;
26847         this.dragable = false;
26848         
26849     },
26850     
26851     onTouchMove : function(e)
26852     {
26853         if(!this.pinching && !this.dragable){
26854             return;
26855         }
26856         
26857         var touches = e.browserEvent.touches;
26858         
26859         if(!touches){
26860             return;
26861         }
26862         
26863         if(this.dragable){
26864             this.onMouseMove(e);
26865             return;
26866         }
26867         
26868         var coords = [];
26869         
26870         for(var i = 0, finger; finger = touches[i]; i++){
26871             coords.push(finger.pageX, finger.pageY);
26872         }
26873         
26874         var x = Math.pow(coords[0] - coords[2], 2);
26875         var y = Math.pow(coords[1] - coords[3], 2);
26876         
26877         this.endDistance = Math.sqrt(x + y);
26878         
26879         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26880         
26881         if(!this.zoomable()){
26882             this.scale = this.startScale;
26883             return;
26884         }
26885         
26886         this.draw();
26887         
26888     },
26889     
26890     onTouchEnd : function(e)
26891     {
26892         this.pinching = false;
26893         this.dragable = false;
26894         
26895     },
26896     
26897     process : function(file, crop)
26898     {
26899         if(this.loadMask){
26900             this.maskEl.mask(this.loadingText);
26901         }
26902         
26903         this.xhr = new XMLHttpRequest();
26904         
26905         file.xhr = this.xhr;
26906
26907         this.xhr.open(this.method, this.url, true);
26908         
26909         var headers = {
26910             "Accept": "application/json",
26911             "Cache-Control": "no-cache",
26912             "X-Requested-With": "XMLHttpRequest"
26913         };
26914         
26915         for (var headerName in headers) {
26916             var headerValue = headers[headerName];
26917             if (headerValue) {
26918                 this.xhr.setRequestHeader(headerName, headerValue);
26919             }
26920         }
26921         
26922         var _this = this;
26923         
26924         this.xhr.onload = function()
26925         {
26926             _this.xhrOnLoad(_this.xhr);
26927         }
26928         
26929         this.xhr.onerror = function()
26930         {
26931             _this.xhrOnError(_this.xhr);
26932         }
26933         
26934         var formData = new FormData();
26935
26936         formData.append('returnHTML', 'NO');
26937         
26938         if(crop){
26939             formData.append('crop', crop);
26940         }
26941         
26942         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26943             formData.append(this.paramName, file, file.name);
26944         }
26945         
26946         if(typeof(file.filename) != 'undefined'){
26947             formData.append('filename', file.filename);
26948         }
26949         
26950         if(typeof(file.mimetype) != 'undefined'){
26951             formData.append('mimetype', file.mimetype);
26952         }
26953         
26954         if(this.fireEvent('arrange', this, formData) != false){
26955             this.xhr.send(formData);
26956         };
26957     },
26958     
26959     xhrOnLoad : function(xhr)
26960     {
26961         if(this.loadMask){
26962             this.maskEl.unmask();
26963         }
26964         
26965         if (xhr.readyState !== 4) {
26966             this.fireEvent('exception', this, xhr);
26967             return;
26968         }
26969
26970         var response = Roo.decode(xhr.responseText);
26971         
26972         if(!response.success){
26973             this.fireEvent('exception', this, xhr);
26974             return;
26975         }
26976         
26977         var response = Roo.decode(xhr.responseText);
26978         
26979         this.fireEvent('upload', this, response);
26980         
26981     },
26982     
26983     xhrOnError : function()
26984     {
26985         if(this.loadMask){
26986             this.maskEl.unmask();
26987         }
26988         
26989         Roo.log('xhr on error');
26990         
26991         var response = Roo.decode(xhr.responseText);
26992           
26993         Roo.log(response);
26994         
26995     },
26996     
26997     prepare : function(file)
26998     {   
26999         if(this.loadMask){
27000             this.maskEl.mask(this.loadingText);
27001         }
27002         
27003         this.file = false;
27004         this.exif = {};
27005         
27006         if(typeof(file) === 'string'){
27007             this.loadCanvas(file);
27008             return;
27009         }
27010         
27011         if(!file || !this.urlAPI){
27012             return;
27013         }
27014         
27015         this.file = file;
27016         this.cropType = file.type;
27017         
27018         var _this = this;
27019         
27020         if(this.fireEvent('prepare', this, this.file) != false){
27021             
27022             var reader = new FileReader();
27023             
27024             reader.onload = function (e) {
27025                 if (e.target.error) {
27026                     Roo.log(e.target.error);
27027                     return;
27028                 }
27029                 
27030                 var buffer = e.target.result,
27031                     dataView = new DataView(buffer),
27032                     offset = 2,
27033                     maxOffset = dataView.byteLength - 4,
27034                     markerBytes,
27035                     markerLength;
27036                 
27037                 if (dataView.getUint16(0) === 0xffd8) {
27038                     while (offset < maxOffset) {
27039                         markerBytes = dataView.getUint16(offset);
27040                         
27041                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27042                             markerLength = dataView.getUint16(offset + 2) + 2;
27043                             if (offset + markerLength > dataView.byteLength) {
27044                                 Roo.log('Invalid meta data: Invalid segment size.');
27045                                 break;
27046                             }
27047                             
27048                             if(markerBytes == 0xffe1){
27049                                 _this.parseExifData(
27050                                     dataView,
27051                                     offset,
27052                                     markerLength
27053                                 );
27054                             }
27055                             
27056                             offset += markerLength;
27057                             
27058                             continue;
27059                         }
27060                         
27061                         break;
27062                     }
27063                     
27064                 }
27065                 
27066                 var url = _this.urlAPI.createObjectURL(_this.file);
27067                 
27068                 _this.loadCanvas(url);
27069                 
27070                 return;
27071             }
27072             
27073             reader.readAsArrayBuffer(this.file);
27074             
27075         }
27076         
27077     },
27078     
27079     parseExifData : function(dataView, offset, length)
27080     {
27081         var tiffOffset = offset + 10,
27082             littleEndian,
27083             dirOffset;
27084     
27085         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27086             // No Exif data, might be XMP data instead
27087             return;
27088         }
27089         
27090         // Check for the ASCII code for "Exif" (0x45786966):
27091         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27092             // No Exif data, might be XMP data instead
27093             return;
27094         }
27095         if (tiffOffset + 8 > dataView.byteLength) {
27096             Roo.log('Invalid Exif data: Invalid segment size.');
27097             return;
27098         }
27099         // Check for the two null bytes:
27100         if (dataView.getUint16(offset + 8) !== 0x0000) {
27101             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27102             return;
27103         }
27104         // Check the byte alignment:
27105         switch (dataView.getUint16(tiffOffset)) {
27106         case 0x4949:
27107             littleEndian = true;
27108             break;
27109         case 0x4D4D:
27110             littleEndian = false;
27111             break;
27112         default:
27113             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27114             return;
27115         }
27116         // Check for the TIFF tag marker (0x002A):
27117         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27118             Roo.log('Invalid Exif data: Missing TIFF marker.');
27119             return;
27120         }
27121         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27122         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27123         
27124         this.parseExifTags(
27125             dataView,
27126             tiffOffset,
27127             tiffOffset + dirOffset,
27128             littleEndian
27129         );
27130     },
27131     
27132     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27133     {
27134         var tagsNumber,
27135             dirEndOffset,
27136             i;
27137         if (dirOffset + 6 > dataView.byteLength) {
27138             Roo.log('Invalid Exif data: Invalid directory offset.');
27139             return;
27140         }
27141         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27142         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27143         if (dirEndOffset + 4 > dataView.byteLength) {
27144             Roo.log('Invalid Exif data: Invalid directory size.');
27145             return;
27146         }
27147         for (i = 0; i < tagsNumber; i += 1) {
27148             this.parseExifTag(
27149                 dataView,
27150                 tiffOffset,
27151                 dirOffset + 2 + 12 * i, // tag offset
27152                 littleEndian
27153             );
27154         }
27155         // Return the offset to the next directory:
27156         return dataView.getUint32(dirEndOffset, littleEndian);
27157     },
27158     
27159     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27160     {
27161         var tag = dataView.getUint16(offset, littleEndian);
27162         
27163         this.exif[tag] = this.getExifValue(
27164             dataView,
27165             tiffOffset,
27166             offset,
27167             dataView.getUint16(offset + 2, littleEndian), // tag type
27168             dataView.getUint32(offset + 4, littleEndian), // tag length
27169             littleEndian
27170         );
27171     },
27172     
27173     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27174     {
27175         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27176             tagSize,
27177             dataOffset,
27178             values,
27179             i,
27180             str,
27181             c;
27182     
27183         if (!tagType) {
27184             Roo.log('Invalid Exif data: Invalid tag type.');
27185             return;
27186         }
27187         
27188         tagSize = tagType.size * length;
27189         // Determine if the value is contained in the dataOffset bytes,
27190         // or if the value at the dataOffset is a pointer to the actual data:
27191         dataOffset = tagSize > 4 ?
27192                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27193         if (dataOffset + tagSize > dataView.byteLength) {
27194             Roo.log('Invalid Exif data: Invalid data offset.');
27195             return;
27196         }
27197         if (length === 1) {
27198             return tagType.getValue(dataView, dataOffset, littleEndian);
27199         }
27200         values = [];
27201         for (i = 0; i < length; i += 1) {
27202             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27203         }
27204         
27205         if (tagType.ascii) {
27206             str = '';
27207             // Concatenate the chars:
27208             for (i = 0; i < values.length; i += 1) {
27209                 c = values[i];
27210                 // Ignore the terminating NULL byte(s):
27211                 if (c === '\u0000') {
27212                     break;
27213                 }
27214                 str += c;
27215             }
27216             return str;
27217         }
27218         return values;
27219     }
27220     
27221 });
27222
27223 Roo.apply(Roo.bootstrap.UploadCropbox, {
27224     tags : {
27225         'Orientation': 0x0112
27226     },
27227     
27228     Orientation: {
27229             1: 0, //'top-left',
27230 //            2: 'top-right',
27231             3: 180, //'bottom-right',
27232 //            4: 'bottom-left',
27233 //            5: 'left-top',
27234             6: 90, //'right-top',
27235 //            7: 'right-bottom',
27236             8: 270 //'left-bottom'
27237     },
27238     
27239     exifTagTypes : {
27240         // byte, 8-bit unsigned int:
27241         1: {
27242             getValue: function (dataView, dataOffset) {
27243                 return dataView.getUint8(dataOffset);
27244             },
27245             size: 1
27246         },
27247         // ascii, 8-bit byte:
27248         2: {
27249             getValue: function (dataView, dataOffset) {
27250                 return String.fromCharCode(dataView.getUint8(dataOffset));
27251             },
27252             size: 1,
27253             ascii: true
27254         },
27255         // short, 16 bit int:
27256         3: {
27257             getValue: function (dataView, dataOffset, littleEndian) {
27258                 return dataView.getUint16(dataOffset, littleEndian);
27259             },
27260             size: 2
27261         },
27262         // long, 32 bit int:
27263         4: {
27264             getValue: function (dataView, dataOffset, littleEndian) {
27265                 return dataView.getUint32(dataOffset, littleEndian);
27266             },
27267             size: 4
27268         },
27269         // rational = two long values, first is numerator, second is denominator:
27270         5: {
27271             getValue: function (dataView, dataOffset, littleEndian) {
27272                 return dataView.getUint32(dataOffset, littleEndian) /
27273                     dataView.getUint32(dataOffset + 4, littleEndian);
27274             },
27275             size: 8
27276         },
27277         // slong, 32 bit signed int:
27278         9: {
27279             getValue: function (dataView, dataOffset, littleEndian) {
27280                 return dataView.getInt32(dataOffset, littleEndian);
27281             },
27282             size: 4
27283         },
27284         // srational, two slongs, first is numerator, second is denominator:
27285         10: {
27286             getValue: function (dataView, dataOffset, littleEndian) {
27287                 return dataView.getInt32(dataOffset, littleEndian) /
27288                     dataView.getInt32(dataOffset + 4, littleEndian);
27289             },
27290             size: 8
27291         }
27292     },
27293     
27294     footer : {
27295         STANDARD : [
27296             {
27297                 tag : 'div',
27298                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27299                 action : 'rotate-left',
27300                 cn : [
27301                     {
27302                         tag : 'button',
27303                         cls : 'btn btn-default',
27304                         html : '<i class="fa fa-undo"></i>'
27305                     }
27306                 ]
27307             },
27308             {
27309                 tag : 'div',
27310                 cls : 'btn-group roo-upload-cropbox-picture',
27311                 action : 'picture',
27312                 cn : [
27313                     {
27314                         tag : 'button',
27315                         cls : 'btn btn-default',
27316                         html : '<i class="fa fa-picture-o"></i>'
27317                     }
27318                 ]
27319             },
27320             {
27321                 tag : 'div',
27322                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27323                 action : 'rotate-right',
27324                 cn : [
27325                     {
27326                         tag : 'button',
27327                         cls : 'btn btn-default',
27328                         html : '<i class="fa fa-repeat"></i>'
27329                     }
27330                 ]
27331             }
27332         ],
27333         DOCUMENT : [
27334             {
27335                 tag : 'div',
27336                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27337                 action : 'rotate-left',
27338                 cn : [
27339                     {
27340                         tag : 'button',
27341                         cls : 'btn btn-default',
27342                         html : '<i class="fa fa-undo"></i>'
27343                     }
27344                 ]
27345             },
27346             {
27347                 tag : 'div',
27348                 cls : 'btn-group roo-upload-cropbox-download',
27349                 action : 'download',
27350                 cn : [
27351                     {
27352                         tag : 'button',
27353                         cls : 'btn btn-default',
27354                         html : '<i class="fa fa-download"></i>'
27355                     }
27356                 ]
27357             },
27358             {
27359                 tag : 'div',
27360                 cls : 'btn-group roo-upload-cropbox-crop',
27361                 action : 'crop',
27362                 cn : [
27363                     {
27364                         tag : 'button',
27365                         cls : 'btn btn-default',
27366                         html : '<i class="fa fa-crop"></i>'
27367                     }
27368                 ]
27369             },
27370             {
27371                 tag : 'div',
27372                 cls : 'btn-group roo-upload-cropbox-trash',
27373                 action : 'trash',
27374                 cn : [
27375                     {
27376                         tag : 'button',
27377                         cls : 'btn btn-default',
27378                         html : '<i class="fa fa-trash"></i>'
27379                     }
27380                 ]
27381             },
27382             {
27383                 tag : 'div',
27384                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27385                 action : 'rotate-right',
27386                 cn : [
27387                     {
27388                         tag : 'button',
27389                         cls : 'btn btn-default',
27390                         html : '<i class="fa fa-repeat"></i>'
27391                     }
27392                 ]
27393             }
27394         ],
27395         ROTATOR : [
27396             {
27397                 tag : 'div',
27398                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27399                 action : 'rotate-left',
27400                 cn : [
27401                     {
27402                         tag : 'button',
27403                         cls : 'btn btn-default',
27404                         html : '<i class="fa fa-undo"></i>'
27405                     }
27406                 ]
27407             },
27408             {
27409                 tag : 'div',
27410                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27411                 action : 'rotate-right',
27412                 cn : [
27413                     {
27414                         tag : 'button',
27415                         cls : 'btn btn-default',
27416                         html : '<i class="fa fa-repeat"></i>'
27417                     }
27418                 ]
27419             }
27420         ]
27421     }
27422 });
27423
27424 /*
27425 * Licence: LGPL
27426 */
27427
27428 /**
27429  * @class Roo.bootstrap.DocumentManager
27430  * @extends Roo.bootstrap.Component
27431  * Bootstrap DocumentManager class
27432  * @cfg {String} paramName default 'imageUpload'
27433  * @cfg {String} toolTipName default 'filename'
27434  * @cfg {String} method default POST
27435  * @cfg {String} url action url
27436  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27437  * @cfg {Boolean} multiple multiple upload default true
27438  * @cfg {Number} thumbSize default 300
27439  * @cfg {String} fieldLabel
27440  * @cfg {Number} labelWidth default 4
27441  * @cfg {String} labelAlign (left|top) default left
27442  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27443  * 
27444  * @constructor
27445  * Create a new DocumentManager
27446  * @param {Object} config The config object
27447  */
27448
27449 Roo.bootstrap.DocumentManager = function(config){
27450     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27451     
27452     this.files = [];
27453     this.delegates = [];
27454     
27455     this.addEvents({
27456         /**
27457          * @event initial
27458          * Fire when initial the DocumentManager
27459          * @param {Roo.bootstrap.DocumentManager} this
27460          */
27461         "initial" : true,
27462         /**
27463          * @event inspect
27464          * inspect selected file
27465          * @param {Roo.bootstrap.DocumentManager} this
27466          * @param {File} file
27467          */
27468         "inspect" : true,
27469         /**
27470          * @event exception
27471          * Fire when xhr load exception
27472          * @param {Roo.bootstrap.DocumentManager} this
27473          * @param {XMLHttpRequest} xhr
27474          */
27475         "exception" : true,
27476         /**
27477          * @event afterupload
27478          * Fire when xhr load exception
27479          * @param {Roo.bootstrap.DocumentManager} this
27480          * @param {XMLHttpRequest} xhr
27481          */
27482         "afterupload" : true,
27483         /**
27484          * @event prepare
27485          * prepare the form data
27486          * @param {Roo.bootstrap.DocumentManager} this
27487          * @param {Object} formData
27488          */
27489         "prepare" : true,
27490         /**
27491          * @event remove
27492          * Fire when remove the file
27493          * @param {Roo.bootstrap.DocumentManager} this
27494          * @param {Object} file
27495          */
27496         "remove" : true,
27497         /**
27498          * @event refresh
27499          * Fire after refresh the file
27500          * @param {Roo.bootstrap.DocumentManager} this
27501          */
27502         "refresh" : true,
27503         /**
27504          * @event click
27505          * Fire after click the image
27506          * @param {Roo.bootstrap.DocumentManager} this
27507          * @param {Object} file
27508          */
27509         "click" : true,
27510         /**
27511          * @event edit
27512          * Fire when upload a image and editable set to true
27513          * @param {Roo.bootstrap.DocumentManager} this
27514          * @param {Object} file
27515          */
27516         "edit" : true,
27517         /**
27518          * @event beforeselectfile
27519          * Fire before select file
27520          * @param {Roo.bootstrap.DocumentManager} this
27521          */
27522         "beforeselectfile" : true,
27523         /**
27524          * @event process
27525          * Fire before process file
27526          * @param {Roo.bootstrap.DocumentManager} this
27527          * @param {Object} file
27528          */
27529         "process" : true
27530         
27531     });
27532 };
27533
27534 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27535     
27536     boxes : 0,
27537     inputName : '',
27538     thumbSize : 300,
27539     multiple : true,
27540     files : false,
27541     method : 'POST',
27542     url : '',
27543     paramName : 'imageUpload',
27544     toolTipName : 'filename',
27545     fieldLabel : '',
27546     labelWidth : 4,
27547     labelAlign : 'left',
27548     editable : true,
27549     delegates : false,
27550     xhr : false, 
27551     
27552     getAutoCreate : function()
27553     {   
27554         var managerWidget = {
27555             tag : 'div',
27556             cls : 'roo-document-manager',
27557             cn : [
27558                 {
27559                     tag : 'input',
27560                     cls : 'roo-document-manager-selector',
27561                     type : 'file'
27562                 },
27563                 {
27564                     tag : 'div',
27565                     cls : 'roo-document-manager-uploader',
27566                     cn : [
27567                         {
27568                             tag : 'div',
27569                             cls : 'roo-document-manager-upload-btn',
27570                             html : '<i class="fa fa-plus"></i>'
27571                         }
27572                     ]
27573                     
27574                 }
27575             ]
27576         };
27577         
27578         var content = [
27579             {
27580                 tag : 'div',
27581                 cls : 'column col-md-12',
27582                 cn : managerWidget
27583             }
27584         ];
27585         
27586         if(this.fieldLabel.length){
27587             
27588             content = [
27589                 {
27590                     tag : 'div',
27591                     cls : 'column col-md-12',
27592                     html : this.fieldLabel
27593                 },
27594                 {
27595                     tag : 'div',
27596                     cls : 'column col-md-12',
27597                     cn : managerWidget
27598                 }
27599             ];
27600
27601             if(this.labelAlign == 'left'){
27602                 content = [
27603                     {
27604                         tag : 'div',
27605                         cls : 'column col-md-' + this.labelWidth,
27606                         html : this.fieldLabel
27607                     },
27608                     {
27609                         tag : 'div',
27610                         cls : 'column col-md-' + (12 - this.labelWidth),
27611                         cn : managerWidget
27612                     }
27613                 ];
27614                 
27615             }
27616         }
27617         
27618         var cfg = {
27619             tag : 'div',
27620             cls : 'row clearfix',
27621             cn : content
27622         };
27623         
27624         return cfg;
27625         
27626     },
27627     
27628     initEvents : function()
27629     {
27630         this.managerEl = this.el.select('.roo-document-manager', true).first();
27631         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27632         
27633         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27634         this.selectorEl.hide();
27635         
27636         if(this.multiple){
27637             this.selectorEl.attr('multiple', 'multiple');
27638         }
27639         
27640         this.selectorEl.on('change', this.onFileSelected, this);
27641         
27642         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27643         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27644         
27645         this.uploader.on('click', this.onUploaderClick, this);
27646         
27647         this.renderProgressDialog();
27648         
27649         var _this = this;
27650         
27651         window.addEventListener("resize", function() { _this.refresh(); } );
27652         
27653         this.fireEvent('initial', this);
27654     },
27655     
27656     renderProgressDialog : function()
27657     {
27658         var _this = this;
27659         
27660         this.progressDialog = new Roo.bootstrap.Modal({
27661             cls : 'roo-document-manager-progress-dialog',
27662             allow_close : false,
27663             title : '',
27664             buttons : [
27665                 {
27666                     name  :'cancel',
27667                     weight : 'danger',
27668                     html : 'Cancel'
27669                 }
27670             ], 
27671             listeners : { 
27672                 btnclick : function() {
27673                     _this.uploadCancel();
27674                     this.hide();
27675                 }
27676             }
27677         });
27678          
27679         this.progressDialog.render(Roo.get(document.body));
27680          
27681         this.progress = new Roo.bootstrap.Progress({
27682             cls : 'roo-document-manager-progress',
27683             active : true,
27684             striped : true
27685         });
27686         
27687         this.progress.render(this.progressDialog.getChildContainer());
27688         
27689         this.progressBar = new Roo.bootstrap.ProgressBar({
27690             cls : 'roo-document-manager-progress-bar',
27691             aria_valuenow : 0,
27692             aria_valuemin : 0,
27693             aria_valuemax : 12,
27694             panel : 'success'
27695         });
27696         
27697         this.progressBar.render(this.progress.getChildContainer());
27698     },
27699     
27700     onUploaderClick : function(e)
27701     {
27702         e.preventDefault();
27703      
27704         if(this.fireEvent('beforeselectfile', this) != false){
27705             this.selectorEl.dom.click();
27706         }
27707         
27708     },
27709     
27710     onFileSelected : function(e)
27711     {
27712         e.preventDefault();
27713         
27714         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27715             return;
27716         }
27717         
27718         Roo.each(this.selectorEl.dom.files, function(file){
27719             if(this.fireEvent('inspect', this, file) != false){
27720                 this.files.push(file);
27721             }
27722         }, this);
27723         
27724         this.queue();
27725         
27726     },
27727     
27728     queue : function()
27729     {
27730         this.selectorEl.dom.value = '';
27731         
27732         if(!this.files.length){
27733             return;
27734         }
27735         
27736         if(this.boxes > 0 && this.files.length > this.boxes){
27737             this.files = this.files.slice(0, this.boxes);
27738         }
27739         
27740         this.uploader.show();
27741         
27742         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27743             this.uploader.hide();
27744         }
27745         
27746         var _this = this;
27747         
27748         var files = [];
27749         
27750         var docs = [];
27751         
27752         Roo.each(this.files, function(file){
27753             
27754             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27755                 var f = this.renderPreview(file);
27756                 files.push(f);
27757                 return;
27758             }
27759             
27760             if(file.type.indexOf('image') != -1){
27761                 this.delegates.push(
27762                     (function(){
27763                         _this.process(file);
27764                     }).createDelegate(this)
27765                 );
27766         
27767                 return;
27768             }
27769             
27770             docs.push(
27771                 (function(){
27772                     _this.process(file);
27773                 }).createDelegate(this)
27774             );
27775             
27776         }, this);
27777         
27778         this.files = files;
27779         
27780         this.delegates = this.delegates.concat(docs);
27781         
27782         if(!this.delegates.length){
27783             this.refresh();
27784             return;
27785         }
27786         
27787         this.progressBar.aria_valuemax = this.delegates.length;
27788         
27789         this.arrange();
27790         
27791         return;
27792     },
27793     
27794     arrange : function()
27795     {
27796         if(!this.delegates.length){
27797             this.progressDialog.hide();
27798             this.refresh();
27799             return;
27800         }
27801         
27802         var delegate = this.delegates.shift();
27803         
27804         this.progressDialog.show();
27805         
27806         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27807         
27808         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27809         
27810         delegate();
27811     },
27812     
27813     refresh : function()
27814     {
27815         this.uploader.show();
27816         
27817         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27818             this.uploader.hide();
27819         }
27820         
27821         Roo.isTouch ? this.closable(false) : this.closable(true);
27822         
27823         this.fireEvent('refresh', this);
27824     },
27825     
27826     onRemove : function(e, el, o)
27827     {
27828         e.preventDefault();
27829         
27830         this.fireEvent('remove', this, o);
27831         
27832     },
27833     
27834     remove : function(o)
27835     {
27836         var files = [];
27837         
27838         Roo.each(this.files, function(file){
27839             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27840                 files.push(file);
27841                 return;
27842             }
27843
27844             o.target.remove();
27845
27846         }, this);
27847         
27848         this.files = files;
27849         
27850         this.refresh();
27851     },
27852     
27853     clear : function()
27854     {
27855         Roo.each(this.files, function(file){
27856             if(!file.target){
27857                 return;
27858             }
27859             
27860             file.target.remove();
27861
27862         }, this);
27863         
27864         this.files = [];
27865         
27866         this.refresh();
27867     },
27868     
27869     onClick : function(e, el, o)
27870     {
27871         e.preventDefault();
27872         
27873         this.fireEvent('click', this, o);
27874         
27875     },
27876     
27877     closable : function(closable)
27878     {
27879         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27880             
27881             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27882             
27883             if(closable){
27884                 el.show();
27885                 return;
27886             }
27887             
27888             el.hide();
27889             
27890         }, this);
27891     },
27892     
27893     xhrOnLoad : function(xhr)
27894     {
27895         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27896             el.remove();
27897         }, this);
27898         
27899         if (xhr.readyState !== 4) {
27900             this.arrange();
27901             this.fireEvent('exception', this, xhr);
27902             return;
27903         }
27904
27905         var response = Roo.decode(xhr.responseText);
27906         
27907         if(!response.success){
27908             this.arrange();
27909             this.fireEvent('exception', this, xhr);
27910             return;
27911         }
27912         
27913         var file = this.renderPreview(response.data);
27914         
27915         this.files.push(file);
27916         
27917         this.arrange();
27918         
27919         this.fireEvent('afterupload', this, xhr);
27920         
27921     },
27922     
27923     xhrOnError : function(xhr)
27924     {
27925         Roo.log('xhr on error');
27926         
27927         var response = Roo.decode(xhr.responseText);
27928           
27929         Roo.log(response);
27930         
27931         this.arrange();
27932     },
27933     
27934     process : function(file)
27935     {
27936         if(this.fireEvent('process', this, file) !== false){
27937             if(this.editable && file.type.indexOf('image') != -1){
27938                 this.fireEvent('edit', this, file);
27939                 return;
27940             }
27941
27942             this.uploadStart(file, false);
27943
27944             return;
27945         }
27946         
27947     },
27948     
27949     uploadStart : function(file, crop)
27950     {
27951         this.xhr = new XMLHttpRequest();
27952         
27953         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27954             this.arrange();
27955             return;
27956         }
27957         
27958         file.xhr = this.xhr;
27959             
27960         this.managerEl.createChild({
27961             tag : 'div',
27962             cls : 'roo-document-manager-loading',
27963             cn : [
27964                 {
27965                     tag : 'div',
27966                     tooltip : file.name,
27967                     cls : 'roo-document-manager-thumb',
27968                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27969                 }
27970             ]
27971
27972         });
27973
27974         this.xhr.open(this.method, this.url, true);
27975         
27976         var headers = {
27977             "Accept": "application/json",
27978             "Cache-Control": "no-cache",
27979             "X-Requested-With": "XMLHttpRequest"
27980         };
27981         
27982         for (var headerName in headers) {
27983             var headerValue = headers[headerName];
27984             if (headerValue) {
27985                 this.xhr.setRequestHeader(headerName, headerValue);
27986             }
27987         }
27988         
27989         var _this = this;
27990         
27991         this.xhr.onload = function()
27992         {
27993             _this.xhrOnLoad(_this.xhr);
27994         }
27995         
27996         this.xhr.onerror = function()
27997         {
27998             _this.xhrOnError(_this.xhr);
27999         }
28000         
28001         var formData = new FormData();
28002
28003         formData.append('returnHTML', 'NO');
28004         
28005         if(crop){
28006             formData.append('crop', crop);
28007         }
28008         
28009         formData.append(this.paramName, file, file.name);
28010         
28011         var options = {
28012             file : file, 
28013             manually : false
28014         };
28015         
28016         if(this.fireEvent('prepare', this, formData, options) != false){
28017             
28018             if(options.manually){
28019                 return;
28020             }
28021             
28022             this.xhr.send(formData);
28023             return;
28024         };
28025         
28026         this.uploadCancel();
28027     },
28028     
28029     uploadCancel : function()
28030     {
28031         if (this.xhr) {
28032             this.xhr.abort();
28033         }
28034         
28035         this.delegates = [];
28036         
28037         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28038             el.remove();
28039         }, this);
28040         
28041         this.arrange();
28042     },
28043     
28044     renderPreview : function(file)
28045     {
28046         if(typeof(file.target) != 'undefined' && file.target){
28047             return file;
28048         }
28049         
28050         var previewEl = this.managerEl.createChild({
28051             tag : 'div',
28052             cls : 'roo-document-manager-preview',
28053             cn : [
28054                 {
28055                     tag : 'div',
28056                     tooltip : file[this.toolTipName],
28057                     cls : 'roo-document-manager-thumb',
28058                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28059                 },
28060                 {
28061                     tag : 'button',
28062                     cls : 'close',
28063                     html : '<i class="fa fa-times-circle"></i>'
28064                 }
28065             ]
28066         });
28067
28068         var close = previewEl.select('button.close', true).first();
28069
28070         close.on('click', this.onRemove, this, file);
28071
28072         file.target = previewEl;
28073
28074         var image = previewEl.select('img', true).first();
28075         
28076         var _this = this;
28077         
28078         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28079         
28080         image.on('click', this.onClick, this, file);
28081         
28082         return file;
28083         
28084     },
28085     
28086     onPreviewLoad : function(file, image)
28087     {
28088         if(typeof(file.target) == 'undefined' || !file.target){
28089             return;
28090         }
28091         
28092         var width = image.dom.naturalWidth || image.dom.width;
28093         var height = image.dom.naturalHeight || image.dom.height;
28094         
28095         if(width > height){
28096             file.target.addClass('wide');
28097             return;
28098         }
28099         
28100         file.target.addClass('tall');
28101         return;
28102         
28103     },
28104     
28105     uploadFromSource : function(file, crop)
28106     {
28107         this.xhr = new XMLHttpRequest();
28108         
28109         this.managerEl.createChild({
28110             tag : 'div',
28111             cls : 'roo-document-manager-loading',
28112             cn : [
28113                 {
28114                     tag : 'div',
28115                     tooltip : file.name,
28116                     cls : 'roo-document-manager-thumb',
28117                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28118                 }
28119             ]
28120
28121         });
28122
28123         this.xhr.open(this.method, this.url, true);
28124         
28125         var headers = {
28126             "Accept": "application/json",
28127             "Cache-Control": "no-cache",
28128             "X-Requested-With": "XMLHttpRequest"
28129         };
28130         
28131         for (var headerName in headers) {
28132             var headerValue = headers[headerName];
28133             if (headerValue) {
28134                 this.xhr.setRequestHeader(headerName, headerValue);
28135             }
28136         }
28137         
28138         var _this = this;
28139         
28140         this.xhr.onload = function()
28141         {
28142             _this.xhrOnLoad(_this.xhr);
28143         }
28144         
28145         this.xhr.onerror = function()
28146         {
28147             _this.xhrOnError(_this.xhr);
28148         }
28149         
28150         var formData = new FormData();
28151
28152         formData.append('returnHTML', 'NO');
28153         
28154         formData.append('crop', crop);
28155         
28156         if(typeof(file.filename) != 'undefined'){
28157             formData.append('filename', file.filename);
28158         }
28159         
28160         if(typeof(file.mimetype) != 'undefined'){
28161             formData.append('mimetype', file.mimetype);
28162         }
28163         
28164         if(this.fireEvent('prepare', this, formData) != false){
28165             this.xhr.send(formData);
28166         };
28167     }
28168 });
28169
28170 /*
28171 * Licence: LGPL
28172 */
28173
28174 /**
28175  * @class Roo.bootstrap.DocumentViewer
28176  * @extends Roo.bootstrap.Component
28177  * Bootstrap DocumentViewer class
28178  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28179  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28180  * 
28181  * @constructor
28182  * Create a new DocumentViewer
28183  * @param {Object} config The config object
28184  */
28185
28186 Roo.bootstrap.DocumentViewer = function(config){
28187     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28188     
28189     this.addEvents({
28190         /**
28191          * @event initial
28192          * Fire after initEvent
28193          * @param {Roo.bootstrap.DocumentViewer} this
28194          */
28195         "initial" : true,
28196         /**
28197          * @event click
28198          * Fire after click
28199          * @param {Roo.bootstrap.DocumentViewer} this
28200          */
28201         "click" : true,
28202         /**
28203          * @event download
28204          * Fire after download button
28205          * @param {Roo.bootstrap.DocumentViewer} this
28206          */
28207         "download" : true,
28208         /**
28209          * @event trash
28210          * Fire after trash button
28211          * @param {Roo.bootstrap.DocumentViewer} this
28212          */
28213         "trash" : true
28214         
28215     });
28216 };
28217
28218 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28219     
28220     showDownload : true,
28221     
28222     showTrash : true,
28223     
28224     getAutoCreate : function()
28225     {
28226         var cfg = {
28227             tag : 'div',
28228             cls : 'roo-document-viewer',
28229             cn : [
28230                 {
28231                     tag : 'div',
28232                     cls : 'roo-document-viewer-body',
28233                     cn : [
28234                         {
28235                             tag : 'div',
28236                             cls : 'roo-document-viewer-thumb',
28237                             cn : [
28238                                 {
28239                                     tag : 'img',
28240                                     cls : 'roo-document-viewer-image'
28241                                 }
28242                             ]
28243                         }
28244                     ]
28245                 },
28246                 {
28247                     tag : 'div',
28248                     cls : 'roo-document-viewer-footer',
28249                     cn : {
28250                         tag : 'div',
28251                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28252                         cn : [
28253                             {
28254                                 tag : 'div',
28255                                 cls : 'btn-group roo-document-viewer-download',
28256                                 cn : [
28257                                     {
28258                                         tag : 'button',
28259                                         cls : 'btn btn-default',
28260                                         html : '<i class="fa fa-download"></i>'
28261                                     }
28262                                 ]
28263                             },
28264                             {
28265                                 tag : 'div',
28266                                 cls : 'btn-group roo-document-viewer-trash',
28267                                 cn : [
28268                                     {
28269                                         tag : 'button',
28270                                         cls : 'btn btn-default',
28271                                         html : '<i class="fa fa-trash"></i>'
28272                                     }
28273                                 ]
28274                             }
28275                         ]
28276                     }
28277                 }
28278             ]
28279         };
28280         
28281         return cfg;
28282     },
28283     
28284     initEvents : function()
28285     {
28286         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28287         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28288         
28289         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28290         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28291         
28292         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28293         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28294         
28295         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28296         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28297         
28298         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28299         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28300         
28301         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28302         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28303         
28304         this.bodyEl.on('click', this.onClick, this);
28305         this.downloadBtn.on('click', this.onDownload, this);
28306         this.trashBtn.on('click', this.onTrash, this);
28307         
28308         this.downloadBtn.hide();
28309         this.trashBtn.hide();
28310         
28311         if(this.showDownload){
28312             this.downloadBtn.show();
28313         }
28314         
28315         if(this.showTrash){
28316             this.trashBtn.show();
28317         }
28318         
28319         if(!this.showDownload && !this.showTrash) {
28320             this.footerEl.hide();
28321         }
28322         
28323     },
28324     
28325     initial : function()
28326     {
28327         this.fireEvent('initial', this);
28328         
28329     },
28330     
28331     onClick : function(e)
28332     {
28333         e.preventDefault();
28334         
28335         this.fireEvent('click', this);
28336     },
28337     
28338     onDownload : function(e)
28339     {
28340         e.preventDefault();
28341         
28342         this.fireEvent('download', this);
28343     },
28344     
28345     onTrash : function(e)
28346     {
28347         e.preventDefault();
28348         
28349         this.fireEvent('trash', this);
28350     }
28351     
28352 });
28353 /*
28354  * - LGPL
28355  *
28356  * nav progress bar
28357  * 
28358  */
28359
28360 /**
28361  * @class Roo.bootstrap.NavProgressBar
28362  * @extends Roo.bootstrap.Component
28363  * Bootstrap NavProgressBar class
28364  * 
28365  * @constructor
28366  * Create a new nav progress bar
28367  * @param {Object} config The config object
28368  */
28369
28370 Roo.bootstrap.NavProgressBar = function(config){
28371     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28372
28373     this.bullets = this.bullets || [];
28374    
28375 //    Roo.bootstrap.NavProgressBar.register(this);
28376      this.addEvents({
28377         /**
28378              * @event changed
28379              * Fires when the active item changes
28380              * @param {Roo.bootstrap.NavProgressBar} this
28381              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28382              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28383          */
28384         'changed': true
28385      });
28386     
28387 };
28388
28389 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28390     
28391     bullets : [],
28392     barItems : [],
28393     
28394     getAutoCreate : function()
28395     {
28396         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28397         
28398         cfg = {
28399             tag : 'div',
28400             cls : 'roo-navigation-bar-group',
28401             cn : [
28402                 {
28403                     tag : 'div',
28404                     cls : 'roo-navigation-top-bar'
28405                 },
28406                 {
28407                     tag : 'div',
28408                     cls : 'roo-navigation-bullets-bar',
28409                     cn : [
28410                         {
28411                             tag : 'ul',
28412                             cls : 'roo-navigation-bar'
28413                         }
28414                     ]
28415                 },
28416                 
28417                 {
28418                     tag : 'div',
28419                     cls : 'roo-navigation-bottom-bar'
28420                 }
28421             ]
28422             
28423         };
28424         
28425         return cfg;
28426         
28427     },
28428     
28429     initEvents: function() 
28430     {
28431         
28432     },
28433     
28434     onRender : function(ct, position) 
28435     {
28436         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28437         
28438         if(this.bullets.length){
28439             Roo.each(this.bullets, function(b){
28440                this.addItem(b);
28441             }, this);
28442         }
28443         
28444         this.format();
28445         
28446     },
28447     
28448     addItem : function(cfg)
28449     {
28450         var item = new Roo.bootstrap.NavProgressItem(cfg);
28451         
28452         item.parentId = this.id;
28453         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28454         
28455         if(cfg.html){
28456             var top = new Roo.bootstrap.Element({
28457                 tag : 'div',
28458                 cls : 'roo-navigation-bar-text'
28459             });
28460             
28461             var bottom = new Roo.bootstrap.Element({
28462                 tag : 'div',
28463                 cls : 'roo-navigation-bar-text'
28464             });
28465             
28466             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28467             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28468             
28469             var topText = new Roo.bootstrap.Element({
28470                 tag : 'span',
28471                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28472             });
28473             
28474             var bottomText = new Roo.bootstrap.Element({
28475                 tag : 'span',
28476                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28477             });
28478             
28479             topText.onRender(top.el, null);
28480             bottomText.onRender(bottom.el, null);
28481             
28482             item.topEl = top;
28483             item.bottomEl = bottom;
28484         }
28485         
28486         this.barItems.push(item);
28487         
28488         return item;
28489     },
28490     
28491     getActive : function()
28492     {
28493         var active = false;
28494         
28495         Roo.each(this.barItems, function(v){
28496             
28497             if (!v.isActive()) {
28498                 return;
28499             }
28500             
28501             active = v;
28502             return false;
28503             
28504         });
28505         
28506         return active;
28507     },
28508     
28509     setActiveItem : function(item)
28510     {
28511         var prev = false;
28512         
28513         Roo.each(this.barItems, function(v){
28514             if (v.rid == item.rid) {
28515                 return ;
28516             }
28517             
28518             if (v.isActive()) {
28519                 v.setActive(false);
28520                 prev = v;
28521             }
28522         });
28523
28524         item.setActive(true);
28525         
28526         this.fireEvent('changed', this, item, prev);
28527     },
28528     
28529     getBarItem: function(rid)
28530     {
28531         var ret = false;
28532         
28533         Roo.each(this.barItems, function(e) {
28534             if (e.rid != rid) {
28535                 return;
28536             }
28537             
28538             ret =  e;
28539             return false;
28540         });
28541         
28542         return ret;
28543     },
28544     
28545     indexOfItem : function(item)
28546     {
28547         var index = false;
28548         
28549         Roo.each(this.barItems, function(v, i){
28550             
28551             if (v.rid != item.rid) {
28552                 return;
28553             }
28554             
28555             index = i;
28556             return false
28557         });
28558         
28559         return index;
28560     },
28561     
28562     setActiveNext : function()
28563     {
28564         var i = this.indexOfItem(this.getActive());
28565         
28566         if (i > this.barItems.length) {
28567             return;
28568         }
28569         
28570         this.setActiveItem(this.barItems[i+1]);
28571     },
28572     
28573     setActivePrev : function()
28574     {
28575         var i = this.indexOfItem(this.getActive());
28576         
28577         if (i  < 1) {
28578             return;
28579         }
28580         
28581         this.setActiveItem(this.barItems[i-1]);
28582     },
28583     
28584     format : function()
28585     {
28586         if(!this.barItems.length){
28587             return;
28588         }
28589      
28590         var width = 100 / this.barItems.length;
28591         
28592         Roo.each(this.barItems, function(i){
28593             i.el.setStyle('width', width + '%');
28594             i.topEl.el.setStyle('width', width + '%');
28595             i.bottomEl.el.setStyle('width', width + '%');
28596         }, this);
28597         
28598     }
28599     
28600 });
28601 /*
28602  * - LGPL
28603  *
28604  * Nav Progress Item
28605  * 
28606  */
28607
28608 /**
28609  * @class Roo.bootstrap.NavProgressItem
28610  * @extends Roo.bootstrap.Component
28611  * Bootstrap NavProgressItem class
28612  * @cfg {String} rid the reference id
28613  * @cfg {Boolean} active (true|false) Is item active default false
28614  * @cfg {Boolean} disabled (true|false) Is item active default false
28615  * @cfg {String} html
28616  * @cfg {String} position (top|bottom) text position default bottom
28617  * @cfg {String} icon show icon instead of number
28618  * 
28619  * @constructor
28620  * Create a new NavProgressItem
28621  * @param {Object} config The config object
28622  */
28623 Roo.bootstrap.NavProgressItem = function(config){
28624     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28625     this.addEvents({
28626         // raw events
28627         /**
28628          * @event click
28629          * The raw click event for the entire grid.
28630          * @param {Roo.bootstrap.NavProgressItem} this
28631          * @param {Roo.EventObject} e
28632          */
28633         "click" : true
28634     });
28635    
28636 };
28637
28638 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28639     
28640     rid : '',
28641     active : false,
28642     disabled : false,
28643     html : '',
28644     position : 'bottom',
28645     icon : false,
28646     
28647     getAutoCreate : function()
28648     {
28649         var iconCls = 'roo-navigation-bar-item-icon';
28650         
28651         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28652         
28653         var cfg = {
28654             tag: 'li',
28655             cls: 'roo-navigation-bar-item',
28656             cn : [
28657                 {
28658                     tag : 'i',
28659                     cls : iconCls
28660                 }
28661             ]
28662         };
28663         
28664         if(this.active){
28665             cfg.cls += ' active';
28666         }
28667         if(this.disabled){
28668             cfg.cls += ' disabled';
28669         }
28670         
28671         return cfg;
28672     },
28673     
28674     disable : function()
28675     {
28676         this.setDisabled(true);
28677     },
28678     
28679     enable : function()
28680     {
28681         this.setDisabled(false);
28682     },
28683     
28684     initEvents: function() 
28685     {
28686         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28687         
28688         this.iconEl.on('click', this.onClick, this);
28689     },
28690     
28691     onClick : function(e)
28692     {
28693         e.preventDefault();
28694         
28695         if(this.disabled){
28696             return;
28697         }
28698         
28699         if(this.fireEvent('click', this, e) === false){
28700             return;
28701         };
28702         
28703         this.parent().setActiveItem(this);
28704     },
28705     
28706     isActive: function () 
28707     {
28708         return this.active;
28709     },
28710     
28711     setActive : function(state)
28712     {
28713         if(this.active == state){
28714             return;
28715         }
28716         
28717         this.active = state;
28718         
28719         if (state) {
28720             this.el.addClass('active');
28721             return;
28722         }
28723         
28724         this.el.removeClass('active');
28725         
28726         return;
28727     },
28728     
28729     setDisabled : function(state)
28730     {
28731         if(this.disabled == state){
28732             return;
28733         }
28734         
28735         this.disabled = state;
28736         
28737         if (state) {
28738             this.el.addClass('disabled');
28739             return;
28740         }
28741         
28742         this.el.removeClass('disabled');
28743     },
28744     
28745     tooltipEl : function()
28746     {
28747         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28748     }
28749 });
28750  
28751
28752  /*
28753  * - LGPL
28754  *
28755  * FieldLabel
28756  * 
28757  */
28758
28759 /**
28760  * @class Roo.bootstrap.FieldLabel
28761  * @extends Roo.bootstrap.Component
28762  * Bootstrap FieldLabel class
28763  * @cfg {String} html contents of the element
28764  * @cfg {String} tag tag of the element default label
28765  * @cfg {String} cls class of the element
28766  * @cfg {String} target label target 
28767  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28768  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28769  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28770  * @cfg {String} iconTooltip default "This field is required"
28771  * 
28772  * @constructor
28773  * Create a new FieldLabel
28774  * @param {Object} config The config object
28775  */
28776
28777 Roo.bootstrap.FieldLabel = function(config){
28778     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28779     
28780     this.addEvents({
28781             /**
28782              * @event invalid
28783              * Fires after the field has been marked as invalid.
28784              * @param {Roo.form.FieldLabel} this
28785              * @param {String} msg The validation message
28786              */
28787             invalid : true,
28788             /**
28789              * @event valid
28790              * Fires after the field has been validated with no errors.
28791              * @param {Roo.form.FieldLabel} this
28792              */
28793             valid : true
28794         });
28795 };
28796
28797 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28798     
28799     tag: 'label',
28800     cls: '',
28801     html: '',
28802     target: '',
28803     allowBlank : true,
28804     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28805     validClass : 'text-success fa fa-lg fa-check',
28806     iconTooltip : 'This field is required',
28807     
28808     getAutoCreate : function(){
28809         
28810         var cfg = {
28811             tag : this.tag,
28812             cls : 'roo-bootstrap-field-label ' + this.cls,
28813             for : this.target,
28814             cn : [
28815                 {
28816                     tag : 'i',
28817                     cls : '',
28818                     tooltip : this.iconTooltip
28819                 },
28820                 {
28821                     tag : 'span',
28822                     html : this.html
28823                 }
28824             ] 
28825         };
28826         
28827         return cfg;
28828     },
28829     
28830     initEvents: function() 
28831     {
28832         Roo.bootstrap.Element.superclass.initEvents.call(this);
28833         
28834         this.iconEl = this.el.select('i', true).first();
28835         
28836         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28837         
28838         Roo.bootstrap.FieldLabel.register(this);
28839     },
28840     
28841     /**
28842      * Mark this field as valid
28843      */
28844     markValid : function()
28845     {
28846         this.iconEl.show();
28847         
28848         this.iconEl.removeClass(this.invalidClass);
28849         
28850         this.iconEl.addClass(this.validClass);
28851         
28852         this.fireEvent('valid', this);
28853     },
28854     
28855     /**
28856      * Mark this field as invalid
28857      * @param {String} msg The validation message
28858      */
28859     markInvalid : function(msg)
28860     {
28861         this.iconEl.show();
28862         
28863         this.iconEl.removeClass(this.validClass);
28864         
28865         this.iconEl.addClass(this.invalidClass);
28866         
28867         this.fireEvent('invalid', this, msg);
28868     }
28869     
28870    
28871 });
28872
28873 Roo.apply(Roo.bootstrap.FieldLabel, {
28874     
28875     groups: {},
28876     
28877      /**
28878     * register a FieldLabel Group
28879     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28880     */
28881     register : function(label)
28882     {
28883         if(this.groups.hasOwnProperty(label.target)){
28884             return;
28885         }
28886      
28887         this.groups[label.target] = label;
28888         
28889     },
28890     /**
28891     * fetch a FieldLabel Group based on the target
28892     * @param {string} target
28893     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28894     */
28895     get: function(target) {
28896         if (typeof(this.groups[target]) == 'undefined') {
28897             return false;
28898         }
28899         
28900         return this.groups[target] ;
28901     }
28902 });
28903
28904  
28905
28906  /*
28907  * - LGPL
28908  *
28909  * page DateSplitField.
28910  * 
28911  */
28912
28913
28914 /**
28915  * @class Roo.bootstrap.DateSplitField
28916  * @extends Roo.bootstrap.Component
28917  * Bootstrap DateSplitField class
28918  * @cfg {string} fieldLabel - the label associated
28919  * @cfg {Number} labelWidth set the width of label (0-12)
28920  * @cfg {String} labelAlign (top|left)
28921  * @cfg {Boolean} dayAllowBlank (true|false) default false
28922  * @cfg {Boolean} monthAllowBlank (true|false) default false
28923  * @cfg {Boolean} yearAllowBlank (true|false) default false
28924  * @cfg {string} dayPlaceholder 
28925  * @cfg {string} monthPlaceholder
28926  * @cfg {string} yearPlaceholder
28927  * @cfg {string} dayFormat default 'd'
28928  * @cfg {string} monthFormat default 'm'
28929  * @cfg {string} yearFormat default 'Y'
28930
28931  *     
28932  * @constructor
28933  * Create a new DateSplitField
28934  * @param {Object} config The config object
28935  */
28936
28937 Roo.bootstrap.DateSplitField = function(config){
28938     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28939     
28940     this.addEvents({
28941         // raw events
28942          /**
28943          * @event years
28944          * getting the data of years
28945          * @param {Roo.bootstrap.DateSplitField} this
28946          * @param {Object} years
28947          */
28948         "years" : true,
28949         /**
28950          * @event days
28951          * getting the data of days
28952          * @param {Roo.bootstrap.DateSplitField} this
28953          * @param {Object} days
28954          */
28955         "days" : true,
28956         /**
28957          * @event invalid
28958          * Fires after the field has been marked as invalid.
28959          * @param {Roo.form.Field} this
28960          * @param {String} msg The validation message
28961          */
28962         invalid : true,
28963        /**
28964          * @event valid
28965          * Fires after the field has been validated with no errors.
28966          * @param {Roo.form.Field} this
28967          */
28968         valid : true
28969     });
28970 };
28971
28972 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28973     
28974     fieldLabel : '',
28975     labelAlign : 'top',
28976     labelWidth : 3,
28977     dayAllowBlank : false,
28978     monthAllowBlank : false,
28979     yearAllowBlank : false,
28980     dayPlaceholder : '',
28981     monthPlaceholder : '',
28982     yearPlaceholder : '',
28983     dayFormat : 'd',
28984     monthFormat : 'm',
28985     yearFormat : 'Y',
28986     isFormField : true,
28987     
28988     getAutoCreate : function()
28989     {
28990         var cfg = {
28991             tag : 'div',
28992             cls : 'row roo-date-split-field-group',
28993             cn : [
28994                 {
28995                     tag : 'input',
28996                     type : 'hidden',
28997                     cls : 'form-hidden-field roo-date-split-field-group-value',
28998                     name : this.name
28999                 }
29000             ]
29001         };
29002         
29003         if(this.fieldLabel){
29004             cfg.cn.push({
29005                 tag : 'div',
29006                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29007                 cn : [
29008                     {
29009                         tag : 'label',
29010                         html : this.fieldLabel
29011                     }
29012                 ]
29013             });
29014         }
29015         
29016         Roo.each(['day', 'month', 'year'], function(t){
29017             cfg.cn.push({
29018                 tag : 'div',
29019                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29020             });
29021         }, this);
29022         
29023         return cfg;
29024     },
29025     
29026     inputEl: function ()
29027     {
29028         return this.el.select('.roo-date-split-field-group-value', true).first();
29029     },
29030     
29031     onRender : function(ct, position) 
29032     {
29033         var _this = this;
29034         
29035         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29036         
29037         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29038         
29039         this.dayField = new Roo.bootstrap.ComboBox({
29040             allowBlank : this.dayAllowBlank,
29041             alwaysQuery : true,
29042             displayField : 'value',
29043             editable : false,
29044             fieldLabel : '',
29045             forceSelection : true,
29046             mode : 'local',
29047             placeholder : this.dayPlaceholder,
29048             selectOnFocus : true,
29049             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29050             triggerAction : 'all',
29051             typeAhead : true,
29052             valueField : 'value',
29053             store : new Roo.data.SimpleStore({
29054                 data : (function() {    
29055                     var days = [];
29056                     _this.fireEvent('days', _this, days);
29057                     return days;
29058                 })(),
29059                 fields : [ 'value' ]
29060             }),
29061             listeners : {
29062                 select : function (_self, record, index)
29063                 {
29064                     _this.setValue(_this.getValue());
29065                 }
29066             }
29067         });
29068
29069         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29070         
29071         this.monthField = new Roo.bootstrap.MonthField({
29072             after : '<i class=\"fa fa-calendar\"></i>',
29073             allowBlank : this.monthAllowBlank,
29074             placeholder : this.monthPlaceholder,
29075             readOnly : true,
29076             listeners : {
29077                 render : function (_self)
29078                 {
29079                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29080                         e.preventDefault();
29081                         _self.focus();
29082                     });
29083                 },
29084                 select : function (_self, oldvalue, newvalue)
29085                 {
29086                     _this.setValue(_this.getValue());
29087                 }
29088             }
29089         });
29090         
29091         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29092         
29093         this.yearField = new Roo.bootstrap.ComboBox({
29094             allowBlank : this.yearAllowBlank,
29095             alwaysQuery : true,
29096             displayField : 'value',
29097             editable : false,
29098             fieldLabel : '',
29099             forceSelection : true,
29100             mode : 'local',
29101             placeholder : this.yearPlaceholder,
29102             selectOnFocus : true,
29103             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29104             triggerAction : 'all',
29105             typeAhead : true,
29106             valueField : 'value',
29107             store : new Roo.data.SimpleStore({
29108                 data : (function() {
29109                     var years = [];
29110                     _this.fireEvent('years', _this, years);
29111                     return years;
29112                 })(),
29113                 fields : [ 'value' ]
29114             }),
29115             listeners : {
29116                 select : function (_self, record, index)
29117                 {
29118                     _this.setValue(_this.getValue());
29119                 }
29120             }
29121         });
29122
29123         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29124     },
29125     
29126     setValue : function(v, format)
29127     {
29128         this.inputEl.dom.value = v;
29129         
29130         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29131         
29132         var d = Date.parseDate(v, f);
29133         
29134         if(!d){
29135             this.validate();
29136             return;
29137         }
29138         
29139         this.setDay(d.format(this.dayFormat));
29140         this.setMonth(d.format(this.monthFormat));
29141         this.setYear(d.format(this.yearFormat));
29142         
29143         this.validate();
29144         
29145         return;
29146     },
29147     
29148     setDay : function(v)
29149     {
29150         this.dayField.setValue(v);
29151         this.inputEl.dom.value = this.getValue();
29152         this.validate();
29153         return;
29154     },
29155     
29156     setMonth : function(v)
29157     {
29158         this.monthField.setValue(v, true);
29159         this.inputEl.dom.value = this.getValue();
29160         this.validate();
29161         return;
29162     },
29163     
29164     setYear : function(v)
29165     {
29166         this.yearField.setValue(v);
29167         this.inputEl.dom.value = this.getValue();
29168         this.validate();
29169         return;
29170     },
29171     
29172     getDay : function()
29173     {
29174         return this.dayField.getValue();
29175     },
29176     
29177     getMonth : function()
29178     {
29179         return this.monthField.getValue();
29180     },
29181     
29182     getYear : function()
29183     {
29184         return this.yearField.getValue();
29185     },
29186     
29187     getValue : function()
29188     {
29189         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29190         
29191         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29192         
29193         return date;
29194     },
29195     
29196     reset : function()
29197     {
29198         this.setDay('');
29199         this.setMonth('');
29200         this.setYear('');
29201         this.inputEl.dom.value = '';
29202         this.validate();
29203         return;
29204     },
29205     
29206     validate : function()
29207     {
29208         var d = this.dayField.validate();
29209         var m = this.monthField.validate();
29210         var y = this.yearField.validate();
29211         
29212         var valid = true;
29213         
29214         if(
29215                 (!this.dayAllowBlank && !d) ||
29216                 (!this.monthAllowBlank && !m) ||
29217                 (!this.yearAllowBlank && !y)
29218         ){
29219             valid = false;
29220         }
29221         
29222         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29223             return valid;
29224         }
29225         
29226         if(valid){
29227             this.markValid();
29228             return valid;
29229         }
29230         
29231         this.markInvalid();
29232         
29233         return valid;
29234     },
29235     
29236     markValid : function()
29237     {
29238         
29239         var label = this.el.select('label', true).first();
29240         var icon = this.el.select('i.fa-star', true).first();
29241
29242         if(label && icon){
29243             icon.remove();
29244         }
29245         
29246         this.fireEvent('valid', this);
29247     },
29248     
29249      /**
29250      * Mark this field as invalid
29251      * @param {String} msg The validation message
29252      */
29253     markInvalid : function(msg)
29254     {
29255         
29256         var label = this.el.select('label', true).first();
29257         var icon = this.el.select('i.fa-star', true).first();
29258
29259         if(label && !icon){
29260             this.el.select('.roo-date-split-field-label', true).createChild({
29261                 tag : 'i',
29262                 cls : 'text-danger fa fa-lg fa-star',
29263                 tooltip : 'This field is required',
29264                 style : 'margin-right:5px;'
29265             }, label, true);
29266         }
29267         
29268         this.fireEvent('invalid', this, msg);
29269     },
29270     
29271     clearInvalid : function()
29272     {
29273         var label = this.el.select('label', true).first();
29274         var icon = this.el.select('i.fa-star', true).first();
29275
29276         if(label && icon){
29277             icon.remove();
29278         }
29279         
29280         this.fireEvent('valid', this);
29281     },
29282     
29283     getName: function()
29284     {
29285         return this.name;
29286     }
29287     
29288 });
29289
29290  /**
29291  *
29292  * This is based on 
29293  * http://masonry.desandro.com
29294  *
29295  * The idea is to render all the bricks based on vertical width...
29296  *
29297  * The original code extends 'outlayer' - we might need to use that....
29298  * 
29299  */
29300
29301
29302 /**
29303  * @class Roo.bootstrap.LayoutMasonry
29304  * @extends Roo.bootstrap.Component
29305  * Bootstrap Layout Masonry class
29306  * 
29307  * @constructor
29308  * Create a new Element
29309  * @param {Object} config The config object
29310  */
29311
29312 Roo.bootstrap.LayoutMasonry = function(config){
29313     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29314     
29315     this.bricks = [];
29316     
29317 };
29318
29319 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29320     
29321     /**
29322      * @cfg {Boolean} isLayoutInstant = no animation?
29323      */   
29324     isLayoutInstant : false, // needed?
29325    
29326     /**
29327      * @cfg {Number} boxWidth  width of the columns
29328      */   
29329     boxWidth : 450,
29330     
29331       /**
29332      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29333      */   
29334     boxHeight : 0,
29335     
29336     /**
29337      * @cfg {Number} padWidth padding below box..
29338      */   
29339     padWidth : 10, 
29340     
29341     /**
29342      * @cfg {Number} gutter gutter width..
29343      */   
29344     gutter : 10,
29345     
29346      /**
29347      * @cfg {Number} maxCols maximum number of columns
29348      */   
29349     
29350     maxCols: 0,
29351     
29352     /**
29353      * @cfg {Boolean} isAutoInitial defalut true
29354      */   
29355     isAutoInitial : true, 
29356     
29357     containerWidth: 0,
29358     
29359     /**
29360      * @cfg {Boolean} isHorizontal defalut false
29361      */   
29362     isHorizontal : false, 
29363
29364     currentSize : null,
29365     
29366     tag: 'div',
29367     
29368     cls: '',
29369     
29370     bricks: null, //CompositeElement
29371     
29372     cols : 1,
29373     
29374     _isLayoutInited : false,
29375     
29376 //    isAlternative : false, // only use for vertical layout...
29377     
29378     /**
29379      * @cfg {Number} alternativePadWidth padding below box..
29380      */   
29381     alternativePadWidth : 50, 
29382     
29383     getAutoCreate : function(){
29384         
29385         var cfg = {
29386             tag: this.tag,
29387             cls: 'blog-masonary-wrapper ' + this.cls,
29388             cn : {
29389                 cls : 'mas-boxes masonary'
29390             }
29391         };
29392         
29393         return cfg;
29394     },
29395     
29396     getChildContainer: function( )
29397     {
29398         if (this.boxesEl) {
29399             return this.boxesEl;
29400         }
29401         
29402         this.boxesEl = this.el.select('.mas-boxes').first();
29403         
29404         return this.boxesEl;
29405     },
29406     
29407     
29408     initEvents : function()
29409     {
29410         var _this = this;
29411         
29412         if(this.isAutoInitial){
29413             Roo.log('hook children rendered');
29414             this.on('childrenrendered', function() {
29415                 Roo.log('children rendered');
29416                 _this.initial();
29417             } ,this);
29418         }
29419     },
29420     
29421     initial : function()
29422     {
29423         this.currentSize = this.el.getBox(true);
29424         
29425         Roo.EventManager.onWindowResize(this.resize, this); 
29426
29427         if(!this.isAutoInitial){
29428             this.layout();
29429             return;
29430         }
29431         
29432         this.layout();
29433         
29434         return;
29435         //this.layout.defer(500,this);
29436         
29437     },
29438     
29439     resize : function()
29440     {
29441         Roo.log('resize');
29442         
29443         var cs = this.el.getBox(true);
29444         
29445         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29446             Roo.log("no change in with or X");
29447             return;
29448         }
29449         
29450         this.currentSize = cs;
29451         
29452         this.layout();
29453         
29454     },
29455     
29456     layout : function()
29457     {   
29458         this._resetLayout();
29459         
29460         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29461         
29462         this.layoutItems( isInstant );
29463       
29464         this._isLayoutInited = true;
29465         
29466     },
29467     
29468     _resetLayout : function()
29469     {
29470         if(this.isHorizontal){
29471             this.horizontalMeasureColumns();
29472             return;
29473         }
29474         
29475         this.verticalMeasureColumns();
29476         
29477     },
29478     
29479     verticalMeasureColumns : function()
29480     {
29481         this.getContainerWidth();
29482         
29483 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29484 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29485 //            return;
29486 //        }
29487         
29488         var boxWidth = this.boxWidth + this.padWidth;
29489         
29490         if(this.containerWidth < this.boxWidth){
29491             boxWidth = this.containerWidth
29492         }
29493         
29494         var containerWidth = this.containerWidth;
29495         
29496         var cols = Math.floor(containerWidth / boxWidth);
29497         
29498         this.cols = Math.max( cols, 1 );
29499         
29500         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29501         
29502         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29503         
29504         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29505         
29506         this.colWidth = boxWidth + avail - this.padWidth;
29507         
29508         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29509         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29510     },
29511     
29512     horizontalMeasureColumns : function()
29513     {
29514         this.getContainerWidth();
29515         
29516         var boxWidth = this.boxWidth;
29517         
29518         if(this.containerWidth < boxWidth){
29519             boxWidth = this.containerWidth;
29520         }
29521         
29522         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29523         
29524         this.el.setHeight(boxWidth);
29525         
29526     },
29527     
29528     getContainerWidth : function()
29529     {
29530         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29531     },
29532     
29533     layoutItems : function( isInstant )
29534     {
29535         var items = Roo.apply([], this.bricks);
29536         
29537         if(this.isHorizontal){
29538             this._horizontalLayoutItems( items , isInstant );
29539             return;
29540         }
29541         
29542 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29543 //            this._verticalAlternativeLayoutItems( items , isInstant );
29544 //            return;
29545 //        }
29546         
29547         this._verticalLayoutItems( items , isInstant );
29548         
29549     },
29550     
29551     _verticalLayoutItems : function ( items , isInstant)
29552     {
29553         if ( !items || !items.length ) {
29554             return;
29555         }
29556         
29557         var standard = [
29558             ['xs', 'xs', 'xs', 'tall'],
29559             ['xs', 'xs', 'tall'],
29560             ['xs', 'xs', 'sm'],
29561             ['xs', 'xs', 'xs'],
29562             ['xs', 'tall'],
29563             ['xs', 'sm'],
29564             ['xs', 'xs'],
29565             ['xs'],
29566             
29567             ['sm', 'xs', 'xs'],
29568             ['sm', 'xs'],
29569             ['sm'],
29570             
29571             ['tall', 'xs', 'xs', 'xs'],
29572             ['tall', 'xs', 'xs'],
29573             ['tall', 'xs'],
29574             ['tall']
29575             
29576         ];
29577         
29578         var queue = [];
29579         
29580         var boxes = [];
29581         
29582         var box = [];
29583         
29584         Roo.each(items, function(item, k){
29585             
29586             switch (item.size) {
29587                 // these layouts take up a full box,
29588                 case 'md' :
29589                 case 'md-left' :
29590                 case 'md-right' :
29591                 case 'wide' :
29592                     
29593                     if(box.length){
29594                         boxes.push(box);
29595                         box = [];
29596                     }
29597                     
29598                     boxes.push([item]);
29599                     
29600                     break;
29601                     
29602                 case 'xs' :
29603                 case 'sm' :
29604                 case 'tall' :
29605                     
29606                     box.push(item);
29607                     
29608                     break;
29609                 default :
29610                     break;
29611                     
29612             }
29613             
29614         }, this);
29615         
29616         if(box.length){
29617             boxes.push(box);
29618             box = [];
29619         }
29620         
29621         var filterPattern = function(box, length)
29622         {
29623             if(!box.length){
29624                 return;
29625             }
29626             
29627             var match = false;
29628             
29629             var pattern = box.slice(0, length);
29630             
29631             var format = [];
29632             
29633             Roo.each(pattern, function(i){
29634                 format.push(i.size);
29635             }, this);
29636             
29637             Roo.each(standard, function(s){
29638                 
29639                 if(String(s) != String(format)){
29640                     return;
29641                 }
29642                 
29643                 match = true;
29644                 return false;
29645                 
29646             }, this);
29647             
29648             if(!match && length == 1){
29649                 return;
29650             }
29651             
29652             if(!match){
29653                 filterPattern(box, length - 1);
29654                 return;
29655             }
29656                 
29657             queue.push(pattern);
29658
29659             box = box.slice(length, box.length);
29660
29661             filterPattern(box, 4);
29662
29663             return;
29664             
29665         }
29666         
29667         Roo.each(boxes, function(box, k){
29668             
29669             if(!box.length){
29670                 return;
29671             }
29672             
29673             if(box.length == 1){
29674                 queue.push(box);
29675                 return;
29676             }
29677             
29678             filterPattern(box, 4);
29679             
29680         }, this);
29681         
29682         this._processVerticalLayoutQueue( queue, isInstant );
29683         
29684     },
29685     
29686 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29687 //    {
29688 //        if ( !items || !items.length ) {
29689 //            return;
29690 //        }
29691 //
29692 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29693 //        
29694 //    },
29695     
29696     _horizontalLayoutItems : function ( items , isInstant)
29697     {
29698         if ( !items || !items.length || items.length < 3) {
29699             return;
29700         }
29701         
29702         items.reverse();
29703         
29704         var eItems = items.slice(0, 3);
29705         
29706         items = items.slice(3, items.length);
29707         
29708         var standard = [
29709             ['xs', 'xs', 'xs', 'wide'],
29710             ['xs', 'xs', 'wide'],
29711             ['xs', 'xs', 'sm'],
29712             ['xs', 'xs', 'xs'],
29713             ['xs', 'wide'],
29714             ['xs', 'sm'],
29715             ['xs', 'xs'],
29716             ['xs'],
29717             
29718             ['sm', 'xs', 'xs'],
29719             ['sm', 'xs'],
29720             ['sm'],
29721             
29722             ['wide', 'xs', 'xs', 'xs'],
29723             ['wide', 'xs', 'xs'],
29724             ['wide', 'xs'],
29725             ['wide'],
29726             
29727             ['wide-thin']
29728         ];
29729         
29730         var queue = [];
29731         
29732         var boxes = [];
29733         
29734         var box = [];
29735         
29736         Roo.each(items, function(item, k){
29737             
29738             switch (item.size) {
29739                 case 'md' :
29740                 case 'md-left' :
29741                 case 'md-right' :
29742                 case 'tall' :
29743                     
29744                     if(box.length){
29745                         boxes.push(box);
29746                         box = [];
29747                     }
29748                     
29749                     boxes.push([item]);
29750                     
29751                     break;
29752                     
29753                 case 'xs' :
29754                 case 'sm' :
29755                 case 'wide' :
29756                 case 'wide-thin' :
29757                     
29758                     box.push(item);
29759                     
29760                     break;
29761                 default :
29762                     break;
29763                     
29764             }
29765             
29766         }, this);
29767         
29768         if(box.length){
29769             boxes.push(box);
29770             box = [];
29771         }
29772         
29773         var filterPattern = function(box, length)
29774         {
29775             if(!box.length){
29776                 return;
29777             }
29778             
29779             var match = false;
29780             
29781             var pattern = box.slice(0, length);
29782             
29783             var format = [];
29784             
29785             Roo.each(pattern, function(i){
29786                 format.push(i.size);
29787             }, this);
29788             
29789             Roo.each(standard, function(s){
29790                 
29791                 if(String(s) != String(format)){
29792                     return;
29793                 }
29794                 
29795                 match = true;
29796                 return false;
29797                 
29798             }, this);
29799             
29800             if(!match && length == 1){
29801                 return;
29802             }
29803             
29804             if(!match){
29805                 filterPattern(box, length - 1);
29806                 return;
29807             }
29808                 
29809             queue.push(pattern);
29810
29811             box = box.slice(length, box.length);
29812
29813             filterPattern(box, 4);
29814
29815             return;
29816             
29817         }
29818         
29819         Roo.each(boxes, function(box, k){
29820             
29821             if(!box.length){
29822                 return;
29823             }
29824             
29825             if(box.length == 1){
29826                 queue.push(box);
29827                 return;
29828             }
29829             
29830             filterPattern(box, 4);
29831             
29832         }, this);
29833         
29834         
29835         var prune = [];
29836         
29837         var pos = this.el.getBox(true);
29838         
29839         var minX = pos.x;
29840         
29841         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29842         
29843         var hit_end = false;
29844         
29845         Roo.each(queue, function(box){
29846             
29847             if(hit_end){
29848                 
29849                 Roo.each(box, function(b){
29850                 
29851                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29852                     b.el.hide();
29853
29854                 }, this);
29855
29856                 return;
29857             }
29858             
29859             var mx = 0;
29860             
29861             Roo.each(box, function(b){
29862                 
29863                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29864                 b.el.show();
29865
29866                 mx = Math.max(mx, b.x);
29867                 
29868             }, this);
29869             
29870             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29871             
29872             if(maxX < minX){
29873                 
29874                 Roo.each(box, function(b){
29875                 
29876                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29877                     b.el.hide();
29878                     
29879                 }, this);
29880                 
29881                 hit_end = true;
29882                 
29883                 return;
29884             }
29885             
29886             prune.push(box);
29887             
29888         }, this);
29889         
29890         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29891     },
29892     
29893     /** Sets position of item in DOM
29894     * @param {Element} item
29895     * @param {Number} x - horizontal position
29896     * @param {Number} y - vertical position
29897     * @param {Boolean} isInstant - disables transitions
29898     */
29899     _processVerticalLayoutQueue : function( queue, isInstant )
29900     {
29901         var pos = this.el.getBox(true);
29902         var x = pos.x;
29903         var y = pos.y;
29904         var maxY = [];
29905         
29906         for (var i = 0; i < this.cols; i++){
29907             maxY[i] = pos.y;
29908         }
29909         
29910         Roo.each(queue, function(box, k){
29911             
29912             var col = k % this.cols;
29913             
29914             Roo.each(box, function(b,kk){
29915                 
29916                 b.el.position('absolute');
29917                 
29918                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29919                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29920                 
29921                 if(b.size == 'md-left' || b.size == 'md-right'){
29922                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29923                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29924                 }
29925                 
29926                 b.el.setWidth(width);
29927                 b.el.setHeight(height);
29928                 // iframe?
29929                 b.el.select('iframe',true).setSize(width,height);
29930                 
29931             }, this);
29932             
29933             for (var i = 0; i < this.cols; i++){
29934                 
29935                 if(maxY[i] < maxY[col]){
29936                     col = i;
29937                     continue;
29938                 }
29939                 
29940                 col = Math.min(col, i);
29941                 
29942             }
29943             
29944             x = pos.x + col * (this.colWidth + this.padWidth);
29945             
29946             y = maxY[col];
29947             
29948             var positions = [];
29949             
29950             switch (box.length){
29951                 case 1 :
29952                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29953                     break;
29954                 case 2 :
29955                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29956                     break;
29957                 case 3 :
29958                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29959                     break;
29960                 case 4 :
29961                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29962                     break;
29963                 default :
29964                     break;
29965             }
29966             
29967             Roo.each(box, function(b,kk){
29968                 
29969                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29970                 
29971                 var sz = b.el.getSize();
29972                 
29973                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29974                 
29975             }, this);
29976             
29977         }, this);
29978         
29979         var mY = 0;
29980         
29981         for (var i = 0; i < this.cols; i++){
29982             mY = Math.max(mY, maxY[i]);
29983         }
29984         
29985         this.el.setHeight(mY - pos.y);
29986         
29987     },
29988     
29989 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29990 //    {
29991 //        var pos = this.el.getBox(true);
29992 //        var x = pos.x;
29993 //        var y = pos.y;
29994 //        var maxX = pos.right;
29995 //        
29996 //        var maxHeight = 0;
29997 //        
29998 //        Roo.each(items, function(item, k){
29999 //            
30000 //            var c = k % 2;
30001 //            
30002 //            item.el.position('absolute');
30003 //                
30004 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30005 //
30006 //            item.el.setWidth(width);
30007 //
30008 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30009 //
30010 //            item.el.setHeight(height);
30011 //            
30012 //            if(c == 0){
30013 //                item.el.setXY([x, y], isInstant ? false : true);
30014 //            } else {
30015 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30016 //            }
30017 //            
30018 //            y = y + height + this.alternativePadWidth;
30019 //            
30020 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30021 //            
30022 //        }, this);
30023 //        
30024 //        this.el.setHeight(maxHeight);
30025 //        
30026 //    },
30027     
30028     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30029     {
30030         var pos = this.el.getBox(true);
30031         
30032         var minX = pos.x;
30033         var minY = pos.y;
30034         
30035         var maxX = pos.right;
30036         
30037         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30038         
30039         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30040         
30041         Roo.each(queue, function(box, k){
30042             
30043             Roo.each(box, function(b, kk){
30044                 
30045                 b.el.position('absolute');
30046                 
30047                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30048                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30049                 
30050                 if(b.size == 'md-left' || b.size == 'md-right'){
30051                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30052                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30053                 }
30054                 
30055                 b.el.setWidth(width);
30056                 b.el.setHeight(height);
30057                 
30058             }, this);
30059             
30060             if(!box.length){
30061                 return;
30062             }
30063             
30064             var positions = [];
30065             
30066             switch (box.length){
30067                 case 1 :
30068                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30069                     break;
30070                 case 2 :
30071                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30072                     break;
30073                 case 3 :
30074                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30075                     break;
30076                 case 4 :
30077                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30078                     break;
30079                 default :
30080                     break;
30081             }
30082             
30083             Roo.each(box, function(b,kk){
30084                 
30085                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30086                 
30087                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30088                 
30089             }, this);
30090             
30091         }, this);
30092         
30093     },
30094     
30095     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30096     {
30097         Roo.each(eItems, function(b,k){
30098             
30099             b.size = (k == 0) ? 'sm' : 'xs';
30100             b.x = (k == 0) ? 2 : 1;
30101             b.y = (k == 0) ? 2 : 1;
30102             
30103             b.el.position('absolute');
30104             
30105             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30106                 
30107             b.el.setWidth(width);
30108             
30109             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30110             
30111             b.el.setHeight(height);
30112             
30113         }, this);
30114
30115         var positions = [];
30116         
30117         positions.push({
30118             x : maxX - this.unitWidth * 2 - this.gutter,
30119             y : minY
30120         });
30121         
30122         positions.push({
30123             x : maxX - this.unitWidth,
30124             y : minY + (this.unitWidth + this.gutter) * 2
30125         });
30126         
30127         positions.push({
30128             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30129             y : minY
30130         });
30131         
30132         Roo.each(eItems, function(b,k){
30133             
30134             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30135
30136         }, this);
30137         
30138     },
30139     
30140     getVerticalOneBoxColPositions : function(x, y, box)
30141     {
30142         var pos = [];
30143         
30144         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30145         
30146         if(box[0].size == 'md-left'){
30147             rand = 0;
30148         }
30149         
30150         if(box[0].size == 'md-right'){
30151             rand = 1;
30152         }
30153         
30154         pos.push({
30155             x : x + (this.unitWidth + this.gutter) * rand,
30156             y : y
30157         });
30158         
30159         return pos;
30160     },
30161     
30162     getVerticalTwoBoxColPositions : function(x, y, box)
30163     {
30164         var pos = [];
30165         
30166         if(box[0].size == 'xs'){
30167             
30168             pos.push({
30169                 x : x,
30170                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30171             });
30172
30173             pos.push({
30174                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30175                 y : y
30176             });
30177             
30178             return pos;
30179             
30180         }
30181         
30182         pos.push({
30183             x : x,
30184             y : y
30185         });
30186
30187         pos.push({
30188             x : x + (this.unitWidth + this.gutter) * 2,
30189             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30190         });
30191         
30192         return pos;
30193         
30194     },
30195     
30196     getVerticalThreeBoxColPositions : function(x, y, box)
30197     {
30198         var pos = [];
30199         
30200         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30201             
30202             pos.push({
30203                 x : x,
30204                 y : y
30205             });
30206
30207             pos.push({
30208                 x : x + (this.unitWidth + this.gutter) * 1,
30209                 y : y
30210             });
30211             
30212             pos.push({
30213                 x : x + (this.unitWidth + this.gutter) * 2,
30214                 y : y
30215             });
30216             
30217             return pos;
30218             
30219         }
30220         
30221         if(box[0].size == 'xs' && box[1].size == 'xs'){
30222             
30223             pos.push({
30224                 x : x,
30225                 y : y
30226             });
30227
30228             pos.push({
30229                 x : x,
30230                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30231             });
30232             
30233             pos.push({
30234                 x : x + (this.unitWidth + this.gutter) * 1,
30235                 y : y
30236             });
30237             
30238             return pos;
30239             
30240         }
30241         
30242         pos.push({
30243             x : x,
30244             y : y
30245         });
30246
30247         pos.push({
30248             x : x + (this.unitWidth + this.gutter) * 2,
30249             y : y
30250         });
30251
30252         pos.push({
30253             x : x + (this.unitWidth + this.gutter) * 2,
30254             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30255         });
30256             
30257         return pos;
30258         
30259     },
30260     
30261     getVerticalFourBoxColPositions : function(x, y, box)
30262     {
30263         var pos = [];
30264         
30265         if(box[0].size == 'xs'){
30266             
30267             pos.push({
30268                 x : x,
30269                 y : y
30270             });
30271
30272             pos.push({
30273                 x : x,
30274                 y : y + (this.unitHeight + this.gutter) * 1
30275             });
30276             
30277             pos.push({
30278                 x : x,
30279                 y : y + (this.unitHeight + this.gutter) * 2
30280             });
30281             
30282             pos.push({
30283                 x : x + (this.unitWidth + this.gutter) * 1,
30284                 y : y
30285             });
30286             
30287             return pos;
30288             
30289         }
30290         
30291         pos.push({
30292             x : x,
30293             y : y
30294         });
30295
30296         pos.push({
30297             x : x + (this.unitWidth + this.gutter) * 2,
30298             y : y
30299         });
30300
30301         pos.push({
30302             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30303             y : y + (this.unitHeight + this.gutter) * 1
30304         });
30305
30306         pos.push({
30307             x : x + (this.unitWidth + this.gutter) * 2,
30308             y : y + (this.unitWidth + this.gutter) * 2
30309         });
30310
30311         return pos;
30312         
30313     },
30314     
30315     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30316     {
30317         var pos = [];
30318         
30319         if(box[0].size == 'md-left'){
30320             pos.push({
30321                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30322                 y : minY
30323             });
30324             
30325             return pos;
30326         }
30327         
30328         if(box[0].size == 'md-right'){
30329             pos.push({
30330                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30331                 y : minY + (this.unitWidth + this.gutter) * 1
30332             });
30333             
30334             return pos;
30335         }
30336         
30337         var rand = Math.floor(Math.random() * (4 - box[0].y));
30338         
30339         pos.push({
30340             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30341             y : minY + (this.unitWidth + this.gutter) * rand
30342         });
30343         
30344         return pos;
30345         
30346     },
30347     
30348     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30349     {
30350         var pos = [];
30351         
30352         if(box[0].size == 'xs'){
30353             
30354             pos.push({
30355                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30356                 y : minY
30357             });
30358
30359             pos.push({
30360                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30361                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30362             });
30363             
30364             return pos;
30365             
30366         }
30367         
30368         pos.push({
30369             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30370             y : minY
30371         });
30372
30373         pos.push({
30374             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30375             y : minY + (this.unitWidth + this.gutter) * 2
30376         });
30377         
30378         return pos;
30379         
30380     },
30381     
30382     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30383     {
30384         var pos = [];
30385         
30386         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30387             
30388             pos.push({
30389                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30390                 y : minY
30391             });
30392
30393             pos.push({
30394                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30395                 y : minY + (this.unitWidth + this.gutter) * 1
30396             });
30397             
30398             pos.push({
30399                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30400                 y : minY + (this.unitWidth + this.gutter) * 2
30401             });
30402             
30403             return pos;
30404             
30405         }
30406         
30407         if(box[0].size == 'xs' && box[1].size == 'xs'){
30408             
30409             pos.push({
30410                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30411                 y : minY
30412             });
30413
30414             pos.push({
30415                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30416                 y : minY
30417             });
30418             
30419             pos.push({
30420                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30421                 y : minY + (this.unitWidth + this.gutter) * 1
30422             });
30423             
30424             return pos;
30425             
30426         }
30427         
30428         pos.push({
30429             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30430             y : minY
30431         });
30432
30433         pos.push({
30434             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30435             y : minY + (this.unitWidth + this.gutter) * 2
30436         });
30437
30438         pos.push({
30439             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30440             y : minY + (this.unitWidth + this.gutter) * 2
30441         });
30442             
30443         return pos;
30444         
30445     },
30446     
30447     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30448     {
30449         var pos = [];
30450         
30451         if(box[0].size == 'xs'){
30452             
30453             pos.push({
30454                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30455                 y : minY
30456             });
30457
30458             pos.push({
30459                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30460                 y : minY
30461             });
30462             
30463             pos.push({
30464                 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),
30465                 y : minY
30466             });
30467             
30468             pos.push({
30469                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30470                 y : minY + (this.unitWidth + this.gutter) * 1
30471             });
30472             
30473             return pos;
30474             
30475         }
30476         
30477         pos.push({
30478             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30479             y : minY
30480         });
30481         
30482         pos.push({
30483             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30484             y : minY + (this.unitWidth + this.gutter) * 2
30485         });
30486         
30487         pos.push({
30488             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30489             y : minY + (this.unitWidth + this.gutter) * 2
30490         });
30491         
30492         pos.push({
30493             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),
30494             y : minY + (this.unitWidth + this.gutter) * 2
30495         });
30496
30497         return pos;
30498         
30499     }
30500     
30501 });
30502
30503  
30504
30505  /**
30506  *
30507  * This is based on 
30508  * http://masonry.desandro.com
30509  *
30510  * The idea is to render all the bricks based on vertical width...
30511  *
30512  * The original code extends 'outlayer' - we might need to use that....
30513  * 
30514  */
30515
30516
30517 /**
30518  * @class Roo.bootstrap.LayoutMasonryAuto
30519  * @extends Roo.bootstrap.Component
30520  * Bootstrap Layout Masonry class
30521  * 
30522  * @constructor
30523  * Create a new Element
30524  * @param {Object} config The config object
30525  */
30526
30527 Roo.bootstrap.LayoutMasonryAuto = function(config){
30528     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30529 };
30530
30531 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30532     
30533       /**
30534      * @cfg {Boolean} isFitWidth  - resize the width..
30535      */   
30536     isFitWidth : false,  // options..
30537     /**
30538      * @cfg {Boolean} isOriginLeft = left align?
30539      */   
30540     isOriginLeft : true,
30541     /**
30542      * @cfg {Boolean} isOriginTop = top align?
30543      */   
30544     isOriginTop : false,
30545     /**
30546      * @cfg {Boolean} isLayoutInstant = no animation?
30547      */   
30548     isLayoutInstant : false, // needed?
30549     /**
30550      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30551      */   
30552     isResizingContainer : true,
30553     /**
30554      * @cfg {Number} columnWidth  width of the columns 
30555      */   
30556     
30557     columnWidth : 0,
30558     
30559     /**
30560      * @cfg {Number} maxCols maximum number of columns
30561      */   
30562     
30563     maxCols: 0,
30564     /**
30565      * @cfg {Number} padHeight padding below box..
30566      */   
30567     
30568     padHeight : 10, 
30569     
30570     /**
30571      * @cfg {Boolean} isAutoInitial defalut true
30572      */   
30573     
30574     isAutoInitial : true, 
30575     
30576     // private?
30577     gutter : 0,
30578     
30579     containerWidth: 0,
30580     initialColumnWidth : 0,
30581     currentSize : null,
30582     
30583     colYs : null, // array.
30584     maxY : 0,
30585     padWidth: 10,
30586     
30587     
30588     tag: 'div',
30589     cls: '',
30590     bricks: null, //CompositeElement
30591     cols : 0, // array?
30592     // element : null, // wrapped now this.el
30593     _isLayoutInited : null, 
30594     
30595     
30596     getAutoCreate : function(){
30597         
30598         var cfg = {
30599             tag: this.tag,
30600             cls: 'blog-masonary-wrapper ' + this.cls,
30601             cn : {
30602                 cls : 'mas-boxes masonary'
30603             }
30604         };
30605         
30606         return cfg;
30607     },
30608     
30609     getChildContainer: function( )
30610     {
30611         if (this.boxesEl) {
30612             return this.boxesEl;
30613         }
30614         
30615         this.boxesEl = this.el.select('.mas-boxes').first();
30616         
30617         return this.boxesEl;
30618     },
30619     
30620     
30621     initEvents : function()
30622     {
30623         var _this = this;
30624         
30625         if(this.isAutoInitial){
30626             Roo.log('hook children rendered');
30627             this.on('childrenrendered', function() {
30628                 Roo.log('children rendered');
30629                 _this.initial();
30630             } ,this);
30631         }
30632         
30633     },
30634     
30635     initial : function()
30636     {
30637         this.reloadItems();
30638
30639         this.currentSize = this.el.getBox(true);
30640
30641         /// was window resize... - let's see if this works..
30642         Roo.EventManager.onWindowResize(this.resize, this); 
30643
30644         if(!this.isAutoInitial){
30645             this.layout();
30646             return;
30647         }
30648         
30649         this.layout.defer(500,this);
30650     },
30651     
30652     reloadItems: function()
30653     {
30654         this.bricks = this.el.select('.masonry-brick', true);
30655         
30656         this.bricks.each(function(b) {
30657             //Roo.log(b.getSize());
30658             if (!b.attr('originalwidth')) {
30659                 b.attr('originalwidth',  b.getSize().width);
30660             }
30661             
30662         });
30663         
30664         Roo.log(this.bricks.elements.length);
30665     },
30666     
30667     resize : function()
30668     {
30669         Roo.log('resize');
30670         var cs = this.el.getBox(true);
30671         
30672         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30673             Roo.log("no change in with or X");
30674             return;
30675         }
30676         this.currentSize = cs;
30677         this.layout();
30678     },
30679     
30680     layout : function()
30681     {
30682          Roo.log('layout');
30683         this._resetLayout();
30684         //this._manageStamps();
30685       
30686         // don't animate first layout
30687         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30688         this.layoutItems( isInstant );
30689       
30690         // flag for initalized
30691         this._isLayoutInited = true;
30692     },
30693     
30694     layoutItems : function( isInstant )
30695     {
30696         //var items = this._getItemsForLayout( this.items );
30697         // original code supports filtering layout items.. we just ignore it..
30698         
30699         this._layoutItems( this.bricks , isInstant );
30700       
30701         this._postLayout();
30702     },
30703     _layoutItems : function ( items , isInstant)
30704     {
30705        //this.fireEvent( 'layout', this, items );
30706     
30707
30708         if ( !items || !items.elements.length ) {
30709           // no items, emit event with empty array
30710             return;
30711         }
30712
30713         var queue = [];
30714         items.each(function(item) {
30715             Roo.log("layout item");
30716             Roo.log(item);
30717             // get x/y object from method
30718             var position = this._getItemLayoutPosition( item );
30719             // enqueue
30720             position.item = item;
30721             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30722             queue.push( position );
30723         }, this);
30724       
30725         this._processLayoutQueue( queue );
30726     },
30727     /** Sets position of item in DOM
30728     * @param {Element} item
30729     * @param {Number} x - horizontal position
30730     * @param {Number} y - vertical position
30731     * @param {Boolean} isInstant - disables transitions
30732     */
30733     _processLayoutQueue : function( queue )
30734     {
30735         for ( var i=0, len = queue.length; i < len; i++ ) {
30736             var obj = queue[i];
30737             obj.item.position('absolute');
30738             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30739         }
30740     },
30741       
30742     
30743     /**
30744     * Any logic you want to do after each layout,
30745     * i.e. size the container
30746     */
30747     _postLayout : function()
30748     {
30749         this.resizeContainer();
30750     },
30751     
30752     resizeContainer : function()
30753     {
30754         if ( !this.isResizingContainer ) {
30755             return;
30756         }
30757         var size = this._getContainerSize();
30758         if ( size ) {
30759             this.el.setSize(size.width,size.height);
30760             this.boxesEl.setSize(size.width,size.height);
30761         }
30762     },
30763     
30764     
30765     
30766     _resetLayout : function()
30767     {
30768         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30769         this.colWidth = this.el.getWidth();
30770         //this.gutter = this.el.getWidth(); 
30771         
30772         this.measureColumns();
30773
30774         // reset column Y
30775         var i = this.cols;
30776         this.colYs = [];
30777         while (i--) {
30778             this.colYs.push( 0 );
30779         }
30780     
30781         this.maxY = 0;
30782     },
30783
30784     measureColumns : function()
30785     {
30786         this.getContainerWidth();
30787       // if columnWidth is 0, default to outerWidth of first item
30788         if ( !this.columnWidth ) {
30789             var firstItem = this.bricks.first();
30790             Roo.log(firstItem);
30791             this.columnWidth  = this.containerWidth;
30792             if (firstItem && firstItem.attr('originalwidth') ) {
30793                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30794             }
30795             // columnWidth fall back to item of first element
30796             Roo.log("set column width?");
30797                         this.initialColumnWidth = this.columnWidth  ;
30798
30799             // if first elem has no width, default to size of container
30800             
30801         }
30802         
30803         
30804         if (this.initialColumnWidth) {
30805             this.columnWidth = this.initialColumnWidth;
30806         }
30807         
30808         
30809             
30810         // column width is fixed at the top - however if container width get's smaller we should
30811         // reduce it...
30812         
30813         // this bit calcs how man columns..
30814             
30815         var columnWidth = this.columnWidth += this.gutter;
30816       
30817         // calculate columns
30818         var containerWidth = this.containerWidth + this.gutter;
30819         
30820         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30821         // fix rounding errors, typically with gutters
30822         var excess = columnWidth - containerWidth % columnWidth;
30823         
30824         
30825         // if overshoot is less than a pixel, round up, otherwise floor it
30826         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30827         cols = Math[ mathMethod ]( cols );
30828         this.cols = Math.max( cols, 1 );
30829         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30830         
30831          // padding positioning..
30832         var totalColWidth = this.cols * this.columnWidth;
30833         var padavail = this.containerWidth - totalColWidth;
30834         // so for 2 columns - we need 3 'pads'
30835         
30836         var padNeeded = (1+this.cols) * this.padWidth;
30837         
30838         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30839         
30840         this.columnWidth += padExtra
30841         //this.padWidth = Math.floor(padavail /  ( this.cols));
30842         
30843         // adjust colum width so that padding is fixed??
30844         
30845         // we have 3 columns ... total = width * 3
30846         // we have X left over... that should be used by 
30847         
30848         //if (this.expandC) {
30849             
30850         //}
30851         
30852         
30853         
30854     },
30855     
30856     getContainerWidth : function()
30857     {
30858        /* // container is parent if fit width
30859         var container = this.isFitWidth ? this.element.parentNode : this.element;
30860         // check that this.size and size are there
30861         // IE8 triggers resize on body size change, so they might not be
30862         
30863         var size = getSize( container );  //FIXME
30864         this.containerWidth = size && size.innerWidth; //FIXME
30865         */
30866          
30867         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30868         
30869     },
30870     
30871     _getItemLayoutPosition : function( item )  // what is item?
30872     {
30873         // we resize the item to our columnWidth..
30874       
30875         item.setWidth(this.columnWidth);
30876         item.autoBoxAdjust  = false;
30877         
30878         var sz = item.getSize();
30879  
30880         // how many columns does this brick span
30881         var remainder = this.containerWidth % this.columnWidth;
30882         
30883         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30884         // round if off by 1 pixel, otherwise use ceil
30885         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30886         colSpan = Math.min( colSpan, this.cols );
30887         
30888         // normally this should be '1' as we dont' currently allow multi width columns..
30889         
30890         var colGroup = this._getColGroup( colSpan );
30891         // get the minimum Y value from the columns
30892         var minimumY = Math.min.apply( Math, colGroup );
30893         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30894         
30895         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30896          
30897         // position the brick
30898         var position = {
30899             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30900             y: this.currentSize.y + minimumY + this.padHeight
30901         };
30902         
30903         Roo.log(position);
30904         // apply setHeight to necessary columns
30905         var setHeight = minimumY + sz.height + this.padHeight;
30906         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30907         
30908         var setSpan = this.cols + 1 - colGroup.length;
30909         for ( var i = 0; i < setSpan; i++ ) {
30910           this.colYs[ shortColIndex + i ] = setHeight ;
30911         }
30912       
30913         return position;
30914     },
30915     
30916     /**
30917      * @param {Number} colSpan - number of columns the element spans
30918      * @returns {Array} colGroup
30919      */
30920     _getColGroup : function( colSpan )
30921     {
30922         if ( colSpan < 2 ) {
30923           // if brick spans only one column, use all the column Ys
30924           return this.colYs;
30925         }
30926       
30927         var colGroup = [];
30928         // how many different places could this brick fit horizontally
30929         var groupCount = this.cols + 1 - colSpan;
30930         // for each group potential horizontal position
30931         for ( var i = 0; i < groupCount; i++ ) {
30932           // make an array of colY values for that one group
30933           var groupColYs = this.colYs.slice( i, i + colSpan );
30934           // and get the max value of the array
30935           colGroup[i] = Math.max.apply( Math, groupColYs );
30936         }
30937         return colGroup;
30938     },
30939     /*
30940     _manageStamp : function( stamp )
30941     {
30942         var stampSize =  stamp.getSize();
30943         var offset = stamp.getBox();
30944         // get the columns that this stamp affects
30945         var firstX = this.isOriginLeft ? offset.x : offset.right;
30946         var lastX = firstX + stampSize.width;
30947         var firstCol = Math.floor( firstX / this.columnWidth );
30948         firstCol = Math.max( 0, firstCol );
30949         
30950         var lastCol = Math.floor( lastX / this.columnWidth );
30951         // lastCol should not go over if multiple of columnWidth #425
30952         lastCol -= lastX % this.columnWidth ? 0 : 1;
30953         lastCol = Math.min( this.cols - 1, lastCol );
30954         
30955         // set colYs to bottom of the stamp
30956         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30957             stampSize.height;
30958             
30959         for ( var i = firstCol; i <= lastCol; i++ ) {
30960           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30961         }
30962     },
30963     */
30964     
30965     _getContainerSize : function()
30966     {
30967         this.maxY = Math.max.apply( Math, this.colYs );
30968         var size = {
30969             height: this.maxY
30970         };
30971       
30972         if ( this.isFitWidth ) {
30973             size.width = this._getContainerFitWidth();
30974         }
30975       
30976         return size;
30977     },
30978     
30979     _getContainerFitWidth : function()
30980     {
30981         var unusedCols = 0;
30982         // count unused columns
30983         var i = this.cols;
30984         while ( --i ) {
30985           if ( this.colYs[i] !== 0 ) {
30986             break;
30987           }
30988           unusedCols++;
30989         }
30990         // fit container to columns that have been used
30991         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30992     },
30993     
30994     needsResizeLayout : function()
30995     {
30996         var previousWidth = this.containerWidth;
30997         this.getContainerWidth();
30998         return previousWidth !== this.containerWidth;
30999     }
31000  
31001 });
31002
31003  
31004
31005  /*
31006  * - LGPL
31007  *
31008  * element
31009  * 
31010  */
31011
31012 /**
31013  * @class Roo.bootstrap.MasonryBrick
31014  * @extends Roo.bootstrap.Component
31015  * Bootstrap MasonryBrick class
31016  * 
31017  * @constructor
31018  * Create a new MasonryBrick
31019  * @param {Object} config The config object
31020  */
31021
31022 Roo.bootstrap.MasonryBrick = function(config){
31023     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31024     
31025     this.addEvents({
31026         // raw events
31027         /**
31028          * @event click
31029          * When a MasonryBrick is clcik
31030          * @param {Roo.bootstrap.MasonryBrick} this
31031          * @param {Roo.EventObject} e
31032          */
31033         "click" : true
31034     });
31035 };
31036
31037 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31038     
31039     /**
31040      * @cfg {String} title
31041      */   
31042     title : '',
31043     /**
31044      * @cfg {String} html
31045      */   
31046     html : '',
31047     /**
31048      * @cfg {String} bgimage
31049      */   
31050     bgimage : '',
31051     /**
31052      * @cfg {String} videourl
31053      */   
31054     videourl : '',
31055     /**
31056      * @cfg {String} cls
31057      */   
31058     cls : '',
31059     /**
31060      * @cfg {String} href
31061      */   
31062     href : '',
31063     /**
31064      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31065      */   
31066     size : 'xs',
31067     
31068     /**
31069      * @cfg {String} (center|bottom) placetitle
31070      */   
31071     placetitle : '',
31072     
31073     /**
31074      * @cfg {Boolean} isFitContainer defalut true
31075      */   
31076     isFitContainer : true, 
31077     
31078     /**
31079      * @cfg {Boolean} preventDefault defalut false
31080      */   
31081     preventDefault : false, 
31082     
31083     getAutoCreate : function()
31084     {
31085         if(!this.isFitContainer){
31086             return this.getSplitAutoCreate();
31087         }
31088         
31089         var cls = 'masonry-brick masonry-brick-full';
31090         
31091         if(this.href.length){
31092             cls += ' masonry-brick-link';
31093         }
31094         
31095         if(this.bgimage.length){
31096             cls += ' masonry-brick-image';
31097         }
31098         
31099         if(!this.html.length){
31100             cls += ' enable-mask';
31101         }
31102         
31103         if(this.size){
31104             cls += ' masonry-' + this.size + '-brick';
31105         }
31106         
31107         if(this.placetitle.length){
31108             
31109             switch (this.placetitle) {
31110                 case 'center' :
31111                     cls += ' masonry-center-title';
31112                     break;
31113                 case 'bottom' :
31114                     cls += ' masonry-bottom-title';
31115                     break;
31116                 default:
31117                     break;
31118             }
31119             
31120         } else {
31121             if(!this.html.length && !this.bgimage.length){
31122                 cls += ' masonry-center-title';
31123             }
31124
31125             if(!this.html.length && this.bgimage.length){
31126                 cls += ' masonry-bottom-title';
31127             }
31128         }
31129         
31130         if(this.cls){
31131             cls += ' ' + this.cls;
31132         }
31133         
31134         var cfg = {
31135             tag: (this.href.length) ? 'a' : 'div',
31136             cls: cls,
31137             cn: [
31138                 {
31139                     tag: 'div',
31140                     cls: 'masonry-brick-paragraph',
31141                     cn: []
31142                 }
31143             ]
31144         };
31145         
31146         if(this.href.length){
31147             cfg.href = this.href;
31148         }
31149         
31150         var cn = cfg.cn[0].cn;
31151         
31152         if(this.title.length){
31153             cn.push({
31154                 tag: 'h4',
31155                 cls: 'masonry-brick-title',
31156                 html: this.title
31157             });
31158         }
31159         
31160         if(this.html.length){
31161             cn.push({
31162                 tag: 'p',
31163                 cls: 'masonry-brick-text',
31164                 html: this.html
31165             });
31166         }  
31167         if (!this.title.length && !this.html.length) {
31168             cfg.cn[0].cls += ' hide';
31169         }
31170         
31171         if(this.bgimage.length){
31172             cfg.cn.push({
31173                 tag: 'img',
31174                 cls: 'masonry-brick-image-view',
31175                 src: this.bgimage
31176             });
31177         }
31178         
31179         if(this.videourl.length){
31180             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31181             // youtube support only?
31182             cfg.cn.push({
31183                 tag: 'iframe',
31184                 cls: 'masonry-brick-image-view',
31185                 src: vurl,
31186                 frameborder : 0,
31187                 allowfullscreen : true
31188             });
31189             
31190             
31191         }
31192         
31193         cfg.cn.push({
31194             tag: 'div',
31195             cls: 'masonry-brick-mask'
31196         });
31197         
31198         return cfg;
31199         
31200     },
31201     
31202     getSplitAutoCreate : function()
31203     {
31204         var cls = 'masonry-brick masonry-brick-split';
31205         
31206         if(this.href.length){
31207             cls += ' masonry-brick-link';
31208         }
31209         
31210         if(this.bgimage.length){
31211             cls += ' masonry-brick-image';
31212         }
31213         
31214         if(this.size){
31215             cls += ' masonry-' + this.size + '-brick';
31216         }
31217         
31218         switch (this.placetitle) {
31219             case 'center' :
31220                 cls += ' masonry-center-title';
31221                 break;
31222             case 'bottom' :
31223                 cls += ' masonry-bottom-title';
31224                 break;
31225             default:
31226                 if(!this.bgimage.length){
31227                     cls += ' masonry-center-title';
31228                 }
31229
31230                 if(this.bgimage.length){
31231                     cls += ' masonry-bottom-title';
31232                 }
31233                 break;
31234         }
31235         
31236         if(this.cls){
31237             cls += ' ' + this.cls;
31238         }
31239         
31240         var cfg = {
31241             tag: (this.href.length) ? 'a' : 'div',
31242             cls: cls,
31243             cn: [
31244                 {
31245                     tag: 'div',
31246                     cls: 'masonry-brick-split-head',
31247                     cn: [
31248                         {
31249                             tag: 'div',
31250                             cls: 'masonry-brick-paragraph',
31251                             cn: []
31252                         }
31253                     ]
31254                 },
31255                 {
31256                     tag: 'div',
31257                     cls: 'masonry-brick-split-body',
31258                     cn: []
31259                 }
31260             ]
31261         };
31262         
31263         if(this.href.length){
31264             cfg.href = this.href;
31265         }
31266         
31267         if(this.title.length){
31268             cfg.cn[0].cn[0].cn.push({
31269                 tag: 'h4',
31270                 cls: 'masonry-brick-title',
31271                 html: this.title
31272             });
31273         }
31274         
31275         if(this.html.length){
31276             cfg.cn[1].cn.push({
31277                 tag: 'p',
31278                 cls: 'masonry-brick-text',
31279                 html: this.html
31280             });
31281         }
31282
31283         if(this.bgimage.length){
31284             cfg.cn[0].cn.push({
31285                 tag: 'img',
31286                 cls: 'masonry-brick-image-view',
31287                 src: this.bgimage
31288             });
31289         }
31290         
31291         if(this.videourl.length){
31292             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31293             // youtube support only?
31294             cfg.cn[0].cn.cn.push({
31295                 tag: 'iframe',
31296                 cls: 'masonry-brick-image-view',
31297                 src: vurl,
31298                 frameborder : 0,
31299                 allowfullscreen : true
31300             });
31301         }
31302         
31303         return cfg;
31304     },
31305     
31306     initEvents: function() 
31307     {
31308         switch (this.size) {
31309             case 'xs' :
31310                 this.x = 1;
31311                 this.y = 1;
31312                 break;
31313             case 'sm' :
31314                 this.x = 2;
31315                 this.y = 2;
31316                 break;
31317             case 'md' :
31318             case 'md-left' :
31319             case 'md-right' :
31320                 this.x = 3;
31321                 this.y = 3;
31322                 break;
31323             case 'tall' :
31324                 this.x = 2;
31325                 this.y = 3;
31326                 break;
31327             case 'wide' :
31328                 this.x = 3;
31329                 this.y = 2;
31330                 break;
31331             case 'wide-thin' :
31332                 this.x = 3;
31333                 this.y = 1;
31334                 break;
31335                         
31336             default :
31337                 break;
31338         }
31339         
31340         if(Roo.isTouch){
31341             this.el.on('touchstart', this.onTouchStart, this);
31342             this.el.on('touchmove', this.onTouchMove, this);
31343             this.el.on('touchend', this.onTouchEnd, this);
31344             this.el.on('contextmenu', this.onContextMenu, this);
31345         } else {
31346             this.el.on('mouseenter'  ,this.enter, this);
31347             this.el.on('mouseleave', this.leave, this);
31348             this.el.on('click', this.onClick, this);
31349         }
31350         
31351         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31352             this.parent().bricks.push(this);   
31353         }
31354         
31355     },
31356     
31357     onClick: function(e, el)
31358     {
31359         var time = this.endTimer - this.startTimer;
31360         
31361         if(Roo.isTouch){
31362             if(time > 1000){
31363                 e.preventDefault();
31364                 return;
31365             }
31366         }
31367         
31368         if(!this.preventDefault){
31369             return;
31370         }
31371         
31372         e.preventDefault();
31373         this.fireEvent('click', this);
31374     },
31375     
31376     enter: function(e, el)
31377     {
31378         e.preventDefault();
31379         
31380         if(!this.isFitContainer){
31381             return;
31382         }
31383         
31384         if(this.bgimage.length && this.html.length){
31385             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31386         }
31387     },
31388     
31389     leave: function(e, el)
31390     {
31391         e.preventDefault();
31392         
31393         if(!this.isFitContainer){
31394             return;
31395         }
31396         
31397         if(this.bgimage.length && this.html.length){
31398             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31399         }
31400     },
31401     
31402     onTouchStart: function(e, el)
31403     {
31404 //        e.preventDefault();
31405         
31406         this.touchmoved = false;
31407         
31408         if(!this.isFitContainer){
31409             return;
31410         }
31411         
31412         if(!this.bgimage.length || !this.html.length){
31413             return;
31414         }
31415         
31416         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31417         
31418         this.timer = new Date().getTime();
31419         
31420     },
31421     
31422     onTouchMove: function(e, el)
31423     {
31424         this.touchmoved = true;
31425     },
31426     
31427     onContextMenu : function(e,el)
31428     {
31429         e.preventDefault();
31430         e.stopPropagation();
31431         return false;
31432     },
31433     
31434     onTouchEnd: function(e, el)
31435     {
31436 //        e.preventDefault();
31437         
31438         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31439         
31440             this.leave(e,el);
31441             
31442             return;
31443         }
31444         
31445         if(!this.bgimage.length || !this.html.length){
31446             
31447             if(this.href.length){
31448                 window.location.href = this.href;
31449             }
31450             
31451             return;
31452         }
31453         
31454         if(!this.isFitContainer){
31455             return;
31456         }
31457         
31458         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31459         
31460         window.location.href = this.href;
31461     }
31462     
31463 });
31464
31465  
31466
31467  /*
31468  * - LGPL
31469  *
31470  * element
31471  * 
31472  */
31473
31474 /**
31475  * @class Roo.bootstrap.Brick
31476  * @extends Roo.bootstrap.Component
31477  * Bootstrap Brick class
31478  * 
31479  * @constructor
31480  * Create a new Brick
31481  * @param {Object} config The config object
31482  */
31483
31484 Roo.bootstrap.Brick = function(config){
31485     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31486     
31487     this.addEvents({
31488         // raw events
31489         /**
31490          * @event click
31491          * When a Brick is click
31492          * @param {Roo.bootstrap.Brick} this
31493          * @param {Roo.EventObject} e
31494          */
31495         "click" : true
31496     });
31497 };
31498
31499 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31500     
31501     /**
31502      * @cfg {String} title
31503      */   
31504     title : '',
31505     /**
31506      * @cfg {String} html
31507      */   
31508     html : '',
31509     /**
31510      * @cfg {String} bgimage
31511      */   
31512     bgimage : '',
31513     /**
31514      * @cfg {String} cls
31515      */   
31516     cls : '',
31517     /**
31518      * @cfg {String} href
31519      */   
31520     href : '',
31521     /**
31522      * @cfg {String} video
31523      */   
31524     video : '',
31525     /**
31526      * @cfg {Boolean} square
31527      */   
31528     square : true,
31529     
31530     getAutoCreate : function()
31531     {
31532         var cls = 'roo-brick';
31533         
31534         if(this.href.length){
31535             cls += ' roo-brick-link';
31536         }
31537         
31538         if(this.bgimage.length){
31539             cls += ' roo-brick-image';
31540         }
31541         
31542         if(!this.html.length && !this.bgimage.length){
31543             cls += ' roo-brick-center-title';
31544         }
31545         
31546         if(!this.html.length && this.bgimage.length){
31547             cls += ' roo-brick-bottom-title';
31548         }
31549         
31550         if(this.cls){
31551             cls += ' ' + this.cls;
31552         }
31553         
31554         var cfg = {
31555             tag: (this.href.length) ? 'a' : 'div',
31556             cls: cls,
31557             cn: [
31558                 {
31559                     tag: 'div',
31560                     cls: 'roo-brick-paragraph',
31561                     cn: []
31562                 }
31563             ]
31564         };
31565         
31566         if(this.href.length){
31567             cfg.href = this.href;
31568         }
31569         
31570         var cn = cfg.cn[0].cn;
31571         
31572         if(this.title.length){
31573             cn.push({
31574                 tag: 'h4',
31575                 cls: 'roo-brick-title',
31576                 html: this.title
31577             });
31578         }
31579         
31580         if(this.html.length){
31581             cn.push({
31582                 tag: 'p',
31583                 cls: 'roo-brick-text',
31584                 html: this.html
31585             });
31586         } else {
31587             cn.cls += ' hide';
31588         }
31589         
31590         if(this.bgimage.length){
31591             cfg.cn.push({
31592                 tag: 'img',
31593                 cls: 'roo-brick-image-view',
31594                 src: this.bgimage
31595             });
31596         }
31597         
31598         return cfg;
31599     },
31600     
31601     initEvents: function() 
31602     {
31603         if(this.title.length || this.html.length){
31604             this.el.on('mouseenter'  ,this.enter, this);
31605             this.el.on('mouseleave', this.leave, this);
31606         }
31607         
31608         
31609         Roo.EventManager.onWindowResize(this.resize, this); 
31610         
31611         this.resize();
31612     },
31613     
31614     resize : function()
31615     {
31616         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31617         
31618         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31619         
31620         if(this.bgimage.length){
31621             var image = this.el.select('.roo-brick-image-view', true).first();
31622             image.setWidth(paragraph.getWidth());
31623             image.setHeight(paragraph.getWidth());
31624             
31625             this.el.setHeight(paragraph.getWidth());
31626             
31627         }
31628         
31629     },
31630     
31631     enter: function(e, el)
31632     {
31633         e.preventDefault();
31634         
31635         if(this.bgimage.length){
31636             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31637             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31638         }
31639     },
31640     
31641     leave: function(e, el)
31642     {
31643         e.preventDefault();
31644         
31645         if(this.bgimage.length){
31646             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31647             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31648         }
31649     }
31650     
31651 });
31652
31653  
31654
31655  /*
31656  * - LGPL
31657  *
31658  * Input
31659  * 
31660  */
31661
31662 /**
31663  * @class Roo.bootstrap.NumberField
31664  * @extends Roo.bootstrap.Input
31665  * Bootstrap NumberField class
31666  * 
31667  * 
31668  * 
31669  * 
31670  * @constructor
31671  * Create a new NumberField
31672  * @param {Object} config The config object
31673  */
31674
31675 Roo.bootstrap.NumberField = function(config){
31676     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31677 };
31678
31679 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31680     
31681     /**
31682      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31683      */
31684     allowDecimals : true,
31685     /**
31686      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31687      */
31688     decimalSeparator : ".",
31689     /**
31690      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31691      */
31692     decimalPrecision : 2,
31693     /**
31694      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31695      */
31696     allowNegative : true,
31697     /**
31698      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31699      */
31700     minValue : Number.NEGATIVE_INFINITY,
31701     /**
31702      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31703      */
31704     maxValue : Number.MAX_VALUE,
31705     /**
31706      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31707      */
31708     minText : "The minimum value for this field is {0}",
31709     /**
31710      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31711      */
31712     maxText : "The maximum value for this field is {0}",
31713     /**
31714      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
31715      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31716      */
31717     nanText : "{0} is not a valid number",
31718     /**
31719      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31720      */
31721     castInt : true,
31722
31723     // private
31724     initEvents : function()
31725     {   
31726         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31727         
31728         var allowed = "0123456789";
31729         
31730         if(this.allowDecimals){
31731             allowed += this.decimalSeparator;
31732         }
31733         
31734         if(this.allowNegative){
31735             allowed += "-";
31736         }
31737         
31738         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31739         
31740         var keyPress = function(e){
31741             
31742             var k = e.getKey();
31743             
31744             var c = e.getCharCode();
31745             
31746             if(
31747                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31748                     allowed.indexOf(String.fromCharCode(c)) === -1
31749             ){
31750                 e.stopEvent();
31751                 return;
31752             }
31753             
31754             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31755                 return;
31756             }
31757             
31758             if(allowed.indexOf(String.fromCharCode(c)) === -1){
31759                 e.stopEvent();
31760             }
31761         };
31762         
31763         this.el.on("keypress", keyPress, this);
31764     },
31765     
31766     validateValue : function(value)
31767     {
31768         
31769         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31770             return false;
31771         }
31772         
31773         var num = this.parseValue(value);
31774         
31775         if(isNaN(num)){
31776             this.markInvalid(String.format(this.nanText, value));
31777             return false;
31778         }
31779         
31780         if(num < this.minValue){
31781             this.markInvalid(String.format(this.minText, this.minValue));
31782             return false;
31783         }
31784         
31785         if(num > this.maxValue){
31786             this.markInvalid(String.format(this.maxText, this.maxValue));
31787             return false;
31788         }
31789         
31790         return true;
31791     },
31792
31793     getValue : function()
31794     {
31795         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31796     },
31797
31798     parseValue : function(value)
31799     {
31800         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31801         return isNaN(value) ? '' : value;
31802     },
31803
31804     fixPrecision : function(value)
31805     {
31806         var nan = isNaN(value);
31807         
31808         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31809             return nan ? '' : value;
31810         }
31811         return parseFloat(value).toFixed(this.decimalPrecision);
31812     },
31813
31814     setValue : function(v)
31815     {
31816         v = this.fixPrecision(v);
31817         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31818     },
31819
31820     decimalPrecisionFcn : function(v)
31821     {
31822         return Math.floor(v);
31823     },
31824
31825     beforeBlur : function()
31826     {
31827         if(!this.castInt){
31828             return;
31829         }
31830         
31831         var v = this.parseValue(this.getRawValue());
31832         if(v){
31833             this.setValue(v);
31834         }
31835     }
31836     
31837 });
31838
31839  
31840
31841 /*
31842 * Licence: LGPL
31843 */
31844
31845 /**
31846  * @class Roo.bootstrap.DocumentSlider
31847  * @extends Roo.bootstrap.Component
31848  * Bootstrap DocumentSlider class
31849  * 
31850  * @constructor
31851  * Create a new DocumentViewer
31852  * @param {Object} config The config object
31853  */
31854
31855 Roo.bootstrap.DocumentSlider = function(config){
31856     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31857     
31858     this.files = [];
31859     
31860     this.addEvents({
31861         /**
31862          * @event initial
31863          * Fire after initEvent
31864          * @param {Roo.bootstrap.DocumentSlider} this
31865          */
31866         "initial" : true,
31867         /**
31868          * @event update
31869          * Fire after update
31870          * @param {Roo.bootstrap.DocumentSlider} this
31871          */
31872         "update" : true,
31873         /**
31874          * @event click
31875          * Fire after click
31876          * @param {Roo.bootstrap.DocumentSlider} this
31877          */
31878         "click" : true
31879     });
31880 };
31881
31882 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
31883     
31884     files : false,
31885     
31886     indicator : 0,
31887     
31888     getAutoCreate : function()
31889     {
31890         var cfg = {
31891             tag : 'div',
31892             cls : 'roo-document-slider',
31893             cn : [
31894                 {
31895                     tag : 'div',
31896                     cls : 'roo-document-slider-header',
31897                     cn : [
31898                         {
31899                             tag : 'div',
31900                             cls : 'roo-document-slider-header-title'
31901                         }
31902                     ]
31903                 },
31904                 {
31905                     tag : 'div',
31906                     cls : 'roo-document-slider-body',
31907                     cn : [
31908                         {
31909                             tag : 'div',
31910                             cls : 'roo-document-slider-prev',
31911                             cn : [
31912                                 {
31913                                     tag : 'i',
31914                                     cls : 'fa fa-chevron-left'
31915                                 }
31916                             ]
31917                         },
31918                         {
31919                             tag : 'div',
31920                             cls : 'roo-document-slider-thumb',
31921                             cn : [
31922                                 {
31923                                     tag : 'img',
31924                                     cls : 'roo-document-slider-image'
31925                                 }
31926                             ]
31927                         },
31928                         {
31929                             tag : 'div',
31930                             cls : 'roo-document-slider-next',
31931                             cn : [
31932                                 {
31933                                     tag : 'i',
31934                                     cls : 'fa fa-chevron-right'
31935                                 }
31936                             ]
31937                         }
31938                     ]
31939                 }
31940             ]
31941         };
31942         
31943         return cfg;
31944     },
31945     
31946     initEvents : function()
31947     {
31948         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31949         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31950         
31951         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31952         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31953         
31954         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31955         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31956         
31957         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31958         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31959         
31960         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31961         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31962         
31963         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31964         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31965         
31966         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31967         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31968         
31969         this.thumbEl.on('click', this.onClick, this);
31970         
31971         this.prevIndicator.on('click', this.prev, this);
31972         
31973         this.nextIndicator.on('click', this.next, this);
31974         
31975     },
31976     
31977     initial : function()
31978     {
31979         if(this.files.length){
31980             this.indicator = 1;
31981             this.update()
31982         }
31983         
31984         this.fireEvent('initial', this);
31985     },
31986     
31987     update : function()
31988     {
31989         this.imageEl.attr('src', this.files[this.indicator - 1]);
31990         
31991         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
31992         
31993         this.prevIndicator.show();
31994         
31995         if(this.indicator == 1){
31996             this.prevIndicator.hide();
31997         }
31998         
31999         this.nextIndicator.show();
32000         
32001         if(this.indicator == this.files.length){
32002             this.nextIndicator.hide();
32003         }
32004         
32005         this.thumbEl.scrollTo('top');
32006         
32007         this.fireEvent('update', this);
32008     },
32009     
32010     onClick : function(e)
32011     {
32012         e.preventDefault();
32013         
32014         this.fireEvent('click', this);
32015     },
32016     
32017     prev : function(e)
32018     {
32019         e.preventDefault();
32020         
32021         this.indicator = Math.max(1, this.indicator - 1);
32022         
32023         this.update();
32024     },
32025     
32026     next : function(e)
32027     {
32028         e.preventDefault();
32029         
32030         this.indicator = Math.min(this.files.length, this.indicator + 1);
32031         
32032         this.update();
32033     }
32034 });
32035 /*
32036  * - LGPL
32037  *
32038  * RadioSet
32039  *
32040  *
32041  */
32042
32043 /**
32044  * @class Roo.bootstrap.RadioSet
32045  * @extends Roo.bootstrap.Input
32046  * Bootstrap RadioSet class
32047  * @cfg {String} indicatorpos (left|right) default left
32048  * @cfg {Boolean} inline (true|false) inline the element (default true)
32049  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32050  * @constructor
32051  * Create a new RadioSet
32052  * @param {Object} config The config object
32053  */
32054
32055 Roo.bootstrap.RadioSet = function(config){
32056     
32057     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
32058
32059     this.radioes = [];
32060     
32061     Roo.bootstrap.RadioSet.register(this);
32062     
32063     this.addEvents({
32064         /**
32065         * @event check
32066         * Fires when the element is checked or unchecked.
32067         * @param {Roo.bootstrap.RadioSet} this This radio
32068         * @param {Roo.bootstrap.Radio} item The checked item
32069         */
32070        check : true
32071     });
32072     
32073 };
32074
32075 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32076
32077     radioes : false,
32078     
32079     inline : true,
32080     
32081     weight : '',
32082     
32083     fieldLabel : '',
32084     
32085     indicatorpos : 'left',
32086     
32087     getAutoCreate : function()
32088     {
32089         var label = {
32090             tag : 'label',
32091             cls : 'roo-radio-set-label',
32092             cn : [
32093                 {
32094                     tag : 'span',
32095                     html : this.fieldLabel
32096                 }
32097             ]
32098         };
32099         
32100         if(this.indicatorpos == 'left'){
32101             label.cn.unshift({
32102                 tag : 'i',
32103                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32104                 tooltip : 'This field is required'
32105             });
32106         } else {
32107             label.cn.push({
32108                 tag : 'i',
32109                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32110                 tooltip : 'This field is required'
32111             });
32112         }
32113         
32114         var items = {
32115             tag : 'div',
32116             cls : 'roo-radio-set-items'
32117         };
32118         
32119         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32120         
32121         if (align === 'left' && this.fieldLabel.length) {
32122             
32123             label.cls += ' col-md-' + this.labelWidth;
32124             
32125             items = {
32126                 cls : "roo-radio-set-right col-md-" + (12 - this.labelWidth), 
32127                 cn: [
32128                     items
32129                 ]
32130             };
32131         }
32132         
32133         var cfg = {
32134             tag : 'div',
32135             cls : 'roo-radio-set',
32136             cn : [
32137                 {
32138                     tag : 'input',
32139                     cls : 'roo-radio-set-input',
32140                     type : 'text',
32141                     name : this.name,
32142                     value : this.value ? this.value :  ''
32143                 },
32144                 label,
32145                 items
32146             ]
32147         };
32148         
32149         if(this.weight.length){
32150             cfg.cls += ' roo-radio-' + this.weight;
32151         }
32152         
32153         if(this.inline) {
32154             cfg.cls += ' roo-radio-set-inline';
32155         }
32156         
32157         return cfg;
32158         
32159     },
32160
32161     initEvents : function()
32162     {
32163         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32164         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32165         
32166         if(!this.fieldLabel.length){
32167             this.labelEl.hide();
32168         }
32169         
32170         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32171         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32172         
32173         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
32174         this.indicatorEl().hide();
32175         
32176         this.originalValue = this.getValue();
32177         
32178     },
32179     
32180     inputEl: function ()
32181     {
32182         return this.el.select('.roo-radio-set-input', true).first();
32183     },
32184     
32185     getChildContainer : function()
32186     {
32187         return this.itemsEl;
32188     },
32189     
32190     register : function(item)
32191     {
32192         this.radioes.push(item);
32193         
32194     },
32195     
32196     validate : function()
32197     {   
32198         var valid = false;
32199         
32200         Roo.each(this.radioes, function(i){
32201             if(!i.checked){
32202                 return;
32203             }
32204             
32205             valid = true;
32206             return false;
32207         });
32208         
32209         if(this.disabled || this.allowBlank || valid){
32210             this.markValid();
32211             return true;
32212         }
32213         
32214         this.markInvalid();
32215         return false;
32216         
32217     },
32218     
32219     markValid : function()
32220     {
32221         if(this.labelEl.isVisible(true)){
32222             this.indicatorEl().hide();
32223         }
32224         
32225         this.el.removeClass([this.invalidClass, this.validClass]);
32226         this.el.addClass(this.validClass);
32227         
32228         this.fireEvent('valid', this);
32229     },
32230     
32231     markInvalid : function(msg)
32232     {
32233         if(this.allowBlank || this.disabled){
32234             return;
32235         }
32236         
32237         if(this.labelEl.isVisible(true)){
32238             this.indicatorEl().show();
32239         }
32240         
32241         this.el.removeClass([this.invalidClass, this.validClass]);
32242         this.el.addClass(this.invalidClass);
32243         
32244         this.fireEvent('invalid', this, msg);
32245         
32246     },
32247     
32248     setValue : function(v, suppressEvent)
32249     {   
32250         Roo.each(this.radioes, function(i){
32251             
32252             i.checked = false;
32253             i.el.removeClass('checked');
32254             
32255             if(i.value === v || i.value.toString() === v.toString()){
32256                 i.checked = true;
32257                 i.el.addClass('checked');
32258                 
32259                 if(suppressEvent !== true){
32260                     this.fireEvent('check', this, i);
32261                 }
32262             }
32263             
32264         }, this);
32265         
32266         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32267         
32268     },
32269     
32270     clearInvalid : function(){
32271         
32272         if(!this.el || this.preventMark){
32273             return;
32274         }
32275         
32276         if(this.labelEl.isVisible(true)){
32277             this.indicatorEl().hide();
32278         }
32279         
32280         this.el.removeClass([this.invalidClass]);
32281         
32282         this.fireEvent('valid', this);
32283     }
32284     
32285 });
32286
32287 Roo.apply(Roo.bootstrap.RadioSet, {
32288     
32289     groups: {},
32290     
32291     register : function(set)
32292     {
32293         this.groups[set.name] = set;
32294     },
32295     
32296     get: function(name) 
32297     {
32298         if (typeof(this.groups[name]) == 'undefined') {
32299             return false;
32300         }
32301         
32302         return this.groups[name] ;
32303     }
32304     
32305 });
32306 /*
32307  * Based on:
32308  * Ext JS Library 1.1.1
32309  * Copyright(c) 2006-2007, Ext JS, LLC.
32310  *
32311  * Originally Released Under LGPL - original licence link has changed is not relivant.
32312  *
32313  * Fork - LGPL
32314  * <script type="text/javascript">
32315  */
32316
32317
32318 /**
32319  * @class Roo.bootstrap.SplitBar
32320  * @extends Roo.util.Observable
32321  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32322  * <br><br>
32323  * Usage:
32324  * <pre><code>
32325 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32326                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32327 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32328 split.minSize = 100;
32329 split.maxSize = 600;
32330 split.animate = true;
32331 split.on('moved', splitterMoved);
32332 </code></pre>
32333  * @constructor
32334  * Create a new SplitBar
32335  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32336  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32337  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32338  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32339                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32340                         position of the SplitBar).
32341  */
32342 Roo.bootstrap.SplitBar = function(cfg){
32343     
32344     /** @private */
32345     
32346     //{
32347     //  dragElement : elm
32348     //  resizingElement: el,
32349         // optional..
32350     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32351     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32352         // existingProxy ???
32353     //}
32354     
32355     this.el = Roo.get(cfg.dragElement, true);
32356     this.el.dom.unselectable = "on";
32357     /** @private */
32358     this.resizingEl = Roo.get(cfg.resizingElement, true);
32359
32360     /**
32361      * @private
32362      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32363      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32364      * @type Number
32365      */
32366     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32367     
32368     /**
32369      * The minimum size of the resizing element. (Defaults to 0)
32370      * @type Number
32371      */
32372     this.minSize = 0;
32373     
32374     /**
32375      * The maximum size of the resizing element. (Defaults to 2000)
32376      * @type Number
32377      */
32378     this.maxSize = 2000;
32379     
32380     /**
32381      * Whether to animate the transition to the new size
32382      * @type Boolean
32383      */
32384     this.animate = false;
32385     
32386     /**
32387      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32388      * @type Boolean
32389      */
32390     this.useShim = false;
32391     
32392     /** @private */
32393     this.shim = null;
32394     
32395     if(!cfg.existingProxy){
32396         /** @private */
32397         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32398     }else{
32399         this.proxy = Roo.get(cfg.existingProxy).dom;
32400     }
32401     /** @private */
32402     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32403     
32404     /** @private */
32405     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32406     
32407     /** @private */
32408     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32409     
32410     /** @private */
32411     this.dragSpecs = {};
32412     
32413     /**
32414      * @private The adapter to use to positon and resize elements
32415      */
32416     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32417     this.adapter.init(this);
32418     
32419     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32420         /** @private */
32421         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32422         this.el.addClass("roo-splitbar-h");
32423     }else{
32424         /** @private */
32425         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32426         this.el.addClass("roo-splitbar-v");
32427     }
32428     
32429     this.addEvents({
32430         /**
32431          * @event resize
32432          * Fires when the splitter is moved (alias for {@link #event-moved})
32433          * @param {Roo.bootstrap.SplitBar} this
32434          * @param {Number} newSize the new width or height
32435          */
32436         "resize" : true,
32437         /**
32438          * @event moved
32439          * Fires when the splitter is moved
32440          * @param {Roo.bootstrap.SplitBar} this
32441          * @param {Number} newSize the new width or height
32442          */
32443         "moved" : true,
32444         /**
32445          * @event beforeresize
32446          * Fires before the splitter is dragged
32447          * @param {Roo.bootstrap.SplitBar} this
32448          */
32449         "beforeresize" : true,
32450
32451         "beforeapply" : true
32452     });
32453
32454     Roo.util.Observable.call(this);
32455 };
32456
32457 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32458     onStartProxyDrag : function(x, y){
32459         this.fireEvent("beforeresize", this);
32460         if(!this.overlay){
32461             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32462             o.unselectable();
32463             o.enableDisplayMode("block");
32464             // all splitbars share the same overlay
32465             Roo.bootstrap.SplitBar.prototype.overlay = o;
32466         }
32467         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32468         this.overlay.show();
32469         Roo.get(this.proxy).setDisplayed("block");
32470         var size = this.adapter.getElementSize(this);
32471         this.activeMinSize = this.getMinimumSize();;
32472         this.activeMaxSize = this.getMaximumSize();;
32473         var c1 = size - this.activeMinSize;
32474         var c2 = Math.max(this.activeMaxSize - size, 0);
32475         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32476             this.dd.resetConstraints();
32477             this.dd.setXConstraint(
32478                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32479                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32480             );
32481             this.dd.setYConstraint(0, 0);
32482         }else{
32483             this.dd.resetConstraints();
32484             this.dd.setXConstraint(0, 0);
32485             this.dd.setYConstraint(
32486                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32487                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32488             );
32489          }
32490         this.dragSpecs.startSize = size;
32491         this.dragSpecs.startPoint = [x, y];
32492         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32493     },
32494     
32495     /** 
32496      * @private Called after the drag operation by the DDProxy
32497      */
32498     onEndProxyDrag : function(e){
32499         Roo.get(this.proxy).setDisplayed(false);
32500         var endPoint = Roo.lib.Event.getXY(e);
32501         if(this.overlay){
32502             this.overlay.hide();
32503         }
32504         var newSize;
32505         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32506             newSize = this.dragSpecs.startSize + 
32507                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32508                     endPoint[0] - this.dragSpecs.startPoint[0] :
32509                     this.dragSpecs.startPoint[0] - endPoint[0]
32510                 );
32511         }else{
32512             newSize = this.dragSpecs.startSize + 
32513                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32514                     endPoint[1] - this.dragSpecs.startPoint[1] :
32515                     this.dragSpecs.startPoint[1] - endPoint[1]
32516                 );
32517         }
32518         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32519         if(newSize != this.dragSpecs.startSize){
32520             if(this.fireEvent('beforeapply', this, newSize) !== false){
32521                 this.adapter.setElementSize(this, newSize);
32522                 this.fireEvent("moved", this, newSize);
32523                 this.fireEvent("resize", this, newSize);
32524             }
32525         }
32526     },
32527     
32528     /**
32529      * Get the adapter this SplitBar uses
32530      * @return The adapter object
32531      */
32532     getAdapter : function(){
32533         return this.adapter;
32534     },
32535     
32536     /**
32537      * Set the adapter this SplitBar uses
32538      * @param {Object} adapter A SplitBar adapter object
32539      */
32540     setAdapter : function(adapter){
32541         this.adapter = adapter;
32542         this.adapter.init(this);
32543     },
32544     
32545     /**
32546      * Gets the minimum size for the resizing element
32547      * @return {Number} The minimum size
32548      */
32549     getMinimumSize : function(){
32550         return this.minSize;
32551     },
32552     
32553     /**
32554      * Sets the minimum size for the resizing element
32555      * @param {Number} minSize The minimum size
32556      */
32557     setMinimumSize : function(minSize){
32558         this.minSize = minSize;
32559     },
32560     
32561     /**
32562      * Gets the maximum size for the resizing element
32563      * @return {Number} The maximum size
32564      */
32565     getMaximumSize : function(){
32566         return this.maxSize;
32567     },
32568     
32569     /**
32570      * Sets the maximum size for the resizing element
32571      * @param {Number} maxSize The maximum size
32572      */
32573     setMaximumSize : function(maxSize){
32574         this.maxSize = maxSize;
32575     },
32576     
32577     /**
32578      * Sets the initialize size for the resizing element
32579      * @param {Number} size The initial size
32580      */
32581     setCurrentSize : function(size){
32582         var oldAnimate = this.animate;
32583         this.animate = false;
32584         this.adapter.setElementSize(this, size);
32585         this.animate = oldAnimate;
32586     },
32587     
32588     /**
32589      * Destroy this splitbar. 
32590      * @param {Boolean} removeEl True to remove the element
32591      */
32592     destroy : function(removeEl){
32593         if(this.shim){
32594             this.shim.remove();
32595         }
32596         this.dd.unreg();
32597         this.proxy.parentNode.removeChild(this.proxy);
32598         if(removeEl){
32599             this.el.remove();
32600         }
32601     }
32602 });
32603
32604 /**
32605  * @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.
32606  */
32607 Roo.bootstrap.SplitBar.createProxy = function(dir){
32608     var proxy = new Roo.Element(document.createElement("div"));
32609     proxy.unselectable();
32610     var cls = 'roo-splitbar-proxy';
32611     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32612     document.body.appendChild(proxy.dom);
32613     return proxy.dom;
32614 };
32615
32616 /** 
32617  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32618  * Default Adapter. It assumes the splitter and resizing element are not positioned
32619  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32620  */
32621 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32622 };
32623
32624 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32625     // do nothing for now
32626     init : function(s){
32627     
32628     },
32629     /**
32630      * Called before drag operations to get the current size of the resizing element. 
32631      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32632      */
32633      getElementSize : function(s){
32634         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32635             return s.resizingEl.getWidth();
32636         }else{
32637             return s.resizingEl.getHeight();
32638         }
32639     },
32640     
32641     /**
32642      * Called after drag operations to set the size of the resizing element.
32643      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32644      * @param {Number} newSize The new size to set
32645      * @param {Function} onComplete A function to be invoked when resizing is complete
32646      */
32647     setElementSize : function(s, newSize, onComplete){
32648         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32649             if(!s.animate){
32650                 s.resizingEl.setWidth(newSize);
32651                 if(onComplete){
32652                     onComplete(s, newSize);
32653                 }
32654             }else{
32655                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32656             }
32657         }else{
32658             
32659             if(!s.animate){
32660                 s.resizingEl.setHeight(newSize);
32661                 if(onComplete){
32662                     onComplete(s, newSize);
32663                 }
32664             }else{
32665                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32666             }
32667         }
32668     }
32669 };
32670
32671 /** 
32672  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32673  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32674  * Adapter that  moves the splitter element to align with the resized sizing element. 
32675  * Used with an absolute positioned SplitBar.
32676  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32677  * document.body, make sure you assign an id to the body element.
32678  */
32679 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32680     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32681     this.container = Roo.get(container);
32682 };
32683
32684 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32685     init : function(s){
32686         this.basic.init(s);
32687     },
32688     
32689     getElementSize : function(s){
32690         return this.basic.getElementSize(s);
32691     },
32692     
32693     setElementSize : function(s, newSize, onComplete){
32694         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32695     },
32696     
32697     moveSplitter : function(s){
32698         var yes = Roo.bootstrap.SplitBar;
32699         switch(s.placement){
32700             case yes.LEFT:
32701                 s.el.setX(s.resizingEl.getRight());
32702                 break;
32703             case yes.RIGHT:
32704                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32705                 break;
32706             case yes.TOP:
32707                 s.el.setY(s.resizingEl.getBottom());
32708                 break;
32709             case yes.BOTTOM:
32710                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32711                 break;
32712         }
32713     }
32714 };
32715
32716 /**
32717  * Orientation constant - Create a vertical SplitBar
32718  * @static
32719  * @type Number
32720  */
32721 Roo.bootstrap.SplitBar.VERTICAL = 1;
32722
32723 /**
32724  * Orientation constant - Create a horizontal SplitBar
32725  * @static
32726  * @type Number
32727  */
32728 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32729
32730 /**
32731  * Placement constant - The resizing element is to the left of the splitter element
32732  * @static
32733  * @type Number
32734  */
32735 Roo.bootstrap.SplitBar.LEFT = 1;
32736
32737 /**
32738  * Placement constant - The resizing element is to the right of the splitter element
32739  * @static
32740  * @type Number
32741  */
32742 Roo.bootstrap.SplitBar.RIGHT = 2;
32743
32744 /**
32745  * Placement constant - The resizing element is positioned above the splitter element
32746  * @static
32747  * @type Number
32748  */
32749 Roo.bootstrap.SplitBar.TOP = 3;
32750
32751 /**
32752  * Placement constant - The resizing element is positioned under splitter element
32753  * @static
32754  * @type Number
32755  */
32756 Roo.bootstrap.SplitBar.BOTTOM = 4;
32757 Roo.namespace("Roo.bootstrap.layout");/*
32758  * Based on:
32759  * Ext JS Library 1.1.1
32760  * Copyright(c) 2006-2007, Ext JS, LLC.
32761  *
32762  * Originally Released Under LGPL - original licence link has changed is not relivant.
32763  *
32764  * Fork - LGPL
32765  * <script type="text/javascript">
32766  */
32767
32768 /**
32769  * @class Roo.bootstrap.layout.Manager
32770  * @extends Roo.bootstrap.Component
32771  * Base class for layout managers.
32772  */
32773 Roo.bootstrap.layout.Manager = function(config)
32774 {
32775     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32776
32777
32778
32779
32780
32781     /** false to disable window resize monitoring @type Boolean */
32782     this.monitorWindowResize = true;
32783     this.regions = {};
32784     this.addEvents({
32785         /**
32786          * @event layout
32787          * Fires when a layout is performed.
32788          * @param {Roo.LayoutManager} this
32789          */
32790         "layout" : true,
32791         /**
32792          * @event regionresized
32793          * Fires when the user resizes a region.
32794          * @param {Roo.LayoutRegion} region The resized region
32795          * @param {Number} newSize The new size (width for east/west, height for north/south)
32796          */
32797         "regionresized" : true,
32798         /**
32799          * @event regioncollapsed
32800          * Fires when a region is collapsed.
32801          * @param {Roo.LayoutRegion} region The collapsed region
32802          */
32803         "regioncollapsed" : true,
32804         /**
32805          * @event regionexpanded
32806          * Fires when a region is expanded.
32807          * @param {Roo.LayoutRegion} region The expanded region
32808          */
32809         "regionexpanded" : true
32810     });
32811     this.updating = false;
32812
32813     if (config.el) {
32814         this.el = Roo.get(config.el);
32815         this.initEvents();
32816     }
32817
32818 };
32819
32820 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32821
32822
32823     regions : null,
32824
32825     monitorWindowResize : true,
32826
32827
32828     updating : false,
32829
32830
32831     onRender : function(ct, position)
32832     {
32833         if(!this.el){
32834             this.el = Roo.get(ct);
32835             this.initEvents();
32836         }
32837         //this.fireEvent('render',this);
32838     },
32839
32840
32841     initEvents: function()
32842     {
32843
32844
32845         // ie scrollbar fix
32846         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32847             document.body.scroll = "no";
32848         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32849             this.el.position('relative');
32850         }
32851         this.id = this.el.id;
32852         this.el.addClass("roo-layout-container");
32853         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32854         if(this.el.dom != document.body ) {
32855             this.el.on('resize', this.layout,this);
32856             this.el.on('show', this.layout,this);
32857         }
32858
32859     },
32860
32861     /**
32862      * Returns true if this layout is currently being updated
32863      * @return {Boolean}
32864      */
32865     isUpdating : function(){
32866         return this.updating;
32867     },
32868
32869     /**
32870      * Suspend the LayoutManager from doing auto-layouts while
32871      * making multiple add or remove calls
32872      */
32873     beginUpdate : function(){
32874         this.updating = true;
32875     },
32876
32877     /**
32878      * Restore auto-layouts and optionally disable the manager from performing a layout
32879      * @param {Boolean} noLayout true to disable a layout update
32880      */
32881     endUpdate : function(noLayout){
32882         this.updating = false;
32883         if(!noLayout){
32884             this.layout();
32885         }
32886     },
32887
32888     layout: function(){
32889         // abstract...
32890     },
32891
32892     onRegionResized : function(region, newSize){
32893         this.fireEvent("regionresized", region, newSize);
32894         this.layout();
32895     },
32896
32897     onRegionCollapsed : function(region){
32898         this.fireEvent("regioncollapsed", region);
32899     },
32900
32901     onRegionExpanded : function(region){
32902         this.fireEvent("regionexpanded", region);
32903     },
32904
32905     /**
32906      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32907      * performs box-model adjustments.
32908      * @return {Object} The size as an object {width: (the width), height: (the height)}
32909      */
32910     getViewSize : function()
32911     {
32912         var size;
32913         if(this.el.dom != document.body){
32914             size = this.el.getSize();
32915         }else{
32916             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32917         }
32918         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32919         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32920         return size;
32921     },
32922
32923     /**
32924      * Returns the Element this layout is bound to.
32925      * @return {Roo.Element}
32926      */
32927     getEl : function(){
32928         return this.el;
32929     },
32930
32931     /**
32932      * Returns the specified region.
32933      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32934      * @return {Roo.LayoutRegion}
32935      */
32936     getRegion : function(target){
32937         return this.regions[target.toLowerCase()];
32938     },
32939
32940     onWindowResize : function(){
32941         if(this.monitorWindowResize){
32942             this.layout();
32943         }
32944     }
32945 });
32946 /*
32947  * Based on:
32948  * Ext JS Library 1.1.1
32949  * Copyright(c) 2006-2007, Ext JS, LLC.
32950  *
32951  * Originally Released Under LGPL - original licence link has changed is not relivant.
32952  *
32953  * Fork - LGPL
32954  * <script type="text/javascript">
32955  */
32956 /**
32957  * @class Roo.bootstrap.layout.Border
32958  * @extends Roo.bootstrap.layout.Manager
32959  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32960  * please see: examples/bootstrap/nested.html<br><br>
32961  
32962 <b>The container the layout is rendered into can be either the body element or any other element.
32963 If it is not the body element, the container needs to either be an absolute positioned element,
32964 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32965 the container size if it is not the body element.</b>
32966
32967 * @constructor
32968 * Create a new Border
32969 * @param {Object} config Configuration options
32970  */
32971 Roo.bootstrap.layout.Border = function(config){
32972     config = config || {};
32973     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32974     
32975     
32976     
32977     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32978         if(config[region]){
32979             config[region].region = region;
32980             this.addRegion(config[region]);
32981         }
32982     },this);
32983     
32984 };
32985
32986 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32987
32988 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32989     /**
32990      * Creates and adds a new region if it doesn't already exist.
32991      * @param {String} target The target region key (north, south, east, west or center).
32992      * @param {Object} config The regions config object
32993      * @return {BorderLayoutRegion} The new region
32994      */
32995     addRegion : function(config)
32996     {
32997         if(!this.regions[config.region]){
32998             var r = this.factory(config);
32999             this.bindRegion(r);
33000         }
33001         return this.regions[config.region];
33002     },
33003
33004     // private (kinda)
33005     bindRegion : function(r){
33006         this.regions[r.config.region] = r;
33007         
33008         r.on("visibilitychange",    this.layout, this);
33009         r.on("paneladded",          this.layout, this);
33010         r.on("panelremoved",        this.layout, this);
33011         r.on("invalidated",         this.layout, this);
33012         r.on("resized",             this.onRegionResized, this);
33013         r.on("collapsed",           this.onRegionCollapsed, this);
33014         r.on("expanded",            this.onRegionExpanded, this);
33015     },
33016
33017     /**
33018      * Performs a layout update.
33019      */
33020     layout : function()
33021     {
33022         if(this.updating) {
33023             return;
33024         }
33025         
33026         // render all the rebions if they have not been done alreayd?
33027         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33028             if(this.regions[region] && !this.regions[region].bodyEl){
33029                 this.regions[region].onRender(this.el)
33030             }
33031         },this);
33032         
33033         var size = this.getViewSize();
33034         var w = size.width;
33035         var h = size.height;
33036         var centerW = w;
33037         var centerH = h;
33038         var centerY = 0;
33039         var centerX = 0;
33040         //var x = 0, y = 0;
33041
33042         var rs = this.regions;
33043         var north = rs["north"];
33044         var south = rs["south"]; 
33045         var west = rs["west"];
33046         var east = rs["east"];
33047         var center = rs["center"];
33048         //if(this.hideOnLayout){ // not supported anymore
33049             //c.el.setStyle("display", "none");
33050         //}
33051         if(north && north.isVisible()){
33052             var b = north.getBox();
33053             var m = north.getMargins();
33054             b.width = w - (m.left+m.right);
33055             b.x = m.left;
33056             b.y = m.top;
33057             centerY = b.height + b.y + m.bottom;
33058             centerH -= centerY;
33059             north.updateBox(this.safeBox(b));
33060         }
33061         if(south && south.isVisible()){
33062             var b = south.getBox();
33063             var m = south.getMargins();
33064             b.width = w - (m.left+m.right);
33065             b.x = m.left;
33066             var totalHeight = (b.height + m.top + m.bottom);
33067             b.y = h - totalHeight + m.top;
33068             centerH -= totalHeight;
33069             south.updateBox(this.safeBox(b));
33070         }
33071         if(west && west.isVisible()){
33072             var b = west.getBox();
33073             var m = west.getMargins();
33074             b.height = centerH - (m.top+m.bottom);
33075             b.x = m.left;
33076             b.y = centerY + m.top;
33077             var totalWidth = (b.width + m.left + m.right);
33078             centerX += totalWidth;
33079             centerW -= totalWidth;
33080             west.updateBox(this.safeBox(b));
33081         }
33082         if(east && east.isVisible()){
33083             var b = east.getBox();
33084             var m = east.getMargins();
33085             b.height = centerH - (m.top+m.bottom);
33086             var totalWidth = (b.width + m.left + m.right);
33087             b.x = w - totalWidth + m.left;
33088             b.y = centerY + m.top;
33089             centerW -= totalWidth;
33090             east.updateBox(this.safeBox(b));
33091         }
33092         if(center){
33093             var m = center.getMargins();
33094             var centerBox = {
33095                 x: centerX + m.left,
33096                 y: centerY + m.top,
33097                 width: centerW - (m.left+m.right),
33098                 height: centerH - (m.top+m.bottom)
33099             };
33100             //if(this.hideOnLayout){
33101                 //center.el.setStyle("display", "block");
33102             //}
33103             center.updateBox(this.safeBox(centerBox));
33104         }
33105         this.el.repaint();
33106         this.fireEvent("layout", this);
33107     },
33108
33109     // private
33110     safeBox : function(box){
33111         box.width = Math.max(0, box.width);
33112         box.height = Math.max(0, box.height);
33113         return box;
33114     },
33115
33116     /**
33117      * Adds a ContentPanel (or subclass) to this layout.
33118      * @param {String} target The target region key (north, south, east, west or center).
33119      * @param {Roo.ContentPanel} panel The panel to add
33120      * @return {Roo.ContentPanel} The added panel
33121      */
33122     add : function(target, panel){
33123          
33124         target = target.toLowerCase();
33125         return this.regions[target].add(panel);
33126     },
33127
33128     /**
33129      * Remove a ContentPanel (or subclass) to this layout.
33130      * @param {String} target The target region key (north, south, east, west or center).
33131      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33132      * @return {Roo.ContentPanel} The removed panel
33133      */
33134     remove : function(target, panel){
33135         target = target.toLowerCase();
33136         return this.regions[target].remove(panel);
33137     },
33138
33139     /**
33140      * Searches all regions for a panel with the specified id
33141      * @param {String} panelId
33142      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33143      */
33144     findPanel : function(panelId){
33145         var rs = this.regions;
33146         for(var target in rs){
33147             if(typeof rs[target] != "function"){
33148                 var p = rs[target].getPanel(panelId);
33149                 if(p){
33150                     return p;
33151                 }
33152             }
33153         }
33154         return null;
33155     },
33156
33157     /**
33158      * Searches all regions for a panel with the specified id and activates (shows) it.
33159      * @param {String/ContentPanel} panelId The panels id or the panel itself
33160      * @return {Roo.ContentPanel} The shown panel or null
33161      */
33162     showPanel : function(panelId) {
33163       var rs = this.regions;
33164       for(var target in rs){
33165          var r = rs[target];
33166          if(typeof r != "function"){
33167             if(r.hasPanel(panelId)){
33168                return r.showPanel(panelId);
33169             }
33170          }
33171       }
33172       return null;
33173    },
33174
33175    /**
33176      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33177      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33178      */
33179    /*
33180     restoreState : function(provider){
33181         if(!provider){
33182             provider = Roo.state.Manager;
33183         }
33184         var sm = new Roo.LayoutStateManager();
33185         sm.init(this, provider);
33186     },
33187 */
33188  
33189  
33190     /**
33191      * Adds a xtype elements to the layout.
33192      * <pre><code>
33193
33194 layout.addxtype({
33195        xtype : 'ContentPanel',
33196        region: 'west',
33197        items: [ .... ]
33198    }
33199 );
33200
33201 layout.addxtype({
33202         xtype : 'NestedLayoutPanel',
33203         region: 'west',
33204         layout: {
33205            center: { },
33206            west: { }   
33207         },
33208         items : [ ... list of content panels or nested layout panels.. ]
33209    }
33210 );
33211 </code></pre>
33212      * @param {Object} cfg Xtype definition of item to add.
33213      */
33214     addxtype : function(cfg)
33215     {
33216         // basically accepts a pannel...
33217         // can accept a layout region..!?!?
33218         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33219         
33220         
33221         // theory?  children can only be panels??
33222         
33223         //if (!cfg.xtype.match(/Panel$/)) {
33224         //    return false;
33225         //}
33226         var ret = false;
33227         
33228         if (typeof(cfg.region) == 'undefined') {
33229             Roo.log("Failed to add Panel, region was not set");
33230             Roo.log(cfg);
33231             return false;
33232         }
33233         var region = cfg.region;
33234         delete cfg.region;
33235         
33236           
33237         var xitems = [];
33238         if (cfg.items) {
33239             xitems = cfg.items;
33240             delete cfg.items;
33241         }
33242         var nb = false;
33243         
33244         switch(cfg.xtype) 
33245         {
33246             case 'Content':  // ContentPanel (el, cfg)
33247             case 'Scroll':  // ContentPanel (el, cfg)
33248             case 'View': 
33249                 cfg.autoCreate = true;
33250                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33251                 //} else {
33252                 //    var el = this.el.createChild();
33253                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33254                 //}
33255                 
33256                 this.add(region, ret);
33257                 break;
33258             
33259             /*
33260             case 'TreePanel': // our new panel!
33261                 cfg.el = this.el.createChild();
33262                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33263                 this.add(region, ret);
33264                 break;
33265             */
33266             
33267             case 'Nest': 
33268                 // create a new Layout (which is  a Border Layout...
33269                 
33270                 var clayout = cfg.layout;
33271                 clayout.el  = this.el.createChild();
33272                 clayout.items   = clayout.items  || [];
33273                 
33274                 delete cfg.layout;
33275                 
33276                 // replace this exitems with the clayout ones..
33277                 xitems = clayout.items;
33278                  
33279                 // force background off if it's in center...
33280                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33281                     cfg.background = false;
33282                 }
33283                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33284                 
33285                 
33286                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33287                 //console.log('adding nested layout panel '  + cfg.toSource());
33288                 this.add(region, ret);
33289                 nb = {}; /// find first...
33290                 break;
33291             
33292             case 'Grid':
33293                 
33294                 // needs grid and region
33295                 
33296                 //var el = this.getRegion(region).el.createChild();
33297                 /*
33298                  *var el = this.el.createChild();
33299                 // create the grid first...
33300                 cfg.grid.container = el;
33301                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33302                 */
33303                 
33304                 if (region == 'center' && this.active ) {
33305                     cfg.background = false;
33306                 }
33307                 
33308                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33309                 
33310                 this.add(region, ret);
33311                 /*
33312                 if (cfg.background) {
33313                     // render grid on panel activation (if panel background)
33314                     ret.on('activate', function(gp) {
33315                         if (!gp.grid.rendered) {
33316                     //        gp.grid.render(el);
33317                         }
33318                     });
33319                 } else {
33320                   //  cfg.grid.render(el);
33321                 }
33322                 */
33323                 break;
33324            
33325            
33326             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33327                 // it was the old xcomponent building that caused this before.
33328                 // espeically if border is the top element in the tree.
33329                 ret = this;
33330                 break; 
33331                 
33332                     
33333                 
33334                 
33335                 
33336             default:
33337                 /*
33338                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33339                     
33340                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33341                     this.add(region, ret);
33342                 } else {
33343                 */
33344                     Roo.log(cfg);
33345                     throw "Can not add '" + cfg.xtype + "' to Border";
33346                     return null;
33347              
33348                                 
33349              
33350         }
33351         this.beginUpdate();
33352         // add children..
33353         var region = '';
33354         var abn = {};
33355         Roo.each(xitems, function(i)  {
33356             region = nb && i.region ? i.region : false;
33357             
33358             var add = ret.addxtype(i);
33359            
33360             if (region) {
33361                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33362                 if (!i.background) {
33363                     abn[region] = nb[region] ;
33364                 }
33365             }
33366             
33367         });
33368         this.endUpdate();
33369
33370         // make the last non-background panel active..
33371         //if (nb) { Roo.log(abn); }
33372         if (nb) {
33373             
33374             for(var r in abn) {
33375                 region = this.getRegion(r);
33376                 if (region) {
33377                     // tried using nb[r], but it does not work..
33378                      
33379                     region.showPanel(abn[r]);
33380                    
33381                 }
33382             }
33383         }
33384         return ret;
33385         
33386     },
33387     
33388     
33389 // private
33390     factory : function(cfg)
33391     {
33392         
33393         var validRegions = Roo.bootstrap.layout.Border.regions;
33394
33395         var target = cfg.region;
33396         cfg.mgr = this;
33397         
33398         var r = Roo.bootstrap.layout;
33399         Roo.log(target);
33400         switch(target){
33401             case "north":
33402                 return new r.North(cfg);
33403             case "south":
33404                 return new r.South(cfg);
33405             case "east":
33406                 return new r.East(cfg);
33407             case "west":
33408                 return new r.West(cfg);
33409             case "center":
33410                 return new r.Center(cfg);
33411         }
33412         throw 'Layout region "'+target+'" not supported.';
33413     }
33414     
33415     
33416 });
33417  /*
33418  * Based on:
33419  * Ext JS Library 1.1.1
33420  * Copyright(c) 2006-2007, Ext JS, LLC.
33421  *
33422  * Originally Released Under LGPL - original licence link has changed is not relivant.
33423  *
33424  * Fork - LGPL
33425  * <script type="text/javascript">
33426  */
33427  
33428 /**
33429  * @class Roo.bootstrap.layout.Basic
33430  * @extends Roo.util.Observable
33431  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33432  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33433  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33434  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33435  * @cfg {string}   region  the region that it inhabits..
33436  * @cfg {bool}   skipConfig skip config?
33437  * 
33438
33439  */
33440 Roo.bootstrap.layout.Basic = function(config){
33441     
33442     this.mgr = config.mgr;
33443     
33444     this.position = config.region;
33445     
33446     var skipConfig = config.skipConfig;
33447     
33448     this.events = {
33449         /**
33450          * @scope Roo.BasicLayoutRegion
33451          */
33452         
33453         /**
33454          * @event beforeremove
33455          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33456          * @param {Roo.LayoutRegion} this
33457          * @param {Roo.ContentPanel} panel The panel
33458          * @param {Object} e The cancel event object
33459          */
33460         "beforeremove" : true,
33461         /**
33462          * @event invalidated
33463          * Fires when the layout for this region is changed.
33464          * @param {Roo.LayoutRegion} this
33465          */
33466         "invalidated" : true,
33467         /**
33468          * @event visibilitychange
33469          * Fires when this region is shown or hidden 
33470          * @param {Roo.LayoutRegion} this
33471          * @param {Boolean} visibility true or false
33472          */
33473         "visibilitychange" : true,
33474         /**
33475          * @event paneladded
33476          * Fires when a panel is added. 
33477          * @param {Roo.LayoutRegion} this
33478          * @param {Roo.ContentPanel} panel The panel
33479          */
33480         "paneladded" : true,
33481         /**
33482          * @event panelremoved
33483          * Fires when a panel is removed. 
33484          * @param {Roo.LayoutRegion} this
33485          * @param {Roo.ContentPanel} panel The panel
33486          */
33487         "panelremoved" : true,
33488         /**
33489          * @event beforecollapse
33490          * Fires when this region before collapse.
33491          * @param {Roo.LayoutRegion} this
33492          */
33493         "beforecollapse" : true,
33494         /**
33495          * @event collapsed
33496          * Fires when this region is collapsed.
33497          * @param {Roo.LayoutRegion} this
33498          */
33499         "collapsed" : true,
33500         /**
33501          * @event expanded
33502          * Fires when this region is expanded.
33503          * @param {Roo.LayoutRegion} this
33504          */
33505         "expanded" : true,
33506         /**
33507          * @event slideshow
33508          * Fires when this region is slid into view.
33509          * @param {Roo.LayoutRegion} this
33510          */
33511         "slideshow" : true,
33512         /**
33513          * @event slidehide
33514          * Fires when this region slides out of view. 
33515          * @param {Roo.LayoutRegion} this
33516          */
33517         "slidehide" : true,
33518         /**
33519          * @event panelactivated
33520          * Fires when a panel is activated. 
33521          * @param {Roo.LayoutRegion} this
33522          * @param {Roo.ContentPanel} panel The activated panel
33523          */
33524         "panelactivated" : true,
33525         /**
33526          * @event resized
33527          * Fires when the user resizes this region. 
33528          * @param {Roo.LayoutRegion} this
33529          * @param {Number} newSize The new size (width for east/west, height for north/south)
33530          */
33531         "resized" : true
33532     };
33533     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33534     this.panels = new Roo.util.MixedCollection();
33535     this.panels.getKey = this.getPanelId.createDelegate(this);
33536     this.box = null;
33537     this.activePanel = null;
33538     // ensure listeners are added...
33539     
33540     if (config.listeners || config.events) {
33541         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33542             listeners : config.listeners || {},
33543             events : config.events || {}
33544         });
33545     }
33546     
33547     if(skipConfig !== true){
33548         this.applyConfig(config);
33549     }
33550 };
33551
33552 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33553 {
33554     getPanelId : function(p){
33555         return p.getId();
33556     },
33557     
33558     applyConfig : function(config){
33559         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33560         this.config = config;
33561         
33562     },
33563     
33564     /**
33565      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33566      * the width, for horizontal (north, south) the height.
33567      * @param {Number} newSize The new width or height
33568      */
33569     resizeTo : function(newSize){
33570         var el = this.el ? this.el :
33571                  (this.activePanel ? this.activePanel.getEl() : null);
33572         if(el){
33573             switch(this.position){
33574                 case "east":
33575                 case "west":
33576                     el.setWidth(newSize);
33577                     this.fireEvent("resized", this, newSize);
33578                 break;
33579                 case "north":
33580                 case "south":
33581                     el.setHeight(newSize);
33582                     this.fireEvent("resized", this, newSize);
33583                 break;                
33584             }
33585         }
33586     },
33587     
33588     getBox : function(){
33589         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33590     },
33591     
33592     getMargins : function(){
33593         return this.margins;
33594     },
33595     
33596     updateBox : function(box){
33597         this.box = box;
33598         var el = this.activePanel.getEl();
33599         el.dom.style.left = box.x + "px";
33600         el.dom.style.top = box.y + "px";
33601         this.activePanel.setSize(box.width, box.height);
33602     },
33603     
33604     /**
33605      * Returns the container element for this region.
33606      * @return {Roo.Element}
33607      */
33608     getEl : function(){
33609         return this.activePanel;
33610     },
33611     
33612     /**
33613      * Returns true if this region is currently visible.
33614      * @return {Boolean}
33615      */
33616     isVisible : function(){
33617         return this.activePanel ? true : false;
33618     },
33619     
33620     setActivePanel : function(panel){
33621         panel = this.getPanel(panel);
33622         if(this.activePanel && this.activePanel != panel){
33623             this.activePanel.setActiveState(false);
33624             this.activePanel.getEl().setLeftTop(-10000,-10000);
33625         }
33626         this.activePanel = panel;
33627         panel.setActiveState(true);
33628         if(this.box){
33629             panel.setSize(this.box.width, this.box.height);
33630         }
33631         this.fireEvent("panelactivated", this, panel);
33632         this.fireEvent("invalidated");
33633     },
33634     
33635     /**
33636      * Show the specified panel.
33637      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33638      * @return {Roo.ContentPanel} The shown panel or null
33639      */
33640     showPanel : function(panel){
33641         panel = this.getPanel(panel);
33642         if(panel){
33643             this.setActivePanel(panel);
33644         }
33645         return panel;
33646     },
33647     
33648     /**
33649      * Get the active panel for this region.
33650      * @return {Roo.ContentPanel} The active panel or null
33651      */
33652     getActivePanel : function(){
33653         return this.activePanel;
33654     },
33655     
33656     /**
33657      * Add the passed ContentPanel(s)
33658      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33659      * @return {Roo.ContentPanel} The panel added (if only one was added)
33660      */
33661     add : function(panel){
33662         if(arguments.length > 1){
33663             for(var i = 0, len = arguments.length; i < len; i++) {
33664                 this.add(arguments[i]);
33665             }
33666             return null;
33667         }
33668         if(this.hasPanel(panel)){
33669             this.showPanel(panel);
33670             return panel;
33671         }
33672         var el = panel.getEl();
33673         if(el.dom.parentNode != this.mgr.el.dom){
33674             this.mgr.el.dom.appendChild(el.dom);
33675         }
33676         if(panel.setRegion){
33677             panel.setRegion(this);
33678         }
33679         this.panels.add(panel);
33680         el.setStyle("position", "absolute");
33681         if(!panel.background){
33682             this.setActivePanel(panel);
33683             if(this.config.initialSize && this.panels.getCount()==1){
33684                 this.resizeTo(this.config.initialSize);
33685             }
33686         }
33687         this.fireEvent("paneladded", this, panel);
33688         return panel;
33689     },
33690     
33691     /**
33692      * Returns true if the panel is in this region.
33693      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33694      * @return {Boolean}
33695      */
33696     hasPanel : function(panel){
33697         if(typeof panel == "object"){ // must be panel obj
33698             panel = panel.getId();
33699         }
33700         return this.getPanel(panel) ? true : false;
33701     },
33702     
33703     /**
33704      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33705      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33706      * @param {Boolean} preservePanel Overrides the config preservePanel option
33707      * @return {Roo.ContentPanel} The panel that was removed
33708      */
33709     remove : function(panel, preservePanel){
33710         panel = this.getPanel(panel);
33711         if(!panel){
33712             return null;
33713         }
33714         var e = {};
33715         this.fireEvent("beforeremove", this, panel, e);
33716         if(e.cancel === true){
33717             return null;
33718         }
33719         var panelId = panel.getId();
33720         this.panels.removeKey(panelId);
33721         return panel;
33722     },
33723     
33724     /**
33725      * Returns the panel specified or null if it's not in this region.
33726      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33727      * @return {Roo.ContentPanel}
33728      */
33729     getPanel : function(id){
33730         if(typeof id == "object"){ // must be panel obj
33731             return id;
33732         }
33733         return this.panels.get(id);
33734     },
33735     
33736     /**
33737      * Returns this regions position (north/south/east/west/center).
33738      * @return {String} 
33739      */
33740     getPosition: function(){
33741         return this.position;    
33742     }
33743 });/*
33744  * Based on:
33745  * Ext JS Library 1.1.1
33746  * Copyright(c) 2006-2007, Ext JS, LLC.
33747  *
33748  * Originally Released Under LGPL - original licence link has changed is not relivant.
33749  *
33750  * Fork - LGPL
33751  * <script type="text/javascript">
33752  */
33753  
33754 /**
33755  * @class Roo.bootstrap.layout.Region
33756  * @extends Roo.bootstrap.layout.Basic
33757  * This class represents a region in a layout manager.
33758  
33759  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33760  * @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})
33761  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33762  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33763  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33764  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33765  * @cfg {String}    title           The title for the region (overrides panel titles)
33766  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33767  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33768  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33769  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33770  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33771  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33772  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33773  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33774  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33775  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33776
33777  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33778  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33779  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33780  * @cfg {Number}    width           For East/West panels
33781  * @cfg {Number}    height          For North/South panels
33782  * @cfg {Boolean}   split           To show the splitter
33783  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33784  * 
33785  * @cfg {string}   cls             Extra CSS classes to add to region
33786  * 
33787  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33788  * @cfg {string}   region  the region that it inhabits..
33789  *
33790
33791  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33792  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33793
33794  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33795  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33796  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33797  */
33798 Roo.bootstrap.layout.Region = function(config)
33799 {
33800     this.applyConfig(config);
33801
33802     var mgr = config.mgr;
33803     var pos = config.region;
33804     config.skipConfig = true;
33805     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33806     
33807     if (mgr.el) {
33808         this.onRender(mgr.el);   
33809     }
33810      
33811     this.visible = true;
33812     this.collapsed = false;
33813     this.unrendered_panels = [];
33814 };
33815
33816 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33817
33818     position: '', // set by wrapper (eg. north/south etc..)
33819     unrendered_panels : null,  // unrendered panels.
33820     createBody : function(){
33821         /** This region's body element 
33822         * @type Roo.Element */
33823         this.bodyEl = this.el.createChild({
33824                 tag: "div",
33825                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33826         });
33827     },
33828
33829     onRender: function(ctr, pos)
33830     {
33831         var dh = Roo.DomHelper;
33832         /** This region's container element 
33833         * @type Roo.Element */
33834         this.el = dh.append(ctr.dom, {
33835                 tag: "div",
33836                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33837             }, true);
33838         /** This region's title element 
33839         * @type Roo.Element */
33840     
33841         this.titleEl = dh.append(this.el.dom,
33842             {
33843                     tag: "div",
33844                     unselectable: "on",
33845                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33846                     children:[
33847                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33848                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33849                     ]}, true);
33850         
33851         this.titleEl.enableDisplayMode();
33852         /** This region's title text element 
33853         * @type HTMLElement */
33854         this.titleTextEl = this.titleEl.dom.firstChild;
33855         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33856         /*
33857         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33858         this.closeBtn.enableDisplayMode();
33859         this.closeBtn.on("click", this.closeClicked, this);
33860         this.closeBtn.hide();
33861     */
33862         this.createBody(this.config);
33863         if(this.config.hideWhenEmpty){
33864             this.hide();
33865             this.on("paneladded", this.validateVisibility, this);
33866             this.on("panelremoved", this.validateVisibility, this);
33867         }
33868         if(this.autoScroll){
33869             this.bodyEl.setStyle("overflow", "auto");
33870         }else{
33871             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33872         }
33873         //if(c.titlebar !== false){
33874             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33875                 this.titleEl.hide();
33876             }else{
33877                 this.titleEl.show();
33878                 if(this.config.title){
33879                     this.titleTextEl.innerHTML = this.config.title;
33880                 }
33881             }
33882         //}
33883         if(this.config.collapsed){
33884             this.collapse(true);
33885         }
33886         if(this.config.hidden){
33887             this.hide();
33888         }
33889         
33890         if (this.unrendered_panels && this.unrendered_panels.length) {
33891             for (var i =0;i< this.unrendered_panels.length; i++) {
33892                 this.add(this.unrendered_panels[i]);
33893             }
33894             this.unrendered_panels = null;
33895             
33896         }
33897         
33898     },
33899     
33900     applyConfig : function(c)
33901     {
33902         /*
33903          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33904             var dh = Roo.DomHelper;
33905             if(c.titlebar !== false){
33906                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33907                 this.collapseBtn.on("click", this.collapse, this);
33908                 this.collapseBtn.enableDisplayMode();
33909                 /*
33910                 if(c.showPin === true || this.showPin){
33911                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33912                     this.stickBtn.enableDisplayMode();
33913                     this.stickBtn.on("click", this.expand, this);
33914                     this.stickBtn.hide();
33915                 }
33916                 
33917             }
33918             */
33919             /** This region's collapsed element
33920             * @type Roo.Element */
33921             /*
33922              *
33923             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33924                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33925             ]}, true);
33926             
33927             if(c.floatable !== false){
33928                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33929                this.collapsedEl.on("click", this.collapseClick, this);
33930             }
33931
33932             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33933                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33934                    id: "message", unselectable: "on", style:{"float":"left"}});
33935                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33936              }
33937             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33938             this.expandBtn.on("click", this.expand, this);
33939             
33940         }
33941         
33942         if(this.collapseBtn){
33943             this.collapseBtn.setVisible(c.collapsible == true);
33944         }
33945         
33946         this.cmargins = c.cmargins || this.cmargins ||
33947                          (this.position == "west" || this.position == "east" ?
33948                              {top: 0, left: 2, right:2, bottom: 0} :
33949                              {top: 2, left: 0, right:0, bottom: 2});
33950         */
33951         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33952         
33953         
33954         this.bottomTabs = c.tabPosition != "top";
33955         
33956         this.autoScroll = c.autoScroll || false;
33957         
33958         
33959        
33960         
33961         this.duration = c.duration || .30;
33962         this.slideDuration = c.slideDuration || .45;
33963         this.config = c;
33964        
33965     },
33966     /**
33967      * Returns true if this region is currently visible.
33968      * @return {Boolean}
33969      */
33970     isVisible : function(){
33971         return this.visible;
33972     },
33973
33974     /**
33975      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33976      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33977      */
33978     //setCollapsedTitle : function(title){
33979     //    title = title || "&#160;";
33980      //   if(this.collapsedTitleTextEl){
33981       //      this.collapsedTitleTextEl.innerHTML = title;
33982        // }
33983     //},
33984
33985     getBox : function(){
33986         var b;
33987       //  if(!this.collapsed){
33988             b = this.el.getBox(false, true);
33989        // }else{
33990           //  b = this.collapsedEl.getBox(false, true);
33991         //}
33992         return b;
33993     },
33994
33995     getMargins : function(){
33996         return this.margins;
33997         //return this.collapsed ? this.cmargins : this.margins;
33998     },
33999 /*
34000     highlight : function(){
34001         this.el.addClass("x-layout-panel-dragover");
34002     },
34003
34004     unhighlight : function(){
34005         this.el.removeClass("x-layout-panel-dragover");
34006     },
34007 */
34008     updateBox : function(box)
34009     {
34010         if (!this.bodyEl) {
34011             return; // not rendered yet..
34012         }
34013         
34014         this.box = box;
34015         if(!this.collapsed){
34016             this.el.dom.style.left = box.x + "px";
34017             this.el.dom.style.top = box.y + "px";
34018             this.updateBody(box.width, box.height);
34019         }else{
34020             this.collapsedEl.dom.style.left = box.x + "px";
34021             this.collapsedEl.dom.style.top = box.y + "px";
34022             this.collapsedEl.setSize(box.width, box.height);
34023         }
34024         if(this.tabs){
34025             this.tabs.autoSizeTabs();
34026         }
34027     },
34028
34029     updateBody : function(w, h)
34030     {
34031         if(w !== null){
34032             this.el.setWidth(w);
34033             w -= this.el.getBorderWidth("rl");
34034             if(this.config.adjustments){
34035                 w += this.config.adjustments[0];
34036             }
34037         }
34038         if(h !== null && h > 0){
34039             this.el.setHeight(h);
34040             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34041             h -= this.el.getBorderWidth("tb");
34042             if(this.config.adjustments){
34043                 h += this.config.adjustments[1];
34044             }
34045             this.bodyEl.setHeight(h);
34046             if(this.tabs){
34047                 h = this.tabs.syncHeight(h);
34048             }
34049         }
34050         if(this.panelSize){
34051             w = w !== null ? w : this.panelSize.width;
34052             h = h !== null ? h : this.panelSize.height;
34053         }
34054         if(this.activePanel){
34055             var el = this.activePanel.getEl();
34056             w = w !== null ? w : el.getWidth();
34057             h = h !== null ? h : el.getHeight();
34058             this.panelSize = {width: w, height: h};
34059             this.activePanel.setSize(w, h);
34060         }
34061         if(Roo.isIE && this.tabs){
34062             this.tabs.el.repaint();
34063         }
34064     },
34065
34066     /**
34067      * Returns the container element for this region.
34068      * @return {Roo.Element}
34069      */
34070     getEl : function(){
34071         return this.el;
34072     },
34073
34074     /**
34075      * Hides this region.
34076      */
34077     hide : function(){
34078         //if(!this.collapsed){
34079             this.el.dom.style.left = "-2000px";
34080             this.el.hide();
34081         //}else{
34082          //   this.collapsedEl.dom.style.left = "-2000px";
34083          //   this.collapsedEl.hide();
34084        // }
34085         this.visible = false;
34086         this.fireEvent("visibilitychange", this, false);
34087     },
34088
34089     /**
34090      * Shows this region if it was previously hidden.
34091      */
34092     show : function(){
34093         //if(!this.collapsed){
34094             this.el.show();
34095         //}else{
34096         //    this.collapsedEl.show();
34097        // }
34098         this.visible = true;
34099         this.fireEvent("visibilitychange", this, true);
34100     },
34101 /*
34102     closeClicked : function(){
34103         if(this.activePanel){
34104             this.remove(this.activePanel);
34105         }
34106     },
34107
34108     collapseClick : function(e){
34109         if(this.isSlid){
34110            e.stopPropagation();
34111            this.slideIn();
34112         }else{
34113            e.stopPropagation();
34114            this.slideOut();
34115         }
34116     },
34117 */
34118     /**
34119      * Collapses this region.
34120      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34121      */
34122     /*
34123     collapse : function(skipAnim, skipCheck = false){
34124         if(this.collapsed) {
34125             return;
34126         }
34127         
34128         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34129             
34130             this.collapsed = true;
34131             if(this.split){
34132                 this.split.el.hide();
34133             }
34134             if(this.config.animate && skipAnim !== true){
34135                 this.fireEvent("invalidated", this);
34136                 this.animateCollapse();
34137             }else{
34138                 this.el.setLocation(-20000,-20000);
34139                 this.el.hide();
34140                 this.collapsedEl.show();
34141                 this.fireEvent("collapsed", this);
34142                 this.fireEvent("invalidated", this);
34143             }
34144         }
34145         
34146     },
34147 */
34148     animateCollapse : function(){
34149         // overridden
34150     },
34151
34152     /**
34153      * Expands this region if it was previously collapsed.
34154      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34155      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34156      */
34157     /*
34158     expand : function(e, skipAnim){
34159         if(e) {
34160             e.stopPropagation();
34161         }
34162         if(!this.collapsed || this.el.hasActiveFx()) {
34163             return;
34164         }
34165         if(this.isSlid){
34166             this.afterSlideIn();
34167             skipAnim = true;
34168         }
34169         this.collapsed = false;
34170         if(this.config.animate && skipAnim !== true){
34171             this.animateExpand();
34172         }else{
34173             this.el.show();
34174             if(this.split){
34175                 this.split.el.show();
34176             }
34177             this.collapsedEl.setLocation(-2000,-2000);
34178             this.collapsedEl.hide();
34179             this.fireEvent("invalidated", this);
34180             this.fireEvent("expanded", this);
34181         }
34182     },
34183 */
34184     animateExpand : function(){
34185         // overridden
34186     },
34187
34188     initTabs : function()
34189     {
34190         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34191         
34192         var ts = new Roo.bootstrap.panel.Tabs({
34193                 el: this.bodyEl.dom,
34194                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34195                 disableTooltips: this.config.disableTabTips,
34196                 toolbar : this.config.toolbar
34197             });
34198         
34199         if(this.config.hideTabs){
34200             ts.stripWrap.setDisplayed(false);
34201         }
34202         this.tabs = ts;
34203         ts.resizeTabs = this.config.resizeTabs === true;
34204         ts.minTabWidth = this.config.minTabWidth || 40;
34205         ts.maxTabWidth = this.config.maxTabWidth || 250;
34206         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34207         ts.monitorResize = false;
34208         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34209         ts.bodyEl.addClass('roo-layout-tabs-body');
34210         this.panels.each(this.initPanelAsTab, this);
34211     },
34212
34213     initPanelAsTab : function(panel){
34214         var ti = this.tabs.addTab(
34215             panel.getEl().id,
34216             panel.getTitle(),
34217             null,
34218             this.config.closeOnTab && panel.isClosable(),
34219             panel.tpl
34220         );
34221         if(panel.tabTip !== undefined){
34222             ti.setTooltip(panel.tabTip);
34223         }
34224         ti.on("activate", function(){
34225               this.setActivePanel(panel);
34226         }, this);
34227         
34228         if(this.config.closeOnTab){
34229             ti.on("beforeclose", function(t, e){
34230                 e.cancel = true;
34231                 this.remove(panel);
34232             }, this);
34233         }
34234         
34235         panel.tabItem = ti;
34236         
34237         return ti;
34238     },
34239
34240     updatePanelTitle : function(panel, title)
34241     {
34242         if(this.activePanel == panel){
34243             this.updateTitle(title);
34244         }
34245         if(this.tabs){
34246             var ti = this.tabs.getTab(panel.getEl().id);
34247             ti.setText(title);
34248             if(panel.tabTip !== undefined){
34249                 ti.setTooltip(panel.tabTip);
34250             }
34251         }
34252     },
34253
34254     updateTitle : function(title){
34255         if(this.titleTextEl && !this.config.title){
34256             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34257         }
34258     },
34259
34260     setActivePanel : function(panel)
34261     {
34262         panel = this.getPanel(panel);
34263         if(this.activePanel && this.activePanel != panel){
34264             this.activePanel.setActiveState(false);
34265         }
34266         this.activePanel = panel;
34267         panel.setActiveState(true);
34268         if(this.panelSize){
34269             panel.setSize(this.panelSize.width, this.panelSize.height);
34270         }
34271         if(this.closeBtn){
34272             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34273         }
34274         this.updateTitle(panel.getTitle());
34275         if(this.tabs){
34276             this.fireEvent("invalidated", this);
34277         }
34278         this.fireEvent("panelactivated", this, panel);
34279     },
34280
34281     /**
34282      * Shows the specified panel.
34283      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34284      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34285      */
34286     showPanel : function(panel)
34287     {
34288         panel = this.getPanel(panel);
34289         if(panel){
34290             if(this.tabs){
34291                 var tab = this.tabs.getTab(panel.getEl().id);
34292                 if(tab.isHidden()){
34293                     this.tabs.unhideTab(tab.id);
34294                 }
34295                 tab.activate();
34296             }else{
34297                 this.setActivePanel(panel);
34298             }
34299         }
34300         return panel;
34301     },
34302
34303     /**
34304      * Get the active panel for this region.
34305      * @return {Roo.ContentPanel} The active panel or null
34306      */
34307     getActivePanel : function(){
34308         return this.activePanel;
34309     },
34310
34311     validateVisibility : function(){
34312         if(this.panels.getCount() < 1){
34313             this.updateTitle("&#160;");
34314             this.closeBtn.hide();
34315             this.hide();
34316         }else{
34317             if(!this.isVisible()){
34318                 this.show();
34319             }
34320         }
34321     },
34322
34323     /**
34324      * Adds the passed ContentPanel(s) to this region.
34325      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34326      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34327      */
34328     add : function(panel)
34329     {
34330         if(arguments.length > 1){
34331             for(var i = 0, len = arguments.length; i < len; i++) {
34332                 this.add(arguments[i]);
34333             }
34334             return null;
34335         }
34336         
34337         // if we have not been rendered yet, then we can not really do much of this..
34338         if (!this.bodyEl) {
34339             this.unrendered_panels.push(panel);
34340             return panel;
34341         }
34342         
34343         
34344         
34345         
34346         if(this.hasPanel(panel)){
34347             this.showPanel(panel);
34348             return panel;
34349         }
34350         panel.setRegion(this);
34351         this.panels.add(panel);
34352        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34353             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34354             // and hide them... ???
34355             this.bodyEl.dom.appendChild(panel.getEl().dom);
34356             if(panel.background !== true){
34357                 this.setActivePanel(panel);
34358             }
34359             this.fireEvent("paneladded", this, panel);
34360             return panel;
34361         }
34362         */
34363         if(!this.tabs){
34364             this.initTabs();
34365         }else{
34366             this.initPanelAsTab(panel);
34367         }
34368         
34369         
34370         if(panel.background !== true){
34371             this.tabs.activate(panel.getEl().id);
34372         }
34373         this.fireEvent("paneladded", this, panel);
34374         return panel;
34375     },
34376
34377     /**
34378      * Hides the tab for the specified panel.
34379      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34380      */
34381     hidePanel : function(panel){
34382         if(this.tabs && (panel = this.getPanel(panel))){
34383             this.tabs.hideTab(panel.getEl().id);
34384         }
34385     },
34386
34387     /**
34388      * Unhides the tab for a previously hidden panel.
34389      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34390      */
34391     unhidePanel : function(panel){
34392         if(this.tabs && (panel = this.getPanel(panel))){
34393             this.tabs.unhideTab(panel.getEl().id);
34394         }
34395     },
34396
34397     clearPanels : function(){
34398         while(this.panels.getCount() > 0){
34399              this.remove(this.panels.first());
34400         }
34401     },
34402
34403     /**
34404      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34405      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34406      * @param {Boolean} preservePanel Overrides the config preservePanel option
34407      * @return {Roo.ContentPanel} The panel that was removed
34408      */
34409     remove : function(panel, preservePanel)
34410     {
34411         panel = this.getPanel(panel);
34412         if(!panel){
34413             return null;
34414         }
34415         var e = {};
34416         this.fireEvent("beforeremove", this, panel, e);
34417         if(e.cancel === true){
34418             return null;
34419         }
34420         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34421         var panelId = panel.getId();
34422         this.panels.removeKey(panelId);
34423         if(preservePanel){
34424             document.body.appendChild(panel.getEl().dom);
34425         }
34426         if(this.tabs){
34427             this.tabs.removeTab(panel.getEl().id);
34428         }else if (!preservePanel){
34429             this.bodyEl.dom.removeChild(panel.getEl().dom);
34430         }
34431         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34432             var p = this.panels.first();
34433             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34434             tempEl.appendChild(p.getEl().dom);
34435             this.bodyEl.update("");
34436             this.bodyEl.dom.appendChild(p.getEl().dom);
34437             tempEl = null;
34438             this.updateTitle(p.getTitle());
34439             this.tabs = null;
34440             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34441             this.setActivePanel(p);
34442         }
34443         panel.setRegion(null);
34444         if(this.activePanel == panel){
34445             this.activePanel = null;
34446         }
34447         if(this.config.autoDestroy !== false && preservePanel !== true){
34448             try{panel.destroy();}catch(e){}
34449         }
34450         this.fireEvent("panelremoved", this, panel);
34451         return panel;
34452     },
34453
34454     /**
34455      * Returns the TabPanel component used by this region
34456      * @return {Roo.TabPanel}
34457      */
34458     getTabs : function(){
34459         return this.tabs;
34460     },
34461
34462     createTool : function(parentEl, className){
34463         var btn = Roo.DomHelper.append(parentEl, {
34464             tag: "div",
34465             cls: "x-layout-tools-button",
34466             children: [ {
34467                 tag: "div",
34468                 cls: "roo-layout-tools-button-inner " + className,
34469                 html: "&#160;"
34470             }]
34471         }, true);
34472         btn.addClassOnOver("roo-layout-tools-button-over");
34473         return btn;
34474     }
34475 });/*
34476  * Based on:
34477  * Ext JS Library 1.1.1
34478  * Copyright(c) 2006-2007, Ext JS, LLC.
34479  *
34480  * Originally Released Under LGPL - original licence link has changed is not relivant.
34481  *
34482  * Fork - LGPL
34483  * <script type="text/javascript">
34484  */
34485  
34486
34487
34488 /**
34489  * @class Roo.SplitLayoutRegion
34490  * @extends Roo.LayoutRegion
34491  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34492  */
34493 Roo.bootstrap.layout.Split = function(config){
34494     this.cursor = config.cursor;
34495     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34496 };
34497
34498 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34499 {
34500     splitTip : "Drag to resize.",
34501     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34502     useSplitTips : false,
34503
34504     applyConfig : function(config){
34505         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34506     },
34507     
34508     onRender : function(ctr,pos) {
34509         
34510         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34511         if(!this.config.split){
34512             return;
34513         }
34514         if(!this.split){
34515             
34516             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34517                             tag: "div",
34518                             id: this.el.id + "-split",
34519                             cls: "roo-layout-split roo-layout-split-"+this.position,
34520                             html: "&#160;"
34521             });
34522             /** The SplitBar for this region 
34523             * @type Roo.SplitBar */
34524             // does not exist yet...
34525             Roo.log([this.position, this.orientation]);
34526             
34527             this.split = new Roo.bootstrap.SplitBar({
34528                 dragElement : splitEl,
34529                 resizingElement: this.el,
34530                 orientation : this.orientation
34531             });
34532             
34533             this.split.on("moved", this.onSplitMove, this);
34534             this.split.useShim = this.config.useShim === true;
34535             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34536             if(this.useSplitTips){
34537                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34538             }
34539             //if(config.collapsible){
34540             //    this.split.el.on("dblclick", this.collapse,  this);
34541             //}
34542         }
34543         if(typeof this.config.minSize != "undefined"){
34544             this.split.minSize = this.config.minSize;
34545         }
34546         if(typeof this.config.maxSize != "undefined"){
34547             this.split.maxSize = this.config.maxSize;
34548         }
34549         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34550             this.hideSplitter();
34551         }
34552         
34553     },
34554
34555     getHMaxSize : function(){
34556          var cmax = this.config.maxSize || 10000;
34557          var center = this.mgr.getRegion("center");
34558          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34559     },
34560
34561     getVMaxSize : function(){
34562          var cmax = this.config.maxSize || 10000;
34563          var center = this.mgr.getRegion("center");
34564          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34565     },
34566
34567     onSplitMove : function(split, newSize){
34568         this.fireEvent("resized", this, newSize);
34569     },
34570     
34571     /** 
34572      * Returns the {@link Roo.SplitBar} for this region.
34573      * @return {Roo.SplitBar}
34574      */
34575     getSplitBar : function(){
34576         return this.split;
34577     },
34578     
34579     hide : function(){
34580         this.hideSplitter();
34581         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34582     },
34583
34584     hideSplitter : function(){
34585         if(this.split){
34586             this.split.el.setLocation(-2000,-2000);
34587             this.split.el.hide();
34588         }
34589     },
34590
34591     show : function(){
34592         if(this.split){
34593             this.split.el.show();
34594         }
34595         Roo.bootstrap.layout.Split.superclass.show.call(this);
34596     },
34597     
34598     beforeSlide: function(){
34599         if(Roo.isGecko){// firefox overflow auto bug workaround
34600             this.bodyEl.clip();
34601             if(this.tabs) {
34602                 this.tabs.bodyEl.clip();
34603             }
34604             if(this.activePanel){
34605                 this.activePanel.getEl().clip();
34606                 
34607                 if(this.activePanel.beforeSlide){
34608                     this.activePanel.beforeSlide();
34609                 }
34610             }
34611         }
34612     },
34613     
34614     afterSlide : function(){
34615         if(Roo.isGecko){// firefox overflow auto bug workaround
34616             this.bodyEl.unclip();
34617             if(this.tabs) {
34618                 this.tabs.bodyEl.unclip();
34619             }
34620             if(this.activePanel){
34621                 this.activePanel.getEl().unclip();
34622                 if(this.activePanel.afterSlide){
34623                     this.activePanel.afterSlide();
34624                 }
34625             }
34626         }
34627     },
34628
34629     initAutoHide : function(){
34630         if(this.autoHide !== false){
34631             if(!this.autoHideHd){
34632                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34633                 this.autoHideHd = {
34634                     "mouseout": function(e){
34635                         if(!e.within(this.el, true)){
34636                             st.delay(500);
34637                         }
34638                     },
34639                     "mouseover" : function(e){
34640                         st.cancel();
34641                     },
34642                     scope : this
34643                 };
34644             }
34645             this.el.on(this.autoHideHd);
34646         }
34647     },
34648
34649     clearAutoHide : function(){
34650         if(this.autoHide !== false){
34651             this.el.un("mouseout", this.autoHideHd.mouseout);
34652             this.el.un("mouseover", this.autoHideHd.mouseover);
34653         }
34654     },
34655
34656     clearMonitor : function(){
34657         Roo.get(document).un("click", this.slideInIf, this);
34658     },
34659
34660     // these names are backwards but not changed for compat
34661     slideOut : function(){
34662         if(this.isSlid || this.el.hasActiveFx()){
34663             return;
34664         }
34665         this.isSlid = true;
34666         if(this.collapseBtn){
34667             this.collapseBtn.hide();
34668         }
34669         this.closeBtnState = this.closeBtn.getStyle('display');
34670         this.closeBtn.hide();
34671         if(this.stickBtn){
34672             this.stickBtn.show();
34673         }
34674         this.el.show();
34675         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34676         this.beforeSlide();
34677         this.el.setStyle("z-index", 10001);
34678         this.el.slideIn(this.getSlideAnchor(), {
34679             callback: function(){
34680                 this.afterSlide();
34681                 this.initAutoHide();
34682                 Roo.get(document).on("click", this.slideInIf, this);
34683                 this.fireEvent("slideshow", this);
34684             },
34685             scope: this,
34686             block: true
34687         });
34688     },
34689
34690     afterSlideIn : function(){
34691         this.clearAutoHide();
34692         this.isSlid = false;
34693         this.clearMonitor();
34694         this.el.setStyle("z-index", "");
34695         if(this.collapseBtn){
34696             this.collapseBtn.show();
34697         }
34698         this.closeBtn.setStyle('display', this.closeBtnState);
34699         if(this.stickBtn){
34700             this.stickBtn.hide();
34701         }
34702         this.fireEvent("slidehide", this);
34703     },
34704
34705     slideIn : function(cb){
34706         if(!this.isSlid || this.el.hasActiveFx()){
34707             Roo.callback(cb);
34708             return;
34709         }
34710         this.isSlid = false;
34711         this.beforeSlide();
34712         this.el.slideOut(this.getSlideAnchor(), {
34713             callback: function(){
34714                 this.el.setLeftTop(-10000, -10000);
34715                 this.afterSlide();
34716                 this.afterSlideIn();
34717                 Roo.callback(cb);
34718             },
34719             scope: this,
34720             block: true
34721         });
34722     },
34723     
34724     slideInIf : function(e){
34725         if(!e.within(this.el)){
34726             this.slideIn();
34727         }
34728     },
34729
34730     animateCollapse : function(){
34731         this.beforeSlide();
34732         this.el.setStyle("z-index", 20000);
34733         var anchor = this.getSlideAnchor();
34734         this.el.slideOut(anchor, {
34735             callback : function(){
34736                 this.el.setStyle("z-index", "");
34737                 this.collapsedEl.slideIn(anchor, {duration:.3});
34738                 this.afterSlide();
34739                 this.el.setLocation(-10000,-10000);
34740                 this.el.hide();
34741                 this.fireEvent("collapsed", this);
34742             },
34743             scope: this,
34744             block: true
34745         });
34746     },
34747
34748     animateExpand : function(){
34749         this.beforeSlide();
34750         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34751         this.el.setStyle("z-index", 20000);
34752         this.collapsedEl.hide({
34753             duration:.1
34754         });
34755         this.el.slideIn(this.getSlideAnchor(), {
34756             callback : function(){
34757                 this.el.setStyle("z-index", "");
34758                 this.afterSlide();
34759                 if(this.split){
34760                     this.split.el.show();
34761                 }
34762                 this.fireEvent("invalidated", this);
34763                 this.fireEvent("expanded", this);
34764             },
34765             scope: this,
34766             block: true
34767         });
34768     },
34769
34770     anchors : {
34771         "west" : "left",
34772         "east" : "right",
34773         "north" : "top",
34774         "south" : "bottom"
34775     },
34776
34777     sanchors : {
34778         "west" : "l",
34779         "east" : "r",
34780         "north" : "t",
34781         "south" : "b"
34782     },
34783
34784     canchors : {
34785         "west" : "tl-tr",
34786         "east" : "tr-tl",
34787         "north" : "tl-bl",
34788         "south" : "bl-tl"
34789     },
34790
34791     getAnchor : function(){
34792         return this.anchors[this.position];
34793     },
34794
34795     getCollapseAnchor : function(){
34796         return this.canchors[this.position];
34797     },
34798
34799     getSlideAnchor : function(){
34800         return this.sanchors[this.position];
34801     },
34802
34803     getAlignAdj : function(){
34804         var cm = this.cmargins;
34805         switch(this.position){
34806             case "west":
34807                 return [0, 0];
34808             break;
34809             case "east":
34810                 return [0, 0];
34811             break;
34812             case "north":
34813                 return [0, 0];
34814             break;
34815             case "south":
34816                 return [0, 0];
34817             break;
34818         }
34819     },
34820
34821     getExpandAdj : function(){
34822         var c = this.collapsedEl, cm = this.cmargins;
34823         switch(this.position){
34824             case "west":
34825                 return [-(cm.right+c.getWidth()+cm.left), 0];
34826             break;
34827             case "east":
34828                 return [cm.right+c.getWidth()+cm.left, 0];
34829             break;
34830             case "north":
34831                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34832             break;
34833             case "south":
34834                 return [0, cm.top+cm.bottom+c.getHeight()];
34835             break;
34836         }
34837     }
34838 });/*
34839  * Based on:
34840  * Ext JS Library 1.1.1
34841  * Copyright(c) 2006-2007, Ext JS, LLC.
34842  *
34843  * Originally Released Under LGPL - original licence link has changed is not relivant.
34844  *
34845  * Fork - LGPL
34846  * <script type="text/javascript">
34847  */
34848 /*
34849  * These classes are private internal classes
34850  */
34851 Roo.bootstrap.layout.Center = function(config){
34852     config.region = "center";
34853     Roo.bootstrap.layout.Region.call(this, config);
34854     this.visible = true;
34855     this.minWidth = config.minWidth || 20;
34856     this.minHeight = config.minHeight || 20;
34857 };
34858
34859 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34860     hide : function(){
34861         // center panel can't be hidden
34862     },
34863     
34864     show : function(){
34865         // center panel can't be hidden
34866     },
34867     
34868     getMinWidth: function(){
34869         return this.minWidth;
34870     },
34871     
34872     getMinHeight: function(){
34873         return this.minHeight;
34874     }
34875 });
34876
34877
34878
34879
34880  
34881
34882
34883
34884
34885
34886 Roo.bootstrap.layout.North = function(config)
34887 {
34888     config.region = 'north';
34889     config.cursor = 'n-resize';
34890     
34891     Roo.bootstrap.layout.Split.call(this, config);
34892     
34893     
34894     if(this.split){
34895         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34896         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34897         this.split.el.addClass("roo-layout-split-v");
34898     }
34899     var size = config.initialSize || config.height;
34900     if(typeof size != "undefined"){
34901         this.el.setHeight(size);
34902     }
34903 };
34904 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34905 {
34906     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34907     
34908     
34909     
34910     getBox : function(){
34911         if(this.collapsed){
34912             return this.collapsedEl.getBox();
34913         }
34914         var box = this.el.getBox();
34915         if(this.split){
34916             box.height += this.split.el.getHeight();
34917         }
34918         return box;
34919     },
34920     
34921     updateBox : function(box){
34922         if(this.split && !this.collapsed){
34923             box.height -= this.split.el.getHeight();
34924             this.split.el.setLeft(box.x);
34925             this.split.el.setTop(box.y+box.height);
34926             this.split.el.setWidth(box.width);
34927         }
34928         if(this.collapsed){
34929             this.updateBody(box.width, null);
34930         }
34931         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34932     }
34933 });
34934
34935
34936
34937
34938
34939 Roo.bootstrap.layout.South = function(config){
34940     config.region = 'south';
34941     config.cursor = 's-resize';
34942     Roo.bootstrap.layout.Split.call(this, config);
34943     if(this.split){
34944         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34945         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34946         this.split.el.addClass("roo-layout-split-v");
34947     }
34948     var size = config.initialSize || config.height;
34949     if(typeof size != "undefined"){
34950         this.el.setHeight(size);
34951     }
34952 };
34953
34954 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34955     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34956     getBox : function(){
34957         if(this.collapsed){
34958             return this.collapsedEl.getBox();
34959         }
34960         var box = this.el.getBox();
34961         if(this.split){
34962             var sh = this.split.el.getHeight();
34963             box.height += sh;
34964             box.y -= sh;
34965         }
34966         return box;
34967     },
34968     
34969     updateBox : function(box){
34970         if(this.split && !this.collapsed){
34971             var sh = this.split.el.getHeight();
34972             box.height -= sh;
34973             box.y += sh;
34974             this.split.el.setLeft(box.x);
34975             this.split.el.setTop(box.y-sh);
34976             this.split.el.setWidth(box.width);
34977         }
34978         if(this.collapsed){
34979             this.updateBody(box.width, null);
34980         }
34981         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34982     }
34983 });
34984
34985 Roo.bootstrap.layout.East = function(config){
34986     config.region = "east";
34987     config.cursor = "e-resize";
34988     Roo.bootstrap.layout.Split.call(this, config);
34989     if(this.split){
34990         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34991         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34992         this.split.el.addClass("roo-layout-split-h");
34993     }
34994     var size = config.initialSize || config.width;
34995     if(typeof size != "undefined"){
34996         this.el.setWidth(size);
34997     }
34998 };
34999 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35000     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35001     getBox : function(){
35002         if(this.collapsed){
35003             return this.collapsedEl.getBox();
35004         }
35005         var box = this.el.getBox();
35006         if(this.split){
35007             var sw = this.split.el.getWidth();
35008             box.width += sw;
35009             box.x -= sw;
35010         }
35011         return box;
35012     },
35013
35014     updateBox : function(box){
35015         if(this.split && !this.collapsed){
35016             var sw = this.split.el.getWidth();
35017             box.width -= sw;
35018             this.split.el.setLeft(box.x);
35019             this.split.el.setTop(box.y);
35020             this.split.el.setHeight(box.height);
35021             box.x += sw;
35022         }
35023         if(this.collapsed){
35024             this.updateBody(null, box.height);
35025         }
35026         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35027     }
35028 });
35029
35030 Roo.bootstrap.layout.West = function(config){
35031     config.region = "west";
35032     config.cursor = "w-resize";
35033     
35034     Roo.bootstrap.layout.Split.call(this, config);
35035     if(this.split){
35036         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
35037         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35038         this.split.el.addClass("roo-layout-split-h");
35039     }
35040     
35041 };
35042 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
35043     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35044     
35045     onRender: function(ctr, pos)
35046     {
35047         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
35048         var size = this.config.initialSize || this.config.width;
35049         if(typeof size != "undefined"){
35050             this.el.setWidth(size);
35051         }
35052     },
35053     
35054     getBox : function(){
35055         if(this.collapsed){
35056             return this.collapsedEl.getBox();
35057         }
35058         var box = this.el.getBox();
35059         if(this.split){
35060             box.width += this.split.el.getWidth();
35061         }
35062         return box;
35063     },
35064     
35065     updateBox : function(box){
35066         if(this.split && !this.collapsed){
35067             var sw = this.split.el.getWidth();
35068             box.width -= sw;
35069             this.split.el.setLeft(box.x+box.width);
35070             this.split.el.setTop(box.y);
35071             this.split.el.setHeight(box.height);
35072         }
35073         if(this.collapsed){
35074             this.updateBody(null, box.height);
35075         }
35076         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35077     }
35078 });
35079 Roo.namespace("Roo.bootstrap.panel");/*
35080  * Based on:
35081  * Ext JS Library 1.1.1
35082  * Copyright(c) 2006-2007, Ext JS, LLC.
35083  *
35084  * Originally Released Under LGPL - original licence link has changed is not relivant.
35085  *
35086  * Fork - LGPL
35087  * <script type="text/javascript">
35088  */
35089 /**
35090  * @class Roo.ContentPanel
35091  * @extends Roo.util.Observable
35092  * A basic ContentPanel element.
35093  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35094  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35095  * @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
35096  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35097  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35098  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35099  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35100  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35101  * @cfg {String} title          The title for this panel
35102  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35103  * @cfg {String} url            Calls {@link #setUrl} with this value
35104  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35105  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35106  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35107  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35108  * @cfg {Boolean} badges render the badges
35109
35110  * @constructor
35111  * Create a new ContentPanel.
35112  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35113  * @param {String/Object} config A string to set only the title or a config object
35114  * @param {String} content (optional) Set the HTML content for this panel
35115  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35116  */
35117 Roo.bootstrap.panel.Content = function( config){
35118     
35119     this.tpl = config.tpl || false;
35120     
35121     var el = config.el;
35122     var content = config.content;
35123
35124     if(config.autoCreate){ // xtype is available if this is called from factory
35125         el = Roo.id();
35126     }
35127     this.el = Roo.get(el);
35128     if(!this.el && config && config.autoCreate){
35129         if(typeof config.autoCreate == "object"){
35130             if(!config.autoCreate.id){
35131                 config.autoCreate.id = config.id||el;
35132             }
35133             this.el = Roo.DomHelper.append(document.body,
35134                         config.autoCreate, true);
35135         }else{
35136             var elcfg =  {   tag: "div",
35137                             cls: "roo-layout-inactive-content",
35138                             id: config.id||el
35139                             };
35140             if (config.html) {
35141                 elcfg.html = config.html;
35142                 
35143             }
35144                         
35145             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35146         }
35147     } 
35148     this.closable = false;
35149     this.loaded = false;
35150     this.active = false;
35151    
35152       
35153     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35154         
35155         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35156         
35157         this.wrapEl = this.el; //this.el.wrap();
35158         var ti = [];
35159         if (config.toolbar.items) {
35160             ti = config.toolbar.items ;
35161             delete config.toolbar.items ;
35162         }
35163         
35164         var nitems = [];
35165         this.toolbar.render(this.wrapEl, 'before');
35166         for(var i =0;i < ti.length;i++) {
35167           //  Roo.log(['add child', items[i]]);
35168             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35169         }
35170         this.toolbar.items = nitems;
35171         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35172         delete config.toolbar;
35173         
35174     }
35175     /*
35176     // xtype created footer. - not sure if will work as we normally have to render first..
35177     if (this.footer && !this.footer.el && this.footer.xtype) {
35178         if (!this.wrapEl) {
35179             this.wrapEl = this.el.wrap();
35180         }
35181     
35182         this.footer.container = this.wrapEl.createChild();
35183          
35184         this.footer = Roo.factory(this.footer, Roo);
35185         
35186     }
35187     */
35188     
35189      if(typeof config == "string"){
35190         this.title = config;
35191     }else{
35192         Roo.apply(this, config);
35193     }
35194     
35195     if(this.resizeEl){
35196         this.resizeEl = Roo.get(this.resizeEl, true);
35197     }else{
35198         this.resizeEl = this.el;
35199     }
35200     // handle view.xtype
35201     
35202  
35203     
35204     
35205     this.addEvents({
35206         /**
35207          * @event activate
35208          * Fires when this panel is activated. 
35209          * @param {Roo.ContentPanel} this
35210          */
35211         "activate" : true,
35212         /**
35213          * @event deactivate
35214          * Fires when this panel is activated. 
35215          * @param {Roo.ContentPanel} this
35216          */
35217         "deactivate" : true,
35218
35219         /**
35220          * @event resize
35221          * Fires when this panel is resized if fitToFrame is true.
35222          * @param {Roo.ContentPanel} this
35223          * @param {Number} width The width after any component adjustments
35224          * @param {Number} height The height after any component adjustments
35225          */
35226         "resize" : true,
35227         
35228          /**
35229          * @event render
35230          * Fires when this tab is created
35231          * @param {Roo.ContentPanel} this
35232          */
35233         "render" : true
35234         
35235         
35236         
35237     });
35238     
35239
35240     
35241     
35242     if(this.autoScroll){
35243         this.resizeEl.setStyle("overflow", "auto");
35244     } else {
35245         // fix randome scrolling
35246         //this.el.on('scroll', function() {
35247         //    Roo.log('fix random scolling');
35248         //    this.scrollTo('top',0); 
35249         //});
35250     }
35251     content = content || this.content;
35252     if(content){
35253         this.setContent(content);
35254     }
35255     if(config && config.url){
35256         this.setUrl(this.url, this.params, this.loadOnce);
35257     }
35258     
35259     
35260     
35261     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35262     
35263     if (this.view && typeof(this.view.xtype) != 'undefined') {
35264         this.view.el = this.el.appendChild(document.createElement("div"));
35265         this.view = Roo.factory(this.view); 
35266         this.view.render  &&  this.view.render(false, '');  
35267     }
35268     
35269     
35270     this.fireEvent('render', this);
35271 };
35272
35273 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35274     
35275     tabTip : '',
35276     
35277     setRegion : function(region){
35278         this.region = region;
35279         this.setActiveClass(region && !this.background);
35280     },
35281     
35282     
35283     setActiveClass: function(state)
35284     {
35285         if(state){
35286            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35287            this.el.setStyle('position','relative');
35288         }else{
35289            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35290            this.el.setStyle('position', 'absolute');
35291         } 
35292     },
35293     
35294     /**
35295      * Returns the toolbar for this Panel if one was configured. 
35296      * @return {Roo.Toolbar} 
35297      */
35298     getToolbar : function(){
35299         return this.toolbar;
35300     },
35301     
35302     setActiveState : function(active)
35303     {
35304         this.active = active;
35305         this.setActiveClass(active);
35306         if(!active){
35307             this.fireEvent("deactivate", this);
35308         }else{
35309             this.fireEvent("activate", this);
35310         }
35311     },
35312     /**
35313      * Updates this panel's element
35314      * @param {String} content The new content
35315      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35316     */
35317     setContent : function(content, loadScripts){
35318         this.el.update(content, loadScripts);
35319     },
35320
35321     ignoreResize : function(w, h){
35322         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35323             return true;
35324         }else{
35325             this.lastSize = {width: w, height: h};
35326             return false;
35327         }
35328     },
35329     /**
35330      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35331      * @return {Roo.UpdateManager} The UpdateManager
35332      */
35333     getUpdateManager : function(){
35334         return this.el.getUpdateManager();
35335     },
35336      /**
35337      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35338      * @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:
35339 <pre><code>
35340 panel.load({
35341     url: "your-url.php",
35342     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35343     callback: yourFunction,
35344     scope: yourObject, //(optional scope)
35345     discardUrl: false,
35346     nocache: false,
35347     text: "Loading...",
35348     timeout: 30,
35349     scripts: false
35350 });
35351 </code></pre>
35352      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35353      * 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.
35354      * @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}
35355      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35356      * @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.
35357      * @return {Roo.ContentPanel} this
35358      */
35359     load : function(){
35360         var um = this.el.getUpdateManager();
35361         um.update.apply(um, arguments);
35362         return this;
35363     },
35364
35365
35366     /**
35367      * 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.
35368      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35369      * @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)
35370      * @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)
35371      * @return {Roo.UpdateManager} The UpdateManager
35372      */
35373     setUrl : function(url, params, loadOnce){
35374         if(this.refreshDelegate){
35375             this.removeListener("activate", this.refreshDelegate);
35376         }
35377         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35378         this.on("activate", this.refreshDelegate);
35379         return this.el.getUpdateManager();
35380     },
35381     
35382     _handleRefresh : function(url, params, loadOnce){
35383         if(!loadOnce || !this.loaded){
35384             var updater = this.el.getUpdateManager();
35385             updater.update(url, params, this._setLoaded.createDelegate(this));
35386         }
35387     },
35388     
35389     _setLoaded : function(){
35390         this.loaded = true;
35391     }, 
35392     
35393     /**
35394      * Returns this panel's id
35395      * @return {String} 
35396      */
35397     getId : function(){
35398         return this.el.id;
35399     },
35400     
35401     /** 
35402      * Returns this panel's element - used by regiosn to add.
35403      * @return {Roo.Element} 
35404      */
35405     getEl : function(){
35406         return this.wrapEl || this.el;
35407     },
35408     
35409    
35410     
35411     adjustForComponents : function(width, height)
35412     {
35413         //Roo.log('adjustForComponents ');
35414         if(this.resizeEl != this.el){
35415             width -= this.el.getFrameWidth('lr');
35416             height -= this.el.getFrameWidth('tb');
35417         }
35418         if(this.toolbar){
35419             var te = this.toolbar.getEl();
35420             height -= te.getHeight();
35421             te.setWidth(width);
35422         }
35423         if(this.footer){
35424             var te = this.footer.getEl();
35425             Roo.log("footer:" + te.getHeight());
35426             
35427             height -= te.getHeight();
35428             te.setWidth(width);
35429         }
35430         
35431         
35432         if(this.adjustments){
35433             width += this.adjustments[0];
35434             height += this.adjustments[1];
35435         }
35436         return {"width": width, "height": height};
35437     },
35438     
35439     setSize : function(width, height){
35440         if(this.fitToFrame && !this.ignoreResize(width, height)){
35441             if(this.fitContainer && this.resizeEl != this.el){
35442                 this.el.setSize(width, height);
35443             }
35444             var size = this.adjustForComponents(width, height);
35445             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35446             this.fireEvent('resize', this, size.width, size.height);
35447         }
35448     },
35449     
35450     /**
35451      * Returns this panel's title
35452      * @return {String} 
35453      */
35454     getTitle : function(){
35455         return this.title;
35456     },
35457     
35458     /**
35459      * Set this panel's title
35460      * @param {String} title
35461      */
35462     setTitle : function(title){
35463         this.title = title;
35464         if(this.region){
35465             this.region.updatePanelTitle(this, title);
35466         }
35467     },
35468     
35469     /**
35470      * Returns true is this panel was configured to be closable
35471      * @return {Boolean} 
35472      */
35473     isClosable : function(){
35474         return this.closable;
35475     },
35476     
35477     beforeSlide : function(){
35478         this.el.clip();
35479         this.resizeEl.clip();
35480     },
35481     
35482     afterSlide : function(){
35483         this.el.unclip();
35484         this.resizeEl.unclip();
35485     },
35486     
35487     /**
35488      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35489      *   Will fail silently if the {@link #setUrl} method has not been called.
35490      *   This does not activate the panel, just updates its content.
35491      */
35492     refresh : function(){
35493         if(this.refreshDelegate){
35494            this.loaded = false;
35495            this.refreshDelegate();
35496         }
35497     },
35498     
35499     /**
35500      * Destroys this panel
35501      */
35502     destroy : function(){
35503         this.el.removeAllListeners();
35504         var tempEl = document.createElement("span");
35505         tempEl.appendChild(this.el.dom);
35506         tempEl.innerHTML = "";
35507         this.el.remove();
35508         this.el = null;
35509     },
35510     
35511     /**
35512      * form - if the content panel contains a form - this is a reference to it.
35513      * @type {Roo.form.Form}
35514      */
35515     form : false,
35516     /**
35517      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35518      *    This contains a reference to it.
35519      * @type {Roo.View}
35520      */
35521     view : false,
35522     
35523       /**
35524      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35525      * <pre><code>
35526
35527 layout.addxtype({
35528        xtype : 'Form',
35529        items: [ .... ]
35530    }
35531 );
35532
35533 </code></pre>
35534      * @param {Object} cfg Xtype definition of item to add.
35535      */
35536     
35537     
35538     getChildContainer: function () {
35539         return this.getEl();
35540     }
35541     
35542     
35543     /*
35544         var  ret = new Roo.factory(cfg);
35545         return ret;
35546         
35547         
35548         // add form..
35549         if (cfg.xtype.match(/^Form$/)) {
35550             
35551             var el;
35552             //if (this.footer) {
35553             //    el = this.footer.container.insertSibling(false, 'before');
35554             //} else {
35555                 el = this.el.createChild();
35556             //}
35557
35558             this.form = new  Roo.form.Form(cfg);
35559             
35560             
35561             if ( this.form.allItems.length) {
35562                 this.form.render(el.dom);
35563             }
35564             return this.form;
35565         }
35566         // should only have one of theses..
35567         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35568             // views.. should not be just added - used named prop 'view''
35569             
35570             cfg.el = this.el.appendChild(document.createElement("div"));
35571             // factory?
35572             
35573             var ret = new Roo.factory(cfg);
35574              
35575              ret.render && ret.render(false, ''); // render blank..
35576             this.view = ret;
35577             return ret;
35578         }
35579         return false;
35580     }
35581     \*/
35582 });
35583  
35584 /**
35585  * @class Roo.bootstrap.panel.Grid
35586  * @extends Roo.bootstrap.panel.Content
35587  * @constructor
35588  * Create a new GridPanel.
35589  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35590  * @param {Object} config A the config object
35591   
35592  */
35593
35594
35595
35596 Roo.bootstrap.panel.Grid = function(config)
35597 {
35598     
35599       
35600     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35601         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35602
35603     config.el = this.wrapper;
35604     //this.el = this.wrapper;
35605     
35606       if (config.container) {
35607         // ctor'ed from a Border/panel.grid
35608         
35609         
35610         this.wrapper.setStyle("overflow", "hidden");
35611         this.wrapper.addClass('roo-grid-container');
35612
35613     }
35614     
35615     
35616     if(config.toolbar){
35617         var tool_el = this.wrapper.createChild();    
35618         this.toolbar = Roo.factory(config.toolbar);
35619         var ti = [];
35620         if (config.toolbar.items) {
35621             ti = config.toolbar.items ;
35622             delete config.toolbar.items ;
35623         }
35624         
35625         var nitems = [];
35626         this.toolbar.render(tool_el);
35627         for(var i =0;i < ti.length;i++) {
35628           //  Roo.log(['add child', items[i]]);
35629             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35630         }
35631         this.toolbar.items = nitems;
35632         
35633         delete config.toolbar;
35634     }
35635     
35636     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35637     config.grid.scrollBody = true;;
35638     config.grid.monitorWindowResize = false; // turn off autosizing
35639     config.grid.autoHeight = false;
35640     config.grid.autoWidth = false;
35641     
35642     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35643     
35644     if (config.background) {
35645         // render grid on panel activation (if panel background)
35646         this.on('activate', function(gp) {
35647             if (!gp.grid.rendered) {
35648                 gp.grid.render(this.wrapper);
35649                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35650             }
35651         });
35652             
35653     } else {
35654         this.grid.render(this.wrapper);
35655         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35656
35657     }
35658     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35659     // ??? needed ??? config.el = this.wrapper;
35660     
35661     
35662     
35663   
35664     // xtype created footer. - not sure if will work as we normally have to render first..
35665     if (this.footer && !this.footer.el && this.footer.xtype) {
35666         
35667         var ctr = this.grid.getView().getFooterPanel(true);
35668         this.footer.dataSource = this.grid.dataSource;
35669         this.footer = Roo.factory(this.footer, Roo);
35670         this.footer.render(ctr);
35671         
35672     }
35673     
35674     
35675     
35676     
35677      
35678 };
35679
35680 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35681     getId : function(){
35682         return this.grid.id;
35683     },
35684     
35685     /**
35686      * Returns the grid for this panel
35687      * @return {Roo.bootstrap.Table} 
35688      */
35689     getGrid : function(){
35690         return this.grid;    
35691     },
35692     
35693     setSize : function(width, height){
35694         if(!this.ignoreResize(width, height)){
35695             var grid = this.grid;
35696             var size = this.adjustForComponents(width, height);
35697             var gridel = grid.getGridEl();
35698             gridel.setSize(size.width, size.height);
35699             /*
35700             var thd = grid.getGridEl().select('thead',true).first();
35701             var tbd = grid.getGridEl().select('tbody', true).first();
35702             if (tbd) {
35703                 tbd.setSize(width, height - thd.getHeight());
35704             }
35705             */
35706             grid.autoSize();
35707         }
35708     },
35709      
35710     
35711     
35712     beforeSlide : function(){
35713         this.grid.getView().scroller.clip();
35714     },
35715     
35716     afterSlide : function(){
35717         this.grid.getView().scroller.unclip();
35718     },
35719     
35720     destroy : function(){
35721         this.grid.destroy();
35722         delete this.grid;
35723         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35724     }
35725 });
35726
35727 /**
35728  * @class Roo.bootstrap.panel.Nest
35729  * @extends Roo.bootstrap.panel.Content
35730  * @constructor
35731  * Create a new Panel, that can contain a layout.Border.
35732  * 
35733  * 
35734  * @param {Roo.BorderLayout} layout The layout for this panel
35735  * @param {String/Object} config A string to set only the title or a config object
35736  */
35737 Roo.bootstrap.panel.Nest = function(config)
35738 {
35739     // construct with only one argument..
35740     /* FIXME - implement nicer consturctors
35741     if (layout.layout) {
35742         config = layout;
35743         layout = config.layout;
35744         delete config.layout;
35745     }
35746     if (layout.xtype && !layout.getEl) {
35747         // then layout needs constructing..
35748         layout = Roo.factory(layout, Roo);
35749     }
35750     */
35751     
35752     config.el =  config.layout.getEl();
35753     
35754     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35755     
35756     config.layout.monitorWindowResize = false; // turn off autosizing
35757     this.layout = config.layout;
35758     this.layout.getEl().addClass("roo-layout-nested-layout");
35759     
35760     
35761     
35762     
35763 };
35764
35765 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35766
35767     setSize : function(width, height){
35768         if(!this.ignoreResize(width, height)){
35769             var size = this.adjustForComponents(width, height);
35770             var el = this.layout.getEl();
35771             if (size.height < 1) {
35772                 el.setWidth(size.width);   
35773             } else {
35774                 el.setSize(size.width, size.height);
35775             }
35776             var touch = el.dom.offsetWidth;
35777             this.layout.layout();
35778             // ie requires a double layout on the first pass
35779             if(Roo.isIE && !this.initialized){
35780                 this.initialized = true;
35781                 this.layout.layout();
35782             }
35783         }
35784     },
35785     
35786     // activate all subpanels if not currently active..
35787     
35788     setActiveState : function(active){
35789         this.active = active;
35790         this.setActiveClass(active);
35791         
35792         if(!active){
35793             this.fireEvent("deactivate", this);
35794             return;
35795         }
35796         
35797         this.fireEvent("activate", this);
35798         // not sure if this should happen before or after..
35799         if (!this.layout) {
35800             return; // should not happen..
35801         }
35802         var reg = false;
35803         for (var r in this.layout.regions) {
35804             reg = this.layout.getRegion(r);
35805             if (reg.getActivePanel()) {
35806                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35807                 reg.setActivePanel(reg.getActivePanel());
35808                 continue;
35809             }
35810             if (!reg.panels.length) {
35811                 continue;
35812             }
35813             reg.showPanel(reg.getPanel(0));
35814         }
35815         
35816         
35817         
35818         
35819     },
35820     
35821     /**
35822      * Returns the nested BorderLayout for this panel
35823      * @return {Roo.BorderLayout} 
35824      */
35825     getLayout : function(){
35826         return this.layout;
35827     },
35828     
35829      /**
35830      * Adds a xtype elements to the layout of the nested panel
35831      * <pre><code>
35832
35833 panel.addxtype({
35834        xtype : 'ContentPanel',
35835        region: 'west',
35836        items: [ .... ]
35837    }
35838 );
35839
35840 panel.addxtype({
35841         xtype : 'NestedLayoutPanel',
35842         region: 'west',
35843         layout: {
35844            center: { },
35845            west: { }   
35846         },
35847         items : [ ... list of content panels or nested layout panels.. ]
35848    }
35849 );
35850 </code></pre>
35851      * @param {Object} cfg Xtype definition of item to add.
35852      */
35853     addxtype : function(cfg) {
35854         return this.layout.addxtype(cfg);
35855     
35856     }
35857 });        /*
35858  * Based on:
35859  * Ext JS Library 1.1.1
35860  * Copyright(c) 2006-2007, Ext JS, LLC.
35861  *
35862  * Originally Released Under LGPL - original licence link has changed is not relivant.
35863  *
35864  * Fork - LGPL
35865  * <script type="text/javascript">
35866  */
35867 /**
35868  * @class Roo.TabPanel
35869  * @extends Roo.util.Observable
35870  * A lightweight tab container.
35871  * <br><br>
35872  * Usage:
35873  * <pre><code>
35874 // basic tabs 1, built from existing content
35875 var tabs = new Roo.TabPanel("tabs1");
35876 tabs.addTab("script", "View Script");
35877 tabs.addTab("markup", "View Markup");
35878 tabs.activate("script");
35879
35880 // more advanced tabs, built from javascript
35881 var jtabs = new Roo.TabPanel("jtabs");
35882 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35883
35884 // set up the UpdateManager
35885 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35886 var updater = tab2.getUpdateManager();
35887 updater.setDefaultUrl("ajax1.htm");
35888 tab2.on('activate', updater.refresh, updater, true);
35889
35890 // Use setUrl for Ajax loading
35891 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35892 tab3.setUrl("ajax2.htm", null, true);
35893
35894 // Disabled tab
35895 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35896 tab4.disable();
35897
35898 jtabs.activate("jtabs-1");
35899  * </code></pre>
35900  * @constructor
35901  * Create a new TabPanel.
35902  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35903  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35904  */
35905 Roo.bootstrap.panel.Tabs = function(config){
35906     /**
35907     * The container element for this TabPanel.
35908     * @type Roo.Element
35909     */
35910     this.el = Roo.get(config.el);
35911     delete config.el;
35912     if(config){
35913         if(typeof config == "boolean"){
35914             this.tabPosition = config ? "bottom" : "top";
35915         }else{
35916             Roo.apply(this, config);
35917         }
35918     }
35919     
35920     if(this.tabPosition == "bottom"){
35921         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35922         this.el.addClass("roo-tabs-bottom");
35923     }
35924     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35925     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35926     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35927     if(Roo.isIE){
35928         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35929     }
35930     if(this.tabPosition != "bottom"){
35931         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35932          * @type Roo.Element
35933          */
35934         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35935         this.el.addClass("roo-tabs-top");
35936     }
35937     this.items = [];
35938
35939     this.bodyEl.setStyle("position", "relative");
35940
35941     this.active = null;
35942     this.activateDelegate = this.activate.createDelegate(this);
35943
35944     this.addEvents({
35945         /**
35946          * @event tabchange
35947          * Fires when the active tab changes
35948          * @param {Roo.TabPanel} this
35949          * @param {Roo.TabPanelItem} activePanel The new active tab
35950          */
35951         "tabchange": true,
35952         /**
35953          * @event beforetabchange
35954          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35955          * @param {Roo.TabPanel} this
35956          * @param {Object} e Set cancel to true on this object to cancel the tab change
35957          * @param {Roo.TabPanelItem} tab The tab being changed to
35958          */
35959         "beforetabchange" : true
35960     });
35961
35962     Roo.EventManager.onWindowResize(this.onResize, this);
35963     this.cpad = this.el.getPadding("lr");
35964     this.hiddenCount = 0;
35965
35966
35967     // toolbar on the tabbar support...
35968     if (this.toolbar) {
35969         alert("no toolbar support yet");
35970         this.toolbar  = false;
35971         /*
35972         var tcfg = this.toolbar;
35973         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35974         this.toolbar = new Roo.Toolbar(tcfg);
35975         if (Roo.isSafari) {
35976             var tbl = tcfg.container.child('table', true);
35977             tbl.setAttribute('width', '100%');
35978         }
35979         */
35980         
35981     }
35982    
35983
35984
35985     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35986 };
35987
35988 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35989     /*
35990      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35991      */
35992     tabPosition : "top",
35993     /*
35994      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35995      */
35996     currentTabWidth : 0,
35997     /*
35998      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35999      */
36000     minTabWidth : 40,
36001     /*
36002      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36003      */
36004     maxTabWidth : 250,
36005     /*
36006      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
36007      */
36008     preferredTabWidth : 175,
36009     /*
36010      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
36011      */
36012     resizeTabs : false,
36013     /*
36014      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
36015      */
36016     monitorResize : true,
36017     /*
36018      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
36019      */
36020     toolbar : false,
36021
36022     /**
36023      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
36024      * @param {String} id The id of the div to use <b>or create</b>
36025      * @param {String} text The text for the tab
36026      * @param {String} content (optional) Content to put in the TabPanelItem body
36027      * @param {Boolean} closable (optional) True to create a close icon on the tab
36028      * @return {Roo.TabPanelItem} The created TabPanelItem
36029      */
36030     addTab : function(id, text, content, closable, tpl)
36031     {
36032         var item = new Roo.bootstrap.panel.TabItem({
36033             panel: this,
36034             id : id,
36035             text : text,
36036             closable : closable,
36037             tpl : tpl
36038         });
36039         this.addTabItem(item);
36040         if(content){
36041             item.setContent(content);
36042         }
36043         return item;
36044     },
36045
36046     /**
36047      * Returns the {@link Roo.TabPanelItem} with the specified id/index
36048      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
36049      * @return {Roo.TabPanelItem}
36050      */
36051     getTab : function(id){
36052         return this.items[id];
36053     },
36054
36055     /**
36056      * Hides the {@link Roo.TabPanelItem} with the specified id/index
36057      * @param {String/Number} id The id or index of the TabPanelItem to hide.
36058      */
36059     hideTab : function(id){
36060         var t = this.items[id];
36061         if(!t.isHidden()){
36062            t.setHidden(true);
36063            this.hiddenCount++;
36064            this.autoSizeTabs();
36065         }
36066     },
36067
36068     /**
36069      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36070      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36071      */
36072     unhideTab : function(id){
36073         var t = this.items[id];
36074         if(t.isHidden()){
36075            t.setHidden(false);
36076            this.hiddenCount--;
36077            this.autoSizeTabs();
36078         }
36079     },
36080
36081     /**
36082      * Adds an existing {@link Roo.TabPanelItem}.
36083      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36084      */
36085     addTabItem : function(item){
36086         this.items[item.id] = item;
36087         this.items.push(item);
36088       //  if(this.resizeTabs){
36089     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36090   //         this.autoSizeTabs();
36091 //        }else{
36092 //            item.autoSize();
36093        // }
36094     },
36095
36096     /**
36097      * Removes a {@link Roo.TabPanelItem}.
36098      * @param {String/Number} id The id or index of the TabPanelItem to remove.
36099      */
36100     removeTab : function(id){
36101         var items = this.items;
36102         var tab = items[id];
36103         if(!tab) { return; }
36104         var index = items.indexOf(tab);
36105         if(this.active == tab && items.length > 1){
36106             var newTab = this.getNextAvailable(index);
36107             if(newTab) {
36108                 newTab.activate();
36109             }
36110         }
36111         this.stripEl.dom.removeChild(tab.pnode.dom);
36112         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36113             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36114         }
36115         items.splice(index, 1);
36116         delete this.items[tab.id];
36117         tab.fireEvent("close", tab);
36118         tab.purgeListeners();
36119         this.autoSizeTabs();
36120     },
36121
36122     getNextAvailable : function(start){
36123         var items = this.items;
36124         var index = start;
36125         // look for a next tab that will slide over to
36126         // replace the one being removed
36127         while(index < items.length){
36128             var item = items[++index];
36129             if(item && !item.isHidden()){
36130                 return item;
36131             }
36132         }
36133         // if one isn't found select the previous tab (on the left)
36134         index = start;
36135         while(index >= 0){
36136             var item = items[--index];
36137             if(item && !item.isHidden()){
36138                 return item;
36139             }
36140         }
36141         return null;
36142     },
36143
36144     /**
36145      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36146      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36147      */
36148     disableTab : function(id){
36149         var tab = this.items[id];
36150         if(tab && this.active != tab){
36151             tab.disable();
36152         }
36153     },
36154
36155     /**
36156      * Enables a {@link Roo.TabPanelItem} that is disabled.
36157      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36158      */
36159     enableTab : function(id){
36160         var tab = this.items[id];
36161         tab.enable();
36162     },
36163
36164     /**
36165      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36166      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36167      * @return {Roo.TabPanelItem} The TabPanelItem.
36168      */
36169     activate : function(id){
36170         var tab = this.items[id];
36171         if(!tab){
36172             return null;
36173         }
36174         if(tab == this.active || tab.disabled){
36175             return tab;
36176         }
36177         var e = {};
36178         this.fireEvent("beforetabchange", this, e, tab);
36179         if(e.cancel !== true && !tab.disabled){
36180             if(this.active){
36181                 this.active.hide();
36182             }
36183             this.active = this.items[id];
36184             this.active.show();
36185             this.fireEvent("tabchange", this, this.active);
36186         }
36187         return tab;
36188     },
36189
36190     /**
36191      * Gets the active {@link Roo.TabPanelItem}.
36192      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36193      */
36194     getActiveTab : function(){
36195         return this.active;
36196     },
36197
36198     /**
36199      * Updates the tab body element to fit the height of the container element
36200      * for overflow scrolling
36201      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36202      */
36203     syncHeight : function(targetHeight){
36204         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36205         var bm = this.bodyEl.getMargins();
36206         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36207         this.bodyEl.setHeight(newHeight);
36208         return newHeight;
36209     },
36210
36211     onResize : function(){
36212         if(this.monitorResize){
36213             this.autoSizeTabs();
36214         }
36215     },
36216
36217     /**
36218      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36219      */
36220     beginUpdate : function(){
36221         this.updating = true;
36222     },
36223
36224     /**
36225      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36226      */
36227     endUpdate : function(){
36228         this.updating = false;
36229         this.autoSizeTabs();
36230     },
36231
36232     /**
36233      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36234      */
36235     autoSizeTabs : function(){
36236         var count = this.items.length;
36237         var vcount = count - this.hiddenCount;
36238         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36239             return;
36240         }
36241         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36242         var availWidth = Math.floor(w / vcount);
36243         var b = this.stripBody;
36244         if(b.getWidth() > w){
36245             var tabs = this.items;
36246             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36247             if(availWidth < this.minTabWidth){
36248                 /*if(!this.sleft){    // incomplete scrolling code
36249                     this.createScrollButtons();
36250                 }
36251                 this.showScroll();
36252                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36253             }
36254         }else{
36255             if(this.currentTabWidth < this.preferredTabWidth){
36256                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36257             }
36258         }
36259     },
36260
36261     /**
36262      * Returns the number of tabs in this TabPanel.
36263      * @return {Number}
36264      */
36265      getCount : function(){
36266          return this.items.length;
36267      },
36268
36269     /**
36270      * Resizes all the tabs to the passed width
36271      * @param {Number} The new width
36272      */
36273     setTabWidth : function(width){
36274         this.currentTabWidth = width;
36275         for(var i = 0, len = this.items.length; i < len; i++) {
36276                 if(!this.items[i].isHidden()) {
36277                 this.items[i].setWidth(width);
36278             }
36279         }
36280     },
36281
36282     /**
36283      * Destroys this TabPanel
36284      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36285      */
36286     destroy : function(removeEl){
36287         Roo.EventManager.removeResizeListener(this.onResize, this);
36288         for(var i = 0, len = this.items.length; i < len; i++){
36289             this.items[i].purgeListeners();
36290         }
36291         if(removeEl === true){
36292             this.el.update("");
36293             this.el.remove();
36294         }
36295     },
36296     
36297     createStrip : function(container)
36298     {
36299         var strip = document.createElement("nav");
36300         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36301         container.appendChild(strip);
36302         return strip;
36303     },
36304     
36305     createStripList : function(strip)
36306     {
36307         // div wrapper for retard IE
36308         // returns the "tr" element.
36309         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36310         //'<div class="x-tabs-strip-wrap">'+
36311           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36312           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36313         return strip.firstChild; //.firstChild.firstChild.firstChild;
36314     },
36315     createBody : function(container)
36316     {
36317         var body = document.createElement("div");
36318         Roo.id(body, "tab-body");
36319         //Roo.fly(body).addClass("x-tabs-body");
36320         Roo.fly(body).addClass("tab-content");
36321         container.appendChild(body);
36322         return body;
36323     },
36324     createItemBody :function(bodyEl, id){
36325         var body = Roo.getDom(id);
36326         if(!body){
36327             body = document.createElement("div");
36328             body.id = id;
36329         }
36330         //Roo.fly(body).addClass("x-tabs-item-body");
36331         Roo.fly(body).addClass("tab-pane");
36332          bodyEl.insertBefore(body, bodyEl.firstChild);
36333         return body;
36334     },
36335     /** @private */
36336     createStripElements :  function(stripEl, text, closable, tpl)
36337     {
36338         var td = document.createElement("li"); // was td..
36339         
36340         
36341         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36342         
36343         
36344         stripEl.appendChild(td);
36345         /*if(closable){
36346             td.className = "x-tabs-closable";
36347             if(!this.closeTpl){
36348                 this.closeTpl = new Roo.Template(
36349                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36350                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36351                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36352                 );
36353             }
36354             var el = this.closeTpl.overwrite(td, {"text": text});
36355             var close = el.getElementsByTagName("div")[0];
36356             var inner = el.getElementsByTagName("em")[0];
36357             return {"el": el, "close": close, "inner": inner};
36358         } else {
36359         */
36360         // not sure what this is..
36361 //            if(!this.tabTpl){
36362                 //this.tabTpl = new Roo.Template(
36363                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36364                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36365                 //);
36366 //                this.tabTpl = new Roo.Template(
36367 //                   '<a href="#">' +
36368 //                   '<span unselectable="on"' +
36369 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36370 //                            ' >{text}</span></a>'
36371 //                );
36372 //                
36373 //            }
36374
36375
36376             var template = tpl || this.tabTpl || false;
36377             
36378             if(!template){
36379                 
36380                 template = new Roo.Template(
36381                    '<a href="#">' +
36382                    '<span unselectable="on"' +
36383                             (this.disableTooltips ? '' : ' title="{text}"') +
36384                             ' >{text}</span></a>'
36385                 );
36386             }
36387             
36388             switch (typeof(template)) {
36389                 case 'object' :
36390                     break;
36391                 case 'string' :
36392                     template = new Roo.Template(template);
36393                     break;
36394                 default :
36395                     break;
36396             }
36397             
36398             var el = template.overwrite(td, {"text": text});
36399             
36400             var inner = el.getElementsByTagName("span")[0];
36401             
36402             return {"el": el, "inner": inner};
36403             
36404     }
36405         
36406     
36407 });
36408
36409 /**
36410  * @class Roo.TabPanelItem
36411  * @extends Roo.util.Observable
36412  * Represents an individual item (tab plus body) in a TabPanel.
36413  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36414  * @param {String} id The id of this TabPanelItem
36415  * @param {String} text The text for the tab of this TabPanelItem
36416  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36417  */
36418 Roo.bootstrap.panel.TabItem = function(config){
36419     /**
36420      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36421      * @type Roo.TabPanel
36422      */
36423     this.tabPanel = config.panel;
36424     /**
36425      * The id for this TabPanelItem
36426      * @type String
36427      */
36428     this.id = config.id;
36429     /** @private */
36430     this.disabled = false;
36431     /** @private */
36432     this.text = config.text;
36433     /** @private */
36434     this.loaded = false;
36435     this.closable = config.closable;
36436
36437     /**
36438      * The body element for this TabPanelItem.
36439      * @type Roo.Element
36440      */
36441     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36442     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36443     this.bodyEl.setStyle("display", "block");
36444     this.bodyEl.setStyle("zoom", "1");
36445     //this.hideAction();
36446
36447     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36448     /** @private */
36449     this.el = Roo.get(els.el);
36450     this.inner = Roo.get(els.inner, true);
36451     this.textEl = Roo.get(this.el.dom.firstChild, true);
36452     this.pnode = Roo.get(els.el.parentNode, true);
36453     this.el.on("mousedown", this.onTabMouseDown, this);
36454     this.el.on("click", this.onTabClick, this);
36455     /** @private */
36456     if(config.closable){
36457         var c = Roo.get(els.close, true);
36458         c.dom.title = this.closeText;
36459         c.addClassOnOver("close-over");
36460         c.on("click", this.closeClick, this);
36461      }
36462
36463     this.addEvents({
36464          /**
36465          * @event activate
36466          * Fires when this tab becomes the active tab.
36467          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36468          * @param {Roo.TabPanelItem} this
36469          */
36470         "activate": true,
36471         /**
36472          * @event beforeclose
36473          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36474          * @param {Roo.TabPanelItem} this
36475          * @param {Object} e Set cancel to true on this object to cancel the close.
36476          */
36477         "beforeclose": true,
36478         /**
36479          * @event close
36480          * Fires when this tab is closed.
36481          * @param {Roo.TabPanelItem} this
36482          */
36483          "close": true,
36484         /**
36485          * @event deactivate
36486          * Fires when this tab is no longer the active tab.
36487          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36488          * @param {Roo.TabPanelItem} this
36489          */
36490          "deactivate" : true
36491     });
36492     this.hidden = false;
36493
36494     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36495 };
36496
36497 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36498            {
36499     purgeListeners : function(){
36500        Roo.util.Observable.prototype.purgeListeners.call(this);
36501        this.el.removeAllListeners();
36502     },
36503     /**
36504      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36505      */
36506     show : function(){
36507         this.pnode.addClass("active");
36508         this.showAction();
36509         if(Roo.isOpera){
36510             this.tabPanel.stripWrap.repaint();
36511         }
36512         this.fireEvent("activate", this.tabPanel, this);
36513     },
36514
36515     /**
36516      * Returns true if this tab is the active tab.
36517      * @return {Boolean}
36518      */
36519     isActive : function(){
36520         return this.tabPanel.getActiveTab() == this;
36521     },
36522
36523     /**
36524      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36525      */
36526     hide : function(){
36527         this.pnode.removeClass("active");
36528         this.hideAction();
36529         this.fireEvent("deactivate", this.tabPanel, this);
36530     },
36531
36532     hideAction : function(){
36533         this.bodyEl.hide();
36534         this.bodyEl.setStyle("position", "absolute");
36535         this.bodyEl.setLeft("-20000px");
36536         this.bodyEl.setTop("-20000px");
36537     },
36538
36539     showAction : function(){
36540         this.bodyEl.setStyle("position", "relative");
36541         this.bodyEl.setTop("");
36542         this.bodyEl.setLeft("");
36543         this.bodyEl.show();
36544     },
36545
36546     /**
36547      * Set the tooltip for the tab.
36548      * @param {String} tooltip The tab's tooltip
36549      */
36550     setTooltip : function(text){
36551         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36552             this.textEl.dom.qtip = text;
36553             this.textEl.dom.removeAttribute('title');
36554         }else{
36555             this.textEl.dom.title = text;
36556         }
36557     },
36558
36559     onTabClick : function(e){
36560         e.preventDefault();
36561         this.tabPanel.activate(this.id);
36562     },
36563
36564     onTabMouseDown : function(e){
36565         e.preventDefault();
36566         this.tabPanel.activate(this.id);
36567     },
36568 /*
36569     getWidth : function(){
36570         return this.inner.getWidth();
36571     },
36572
36573     setWidth : function(width){
36574         var iwidth = width - this.pnode.getPadding("lr");
36575         this.inner.setWidth(iwidth);
36576         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36577         this.pnode.setWidth(width);
36578     },
36579 */
36580     /**
36581      * Show or hide the tab
36582      * @param {Boolean} hidden True to hide or false to show.
36583      */
36584     setHidden : function(hidden){
36585         this.hidden = hidden;
36586         this.pnode.setStyle("display", hidden ? "none" : "");
36587     },
36588
36589     /**
36590      * Returns true if this tab is "hidden"
36591      * @return {Boolean}
36592      */
36593     isHidden : function(){
36594         return this.hidden;
36595     },
36596
36597     /**
36598      * Returns the text for this tab
36599      * @return {String}
36600      */
36601     getText : function(){
36602         return this.text;
36603     },
36604     /*
36605     autoSize : function(){
36606         //this.el.beginMeasure();
36607         this.textEl.setWidth(1);
36608         /*
36609          *  #2804 [new] Tabs in Roojs
36610          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36611          */
36612         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36613         //this.el.endMeasure();
36614     //},
36615
36616     /**
36617      * Sets the text for the tab (Note: this also sets the tooltip text)
36618      * @param {String} text The tab's text and tooltip
36619      */
36620     setText : function(text){
36621         this.text = text;
36622         this.textEl.update(text);
36623         this.setTooltip(text);
36624         //if(!this.tabPanel.resizeTabs){
36625         //    this.autoSize();
36626         //}
36627     },
36628     /**
36629      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36630      */
36631     activate : function(){
36632         this.tabPanel.activate(this.id);
36633     },
36634
36635     /**
36636      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36637      */
36638     disable : function(){
36639         if(this.tabPanel.active != this){
36640             this.disabled = true;
36641             this.pnode.addClass("disabled");
36642         }
36643     },
36644
36645     /**
36646      * Enables this TabPanelItem if it was previously disabled.
36647      */
36648     enable : function(){
36649         this.disabled = false;
36650         this.pnode.removeClass("disabled");
36651     },
36652
36653     /**
36654      * Sets the content for this TabPanelItem.
36655      * @param {String} content The content
36656      * @param {Boolean} loadScripts true to look for and load scripts
36657      */
36658     setContent : function(content, loadScripts){
36659         this.bodyEl.update(content, loadScripts);
36660     },
36661
36662     /**
36663      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36664      * @return {Roo.UpdateManager} The UpdateManager
36665      */
36666     getUpdateManager : function(){
36667         return this.bodyEl.getUpdateManager();
36668     },
36669
36670     /**
36671      * Set a URL to be used to load the content for this TabPanelItem.
36672      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36673      * @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)
36674      * @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)
36675      * @return {Roo.UpdateManager} The UpdateManager
36676      */
36677     setUrl : function(url, params, loadOnce){
36678         if(this.refreshDelegate){
36679             this.un('activate', this.refreshDelegate);
36680         }
36681         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36682         this.on("activate", this.refreshDelegate);
36683         return this.bodyEl.getUpdateManager();
36684     },
36685
36686     /** @private */
36687     _handleRefresh : function(url, params, loadOnce){
36688         if(!loadOnce || !this.loaded){
36689             var updater = this.bodyEl.getUpdateManager();
36690             updater.update(url, params, this._setLoaded.createDelegate(this));
36691         }
36692     },
36693
36694     /**
36695      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36696      *   Will fail silently if the setUrl method has not been called.
36697      *   This does not activate the panel, just updates its content.
36698      */
36699     refresh : function(){
36700         if(this.refreshDelegate){
36701            this.loaded = false;
36702            this.refreshDelegate();
36703         }
36704     },
36705
36706     /** @private */
36707     _setLoaded : function(){
36708         this.loaded = true;
36709     },
36710
36711     /** @private */
36712     closeClick : function(e){
36713         var o = {};
36714         e.stopEvent();
36715         this.fireEvent("beforeclose", this, o);
36716         if(o.cancel !== true){
36717             this.tabPanel.removeTab(this.id);
36718         }
36719     },
36720     /**
36721      * The text displayed in the tooltip for the close icon.
36722      * @type String
36723      */
36724     closeText : "Close this tab"
36725 });