roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     bodyEl : function()
1297     {
1298         if(!this.el || !this.panel.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-body',true).first()
1303     },
1304     
1305     titleEl : function()
1306     {
1307         if(!this.el || !this.panel.length || !this.header.length){
1308             return;
1309         }
1310         
1311         return this.el.select('.panel-title',true).first();
1312     },
1313     
1314     setTitle : function(v)
1315     {
1316         var titleEl = this.titleEl();
1317         
1318         if(!titleEl){
1319             return;
1320         }
1321         
1322         titleEl.dom.innerHTML = v;
1323     },
1324     
1325     getTitle : function()
1326     {
1327         
1328         var titleEl = this.titleEl();
1329         
1330         if(!titleEl){
1331             return '';
1332         }
1333         
1334         return titleEl.dom.innerHTML;
1335     },
1336     
1337     setRightTitle : function(v)
1338     {
1339         var t = this.el.select('.panel-header-right',true).first();
1340         
1341         if(!t){
1342             return;
1343         }
1344         
1345         t.dom.innerHTML = v;
1346     },
1347     
1348     onClick : function(e)
1349     {
1350         e.preventDefault();
1351         
1352         this.fireEvent('click', this, e);
1353     }
1354    
1355 });
1356
1357  /*
1358  * - LGPL
1359  *
1360  * image
1361  * 
1362  */
1363
1364
1365 /**
1366  * @class Roo.bootstrap.Img
1367  * @extends Roo.bootstrap.Component
1368  * Bootstrap Img class
1369  * @cfg {Boolean} imgResponsive false | true
1370  * @cfg {String} border rounded | circle | thumbnail
1371  * @cfg {String} src image source
1372  * @cfg {String} alt image alternative text
1373  * @cfg {String} href a tag href
1374  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1375  * @cfg {String} xsUrl xs image source
1376  * @cfg {String} smUrl sm image source
1377  * @cfg {String} mdUrl md image source
1378  * @cfg {String} lgUrl lg image source
1379  * 
1380  * @constructor
1381  * Create a new Input
1382  * @param {Object} config The config object
1383  */
1384
1385 Roo.bootstrap.Img = function(config){
1386     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1387     
1388     this.addEvents({
1389         // img events
1390         /**
1391          * @event click
1392          * The img click event for the img.
1393          * @param {Roo.EventObject} e
1394          */
1395         "click" : true
1396     });
1397 };
1398
1399 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1400     
1401     imgResponsive: true,
1402     border: '',
1403     src: 'about:blank',
1404     href: false,
1405     target: false,
1406     xsUrl: '',
1407     smUrl: '',
1408     mdUrl: '',
1409     lgUrl: '',
1410
1411     getAutoCreate : function()
1412     {   
1413         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1414             return this.createSingleImg();
1415         }
1416         
1417         var cfg = {
1418             tag: 'div',
1419             cls: 'roo-image-responsive-group',
1420             cn: []
1421         };
1422         var _this = this;
1423         
1424         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1425             
1426             if(!_this[size + 'Url']){
1427                 return;
1428             }
1429             
1430             var img = {
1431                 tag: 'img',
1432                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1433                 html: _this.html || cfg.html,
1434                 src: _this[size + 'Url']
1435             };
1436             
1437             img.cls += ' roo-image-responsive-' + size;
1438             
1439             var s = ['xs', 'sm', 'md', 'lg'];
1440             
1441             s.splice(s.indexOf(size), 1);
1442             
1443             Roo.each(s, function(ss){
1444                 img.cls += ' hidden-' + ss;
1445             });
1446             
1447             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1448                 cfg.cls += ' img-' + _this.border;
1449             }
1450             
1451             if(_this.alt){
1452                 cfg.alt = _this.alt;
1453             }
1454             
1455             if(_this.href){
1456                 var a = {
1457                     tag: 'a',
1458                     href: _this.href,
1459                     cn: [
1460                         img
1461                     ]
1462                 };
1463
1464                 if(this.target){
1465                     a.target = _this.target;
1466                 }
1467             }
1468             
1469             cfg.cn.push((_this.href) ? a : img);
1470             
1471         });
1472         
1473         return cfg;
1474     },
1475     
1476     createSingleImg : function()
1477     {
1478         var cfg = {
1479             tag: 'img',
1480             cls: (this.imgResponsive) ? 'img-responsive' : '',
1481             html : null,
1482             src : 'about:blank'  // just incase src get's set to undefined?!?
1483         };
1484         
1485         cfg.html = this.html || cfg.html;
1486         
1487         cfg.src = this.src || cfg.src;
1488         
1489         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1490             cfg.cls += ' img-' + this.border;
1491         }
1492         
1493         if(this.alt){
1494             cfg.alt = this.alt;
1495         }
1496         
1497         if(this.href){
1498             var a = {
1499                 tag: 'a',
1500                 href: this.href,
1501                 cn: [
1502                     cfg
1503                 ]
1504             };
1505             
1506             if(this.target){
1507                 a.target = this.target;
1508             }
1509             
1510         }
1511         
1512         return (this.href) ? a : cfg;
1513     },
1514     
1515     initEvents: function() 
1516     {
1517         if(!this.href){
1518             this.el.on('click', this.onClick, this);
1519         }
1520         
1521     },
1522     
1523     onClick : function(e)
1524     {
1525         Roo.log('img onclick');
1526         this.fireEvent('click', this, e);
1527     },
1528     /**
1529      * Sets the url of the image - used to update it
1530      * @param {String} url the url of the image
1531      */
1532     
1533     setSrc : function(url)
1534     {
1535         this.src =  url;
1536         
1537         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1538             this.el.dom.src =  url;
1539             return;
1540         }
1541         
1542         this.el.select('img', true).first().dom.src =  url;
1543     }
1544     
1545     
1546    
1547 });
1548
1549  /*
1550  * - LGPL
1551  *
1552  * image
1553  * 
1554  */
1555
1556
1557 /**
1558  * @class Roo.bootstrap.Link
1559  * @extends Roo.bootstrap.Component
1560  * Bootstrap Link Class
1561  * @cfg {String} alt image alternative text
1562  * @cfg {String} href a tag href
1563  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1564  * @cfg {String} html the content of the link.
1565  * @cfg {String} anchor name for the anchor link
1566  * @cfg {String} fa - favicon
1567
1568  * @cfg {Boolean} preventDefault (true | false) default false
1569
1570  * 
1571  * @constructor
1572  * Create a new Input
1573  * @param {Object} config The config object
1574  */
1575
1576 Roo.bootstrap.Link = function(config){
1577     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1578     
1579     this.addEvents({
1580         // img events
1581         /**
1582          * @event click
1583          * The img click event for the img.
1584          * @param {Roo.EventObject} e
1585          */
1586         "click" : true
1587     });
1588 };
1589
1590 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1591     
1592     href: false,
1593     target: false,
1594     preventDefault: false,
1595     anchor : false,
1596     alt : false,
1597     fa: false,
1598
1599
1600     getAutoCreate : function()
1601     {
1602         var html = this.html || '';
1603         
1604         if (this.fa !== false) {
1605             html = '<i class="fa fa-' + this.fa + '"></i>';
1606         }
1607         var cfg = {
1608             tag: 'a'
1609         };
1610         // anchor's do not require html/href...
1611         if (this.anchor === false) {
1612             cfg.html = html;
1613             cfg.href = this.href || '#';
1614         } else {
1615             cfg.name = this.anchor;
1616             if (this.html !== false || this.fa !== false) {
1617                 cfg.html = html;
1618             }
1619             if (this.href !== false) {
1620                 cfg.href = this.href;
1621             }
1622         }
1623         
1624         if(this.alt !== false){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         
1629         if(this.target !== false) {
1630             cfg.target = this.target;
1631         }
1632         
1633         return cfg;
1634     },
1635     
1636     initEvents: function() {
1637         
1638         if(!this.href || this.preventDefault){
1639             this.el.on('click', this.onClick, this);
1640         }
1641     },
1642     
1643     onClick : function(e)
1644     {
1645         if(this.preventDefault){
1646             e.preventDefault();
1647         }
1648         //Roo.log('img onclick');
1649         this.fireEvent('click', this, e);
1650     }
1651    
1652 });
1653
1654  /*
1655  * - LGPL
1656  *
1657  * header
1658  * 
1659  */
1660
1661 /**
1662  * @class Roo.bootstrap.Header
1663  * @extends Roo.bootstrap.Component
1664  * Bootstrap Header class
1665  * @cfg {String} html content of header
1666  * @cfg {Number} level (1|2|3|4|5|6) default 1
1667  * 
1668  * @constructor
1669  * Create a new Header
1670  * @param {Object} config The config object
1671  */
1672
1673
1674 Roo.bootstrap.Header  = function(config){
1675     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1676 };
1677
1678 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1679     
1680     //href : false,
1681     html : false,
1682     level : 1,
1683     
1684     
1685     
1686     getAutoCreate : function(){
1687         
1688         
1689         
1690         var cfg = {
1691             tag: 'h' + (1 *this.level),
1692             html: this.html || ''
1693         } ;
1694         
1695         return cfg;
1696     }
1697    
1698 });
1699
1700  
1701
1702  /*
1703  * Based on:
1704  * Ext JS Library 1.1.1
1705  * Copyright(c) 2006-2007, Ext JS, LLC.
1706  *
1707  * Originally Released Under LGPL - original licence link has changed is not relivant.
1708  *
1709  * Fork - LGPL
1710  * <script type="text/javascript">
1711  */
1712  
1713 /**
1714  * @class Roo.bootstrap.MenuMgr
1715  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1716  * @singleton
1717  */
1718 Roo.bootstrap.MenuMgr = function(){
1719    var menus, active, groups = {}, attached = false, lastShow = new Date();
1720
1721    // private - called when first menu is created
1722    function init(){
1723        menus = {};
1724        active = new Roo.util.MixedCollection();
1725        Roo.get(document).addKeyListener(27, function(){
1726            if(active.length > 0){
1727                hideAll();
1728            }
1729        });
1730    }
1731
1732    // private
1733    function hideAll(){
1734        if(active && active.length > 0){
1735            var c = active.clone();
1736            c.each(function(m){
1737                m.hide();
1738            });
1739        }
1740    }
1741
1742    // private
1743    function onHide(m){
1744        active.remove(m);
1745        if(active.length < 1){
1746            Roo.get(document).un("mouseup", onMouseDown);
1747             
1748            attached = false;
1749        }
1750    }
1751
1752    // private
1753    function onShow(m){
1754        var last = active.last();
1755        lastShow = new Date();
1756        active.add(m);
1757        if(!attached){
1758           Roo.get(document).on("mouseup", onMouseDown);
1759            
1760            attached = true;
1761        }
1762        if(m.parentMenu){
1763           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1764           m.parentMenu.activeChild = m;
1765        }else if(last && last.isVisible()){
1766           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1767        }
1768    }
1769
1770    // private
1771    function onBeforeHide(m){
1772        if(m.activeChild){
1773            m.activeChild.hide();
1774        }
1775        if(m.autoHideTimer){
1776            clearTimeout(m.autoHideTimer);
1777            delete m.autoHideTimer;
1778        }
1779    }
1780
1781    // private
1782    function onBeforeShow(m){
1783        var pm = m.parentMenu;
1784        if(!pm && !m.allowOtherMenus){
1785            hideAll();
1786        }else if(pm && pm.activeChild && active != m){
1787            pm.activeChild.hide();
1788        }
1789    }
1790
1791    // private this should really trigger on mouseup..
1792    function onMouseDown(e){
1793         Roo.log("on Mouse Up");
1794         
1795         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1796             Roo.log("MenuManager hideAll");
1797             hideAll();
1798             e.stopEvent();
1799         }
1800         
1801         
1802    }
1803
1804    // private
1805    function onBeforeCheck(mi, state){
1806        if(state){
1807            var g = groups[mi.group];
1808            for(var i = 0, l = g.length; i < l; i++){
1809                if(g[i] != mi){
1810                    g[i].setChecked(false);
1811                }
1812            }
1813        }
1814    }
1815
1816    return {
1817
1818        /**
1819         * Hides all menus that are currently visible
1820         */
1821        hideAll : function(){
1822             hideAll();  
1823        },
1824
1825        // private
1826        register : function(menu){
1827            if(!menus){
1828                init();
1829            }
1830            menus[menu.id] = menu;
1831            menu.on("beforehide", onBeforeHide);
1832            menu.on("hide", onHide);
1833            menu.on("beforeshow", onBeforeShow);
1834            menu.on("show", onShow);
1835            var g = menu.group;
1836            if(g && menu.events["checkchange"]){
1837                if(!groups[g]){
1838                    groups[g] = [];
1839                }
1840                groups[g].push(menu);
1841                menu.on("checkchange", onCheck);
1842            }
1843        },
1844
1845         /**
1846          * Returns a {@link Roo.menu.Menu} object
1847          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1848          * be used to generate and return a new Menu instance.
1849          */
1850        get : function(menu){
1851            if(typeof menu == "string"){ // menu id
1852                return menus[menu];
1853            }else if(menu.events){  // menu instance
1854                return menu;
1855            }
1856            /*else if(typeof menu.length == 'number'){ // array of menu items?
1857                return new Roo.bootstrap.Menu({items:menu});
1858            }else{ // otherwise, must be a config
1859                return new Roo.bootstrap.Menu(menu);
1860            }
1861            */
1862            return false;
1863        },
1864
1865        // private
1866        unregister : function(menu){
1867            delete menus[menu.id];
1868            menu.un("beforehide", onBeforeHide);
1869            menu.un("hide", onHide);
1870            menu.un("beforeshow", onBeforeShow);
1871            menu.un("show", onShow);
1872            var g = menu.group;
1873            if(g && menu.events["checkchange"]){
1874                groups[g].remove(menu);
1875                menu.un("checkchange", onCheck);
1876            }
1877        },
1878
1879        // private
1880        registerCheckable : function(menuItem){
1881            var g = menuItem.group;
1882            if(g){
1883                if(!groups[g]){
1884                    groups[g] = [];
1885                }
1886                groups[g].push(menuItem);
1887                menuItem.on("beforecheckchange", onBeforeCheck);
1888            }
1889        },
1890
1891        // private
1892        unregisterCheckable : function(menuItem){
1893            var g = menuItem.group;
1894            if(g){
1895                groups[g].remove(menuItem);
1896                menuItem.un("beforecheckchange", onBeforeCheck);
1897            }
1898        }
1899    };
1900 }();/*
1901  * - LGPL
1902  *
1903  * menu
1904  * 
1905  */
1906
1907 /**
1908  * @class Roo.bootstrap.Menu
1909  * @extends Roo.bootstrap.Component
1910  * Bootstrap Menu class - container for MenuItems
1911  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1912  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1913  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1914  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1915  * 
1916  * @constructor
1917  * Create a new Menu
1918  * @param {Object} config The config object
1919  */
1920
1921
1922 Roo.bootstrap.Menu = function(config){
1923     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1924     if (this.registerMenu && this.type != 'treeview')  {
1925         Roo.bootstrap.MenuMgr.register(this);
1926     }
1927     this.addEvents({
1928         /**
1929          * @event beforeshow
1930          * Fires before this menu is displayed
1931          * @param {Roo.menu.Menu} this
1932          */
1933         beforeshow : true,
1934         /**
1935          * @event beforehide
1936          * Fires before this menu is hidden
1937          * @param {Roo.menu.Menu} this
1938          */
1939         beforehide : true,
1940         /**
1941          * @event show
1942          * Fires after this menu is displayed
1943          * @param {Roo.menu.Menu} this
1944          */
1945         show : true,
1946         /**
1947          * @event hide
1948          * Fires after this menu is hidden
1949          * @param {Roo.menu.Menu} this
1950          */
1951         hide : true,
1952         /**
1953          * @event click
1954          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1955          * @param {Roo.menu.Menu} this
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          * @param {Roo.EventObject} e
1958          */
1959         click : true,
1960         /**
1961          * @event mouseover
1962          * Fires when the mouse is hovering over this menu
1963          * @param {Roo.menu.Menu} this
1964          * @param {Roo.EventObject} e
1965          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1966          */
1967         mouseover : true,
1968         /**
1969          * @event mouseout
1970          * Fires when the mouse exits this menu
1971          * @param {Roo.menu.Menu} this
1972          * @param {Roo.EventObject} e
1973          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1974          */
1975         mouseout : true,
1976         /**
1977          * @event itemclick
1978          * Fires when a menu item contained in this menu is clicked
1979          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1980          * @param {Roo.EventObject} e
1981          */
1982         itemclick: true
1983     });
1984     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1985 };
1986
1987 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1988     
1989    /// html : false,
1990     //align : '',
1991     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1992     type: false,
1993     /**
1994      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1995      */
1996     registerMenu : true,
1997     
1998     menuItems :false, // stores the menu items..
1999     
2000     hidden:true,
2001         
2002     parentMenu : false,
2003     
2004     stopEvent : true,
2005     
2006     isLink : false,
2007     
2008     getChildContainer : function() {
2009         return this.el;  
2010     },
2011     
2012     getAutoCreate : function(){
2013          
2014         //if (['right'].indexOf(this.align)!==-1) {
2015         //    cfg.cn[1].cls += ' pull-right'
2016         //}
2017         
2018         
2019         var cfg = {
2020             tag : 'ul',
2021             cls : 'dropdown-menu' ,
2022             style : 'z-index:1000'
2023             
2024         };
2025         
2026         if (this.type === 'submenu') {
2027             cfg.cls = 'submenu active';
2028         }
2029         if (this.type === 'treeview') {
2030             cfg.cls = 'treeview-menu';
2031         }
2032         
2033         return cfg;
2034     },
2035     initEvents : function() {
2036         
2037        // Roo.log("ADD event");
2038        // Roo.log(this.triggerEl.dom);
2039         
2040         this.triggerEl.on('click', this.onTriggerClick, this);
2041         
2042         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2043         
2044         this.triggerEl.addClass('dropdown-toggle');
2045         
2046         if (Roo.isTouch) {
2047             this.el.on('touchstart'  , this.onTouch, this);
2048         }
2049         this.el.on('click' , this.onClick, this);
2050
2051         this.el.on("mouseover", this.onMouseOver, this);
2052         this.el.on("mouseout", this.onMouseOut, this);
2053         
2054     },
2055     
2056     findTargetItem : function(e)
2057     {
2058         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2059         if(!t){
2060             return false;
2061         }
2062         //Roo.log(t);         Roo.log(t.id);
2063         if(t && t.id){
2064             //Roo.log(this.menuitems);
2065             return this.menuitems.get(t.id);
2066             
2067             //return this.items.get(t.menuItemId);
2068         }
2069         
2070         return false;
2071     },
2072     
2073     onTouch : function(e) 
2074     {
2075         Roo.log("menu.onTouch");
2076         //e.stopEvent(); this make the user popdown broken
2077         this.onClick(e);
2078     },
2079     
2080     onClick : function(e)
2081     {
2082         Roo.log("menu.onClick");
2083         
2084         var t = this.findTargetItem(e);
2085         if(!t || t.isContainer){
2086             return;
2087         }
2088         Roo.log(e);
2089         /*
2090         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2091             if(t == this.activeItem && t.shouldDeactivate(e)){
2092                 this.activeItem.deactivate();
2093                 delete this.activeItem;
2094                 return;
2095             }
2096             if(t.canActivate){
2097                 this.setActiveItem(t, true);
2098             }
2099             return;
2100             
2101             
2102         }
2103         */
2104        
2105         Roo.log('pass click event');
2106         
2107         t.onClick(e);
2108         
2109         this.fireEvent("click", this, t, e);
2110         
2111         var _this = this;
2112         
2113         (function() { _this.hide(); }).defer(100);
2114     },
2115     
2116     onMouseOver : function(e){
2117         var t  = this.findTargetItem(e);
2118         //Roo.log(t);
2119         //if(t){
2120         //    if(t.canActivate && !t.disabled){
2121         //        this.setActiveItem(t, true);
2122         //    }
2123         //}
2124         
2125         this.fireEvent("mouseover", this, e, t);
2126     },
2127     isVisible : function(){
2128         return !this.hidden;
2129     },
2130      onMouseOut : function(e){
2131         var t  = this.findTargetItem(e);
2132         
2133         //if(t ){
2134         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2135         //        this.activeItem.deactivate();
2136         //        delete this.activeItem;
2137         //    }
2138         //}
2139         this.fireEvent("mouseout", this, e, t);
2140     },
2141     
2142     
2143     /**
2144      * Displays this menu relative to another element
2145      * @param {String/HTMLElement/Roo.Element} element The element to align to
2146      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2147      * the element (defaults to this.defaultAlign)
2148      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2149      */
2150     show : function(el, pos, parentMenu){
2151         this.parentMenu = parentMenu;
2152         if(!this.el){
2153             this.render();
2154         }
2155         this.fireEvent("beforeshow", this);
2156         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2157     },
2158      /**
2159      * Displays this menu at a specific xy position
2160      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2161      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2162      */
2163     showAt : function(xy, parentMenu, /* private: */_e){
2164         this.parentMenu = parentMenu;
2165         if(!this.el){
2166             this.render();
2167         }
2168         if(_e !== false){
2169             this.fireEvent("beforeshow", this);
2170             //xy = this.el.adjustForConstraints(xy);
2171         }
2172         
2173         //this.el.show();
2174         this.hideMenuItems();
2175         this.hidden = false;
2176         this.triggerEl.addClass('open');
2177         
2178         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2179             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2180         }
2181         
2182         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2183             this.el.setXY(xy);
2184         }
2185         
2186         this.focus();
2187         this.fireEvent("show", this);
2188     },
2189     
2190     focus : function(){
2191         return;
2192         if(!this.hidden){
2193             this.doFocus.defer(50, this);
2194         }
2195     },
2196
2197     doFocus : function(){
2198         if(!this.hidden){
2199             this.focusEl.focus();
2200         }
2201     },
2202
2203     /**
2204      * Hides this menu and optionally all parent menus
2205      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2206      */
2207     hide : function(deep)
2208     {
2209         
2210         this.hideMenuItems();
2211         if(this.el && this.isVisible()){
2212             this.fireEvent("beforehide", this);
2213             if(this.activeItem){
2214                 this.activeItem.deactivate();
2215                 this.activeItem = null;
2216             }
2217             this.triggerEl.removeClass('open');;
2218             this.hidden = true;
2219             this.fireEvent("hide", this);
2220         }
2221         if(deep === true && this.parentMenu){
2222             this.parentMenu.hide(true);
2223         }
2224     },
2225     
2226     onTriggerClick : function(e)
2227     {
2228         Roo.log('trigger click');
2229         
2230         var target = e.getTarget();
2231         
2232         Roo.log(target.nodeName.toLowerCase());
2233         
2234         if(target.nodeName.toLowerCase() === 'i'){
2235             e.preventDefault();
2236         }
2237         
2238     },
2239     
2240     onTriggerPress  : function(e)
2241     {
2242         Roo.log('trigger press');
2243         //Roo.log(e.getTarget());
2244        // Roo.log(this.triggerEl.dom);
2245        
2246         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2247         var pel = Roo.get(e.getTarget());
2248         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2249             Roo.log('is treeview or dropdown?');
2250             return;
2251         }
2252         
2253         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2254             return;
2255         }
2256         
2257         if (this.isVisible()) {
2258             Roo.log('hide');
2259             this.hide();
2260         } else {
2261             Roo.log('show');
2262             this.show(this.triggerEl, false, false);
2263         }
2264         
2265         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2266             e.stopEvent();
2267         }
2268         
2269     },
2270        
2271     
2272     hideMenuItems : function()
2273     {
2274         Roo.log("hide Menu Items");
2275         if (!this.el) { 
2276             return;
2277         }
2278         //$(backdrop).remove()
2279         this.el.select('.open',true).each(function(aa) {
2280             
2281             aa.removeClass('open');
2282           //var parent = getParent($(this))
2283           //var relatedTarget = { relatedTarget: this }
2284           
2285            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2286           //if (e.isDefaultPrevented()) return
2287            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2288         });
2289     },
2290     addxtypeChild : function (tree, cntr) {
2291         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2292           
2293         this.menuitems.add(comp);
2294         return comp;
2295
2296     },
2297     getEl : function()
2298     {
2299         Roo.log(this.el);
2300         return this.el;
2301     }
2302 });
2303
2304  
2305  /*
2306  * - LGPL
2307  *
2308  * menu item
2309  * 
2310  */
2311
2312
2313 /**
2314  * @class Roo.bootstrap.MenuItem
2315  * @extends Roo.bootstrap.Component
2316  * Bootstrap MenuItem class
2317  * @cfg {String} html the menu label
2318  * @cfg {String} href the link
2319  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2320  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2321  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2322  * @cfg {String} fa favicon to show on left of menu item.
2323  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2324  * 
2325  * 
2326  * @constructor
2327  * Create a new MenuItem
2328  * @param {Object} config The config object
2329  */
2330
2331
2332 Roo.bootstrap.MenuItem = function(config){
2333     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2334     this.addEvents({
2335         // raw events
2336         /**
2337          * @event click
2338          * The raw click event for the entire grid.
2339          * @param {Roo.bootstrap.MenuItem} this
2340          * @param {Roo.EventObject} e
2341          */
2342         "click" : true
2343     });
2344 };
2345
2346 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2347     
2348     href : false,
2349     html : false,
2350     preventDefault: false,
2351     isContainer : false,
2352     active : false,
2353     fa: false,
2354     
2355     getAutoCreate : function(){
2356         
2357         if(this.isContainer){
2358             return {
2359                 tag: 'li',
2360                 cls: 'dropdown-menu-item'
2361             };
2362         }
2363         var ctag = {
2364             tag: 'span',
2365             html: 'Link'
2366         };
2367         
2368         var anc = {
2369             tag : 'a',
2370             href : '#',
2371             cn : [  ]
2372         };
2373         
2374         if (this.fa !== false) {
2375             anc.cn.push({
2376                 tag : 'i',
2377                 cls : 'fa fa-' + this.fa
2378             });
2379         }
2380         
2381         anc.cn.push(ctag);
2382         
2383         
2384         var cfg= {
2385             tag: 'li',
2386             cls: 'dropdown-menu-item',
2387             cn: [ anc ]
2388         };
2389         if (this.parent().type == 'treeview') {
2390             cfg.cls = 'treeview-menu';
2391         }
2392         if (this.active) {
2393             cfg.cls += ' active';
2394         }
2395         
2396         
2397         
2398         anc.href = this.href || cfg.cn[0].href ;
2399         ctag.html = this.html || cfg.cn[0].html ;
2400         return cfg;
2401     },
2402     
2403     initEvents: function()
2404     {
2405         if (this.parent().type == 'treeview') {
2406             this.el.select('a').on('click', this.onClick, this);
2407         }
2408         if (this.menu) {
2409             this.menu.parentType = this.xtype;
2410             this.menu.triggerEl = this.el;
2411             this.menu = this.addxtype(Roo.apply({}, this.menu));
2412         }
2413         
2414     },
2415     onClick : function(e)
2416     {
2417         Roo.log('item on click ');
2418         
2419         if(this.preventDefault){
2420             e.preventDefault();
2421         }
2422         //this.parent().hideMenuItems();
2423         
2424         this.fireEvent('click', this, e);
2425     },
2426     getEl : function()
2427     {
2428         return this.el;
2429     } 
2430 });
2431
2432  
2433
2434  /*
2435  * - LGPL
2436  *
2437  * menu separator
2438  * 
2439  */
2440
2441
2442 /**
2443  * @class Roo.bootstrap.MenuSeparator
2444  * @extends Roo.bootstrap.Component
2445  * Bootstrap MenuSeparator class
2446  * 
2447  * @constructor
2448  * Create a new MenuItem
2449  * @param {Object} config The config object
2450  */
2451
2452
2453 Roo.bootstrap.MenuSeparator = function(config){
2454     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2455 };
2456
2457 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2458     
2459     getAutoCreate : function(){
2460         var cfg = {
2461             cls: 'divider',
2462             tag : 'li'
2463         };
2464         
2465         return cfg;
2466     }
2467    
2468 });
2469
2470  
2471
2472  
2473 /*
2474 * Licence: LGPL
2475 */
2476
2477 /**
2478  * @class Roo.bootstrap.Modal
2479  * @extends Roo.bootstrap.Component
2480  * Bootstrap Modal class
2481  * @cfg {String} title Title of dialog
2482  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2483  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2484  * @cfg {Boolean} specificTitle default false
2485  * @cfg {Array} buttons Array of buttons or standard button set..
2486  * @cfg {String} buttonPosition (left|right|center) default right
2487  * @cfg {Boolean} animate default true
2488  * @cfg {Boolean} allow_close default true
2489  * @cfg {Boolean} fitwindow default false
2490  * @cfg {String} size (sm|lg) default empty
2491  *
2492  *
2493  * @constructor
2494  * Create a new Modal Dialog
2495  * @param {Object} config The config object
2496  */
2497
2498 Roo.bootstrap.Modal = function(config){
2499     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2500     this.addEvents({
2501         // raw events
2502         /**
2503          * @event btnclick
2504          * The raw btnclick event for the button
2505          * @param {Roo.EventObject} e
2506          */
2507         "btnclick" : true,
2508         /**
2509          * @event resize
2510          * Fire when dialog resize
2511          * @param {Roo.bootstrap.Modal} this
2512          * @param {Roo.EventObject} e
2513          */
2514         "resize" : true
2515     });
2516     this.buttons = this.buttons || [];
2517
2518     if (this.tmpl) {
2519         this.tmpl = Roo.factory(this.tmpl);
2520     }
2521
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2525
2526     title : 'test dialog',
2527
2528     buttons : false,
2529
2530     // set on load...
2531
2532     html: false,
2533
2534     tmp: false,
2535
2536     specificTitle: false,
2537
2538     buttonPosition: 'right',
2539
2540     allow_close : true,
2541
2542     animate : true,
2543
2544     fitwindow: false,
2545
2546
2547      // private
2548     dialogEl: false,
2549     bodyEl:  false,
2550     footerEl:  false,
2551     titleEl:  false,
2552     closeEl:  false,
2553
2554     size: '',
2555
2556
2557     onRender : function(ct, position)
2558     {
2559         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2560
2561         if(!this.el){
2562             var cfg = Roo.apply({},  this.getAutoCreate());
2563             cfg.id = Roo.id();
2564             //if(!cfg.name){
2565             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2566             //}
2567             //if (!cfg.name.length) {
2568             //    delete cfg.name;
2569            // }
2570             if (this.cls) {
2571                 cfg.cls += ' ' + this.cls;
2572             }
2573             if (this.style) {
2574                 cfg.style = this.style;
2575             }
2576             this.el = Roo.get(document.body).createChild(cfg, position);
2577         }
2578         //var type = this.el.dom.type;
2579
2580
2581         if(this.tabIndex !== undefined){
2582             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2583         }
2584
2585         this.dialogEl = this.el.select('.modal-dialog',true).first();
2586         this.bodyEl = this.el.select('.modal-body',true).first();
2587         this.closeEl = this.el.select('.modal-header .close', true).first();
2588         this.headerEl = this.el.select('.modal-header',true).first();
2589         this.titleEl = this.el.select('.modal-title',true).first();
2590         this.footerEl = this.el.select('.modal-footer',true).first();
2591
2592         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2593         this.maskEl.enableDisplayMode("block");
2594         this.maskEl.hide();
2595         //this.el.addClass("x-dlg-modal");
2596
2597         if (this.buttons.length) {
2598             Roo.each(this.buttons, function(bb) {
2599                 var b = Roo.apply({}, bb);
2600                 b.xns = b.xns || Roo.bootstrap;
2601                 b.xtype = b.xtype || 'Button';
2602                 if (typeof(b.listeners) == 'undefined') {
2603                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2604                 }
2605
2606                 var btn = Roo.factory(b);
2607
2608                 btn.render(this.el.select('.modal-footer div').first());
2609
2610             },this);
2611         }
2612         // render the children.
2613         var nitems = [];
2614
2615         if(typeof(this.items) != 'undefined'){
2616             var items = this.items;
2617             delete this.items;
2618
2619             for(var i =0;i < items.length;i++) {
2620                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2621             }
2622         }
2623
2624         this.items = nitems;
2625
2626         // where are these used - they used to be body/close/footer
2627
2628
2629         this.initEvents();
2630         //this.el.addClass([this.fieldClass, this.cls]);
2631
2632     },
2633
2634     getAutoCreate : function(){
2635
2636
2637         var bdy = {
2638                 cls : 'modal-body',
2639                 html : this.html || ''
2640         };
2641
2642         var title = {
2643             tag: 'h4',
2644             cls : 'modal-title',
2645             html : this.title
2646         };
2647
2648         if(this.specificTitle){
2649             title = this.title;
2650
2651         };
2652
2653         var header = [];
2654         if (this.allow_close) {
2655             header.push({
2656                 tag: 'button',
2657                 cls : 'close',
2658                 html : '&times'
2659             });
2660         }
2661
2662         header.push(title);
2663
2664         var size = '';
2665
2666         if(this.size.length){
2667             size = 'modal-' + this.size;
2668         }
2669
2670         var modal = {
2671             cls: "modal",
2672             style : 'display: none',
2673             cn : [
2674                 {
2675                     cls: "modal-dialog " + size,
2676                     cn : [
2677                         {
2678                             cls : "modal-content",
2679                             cn : [
2680                                 {
2681                                     cls : 'modal-header',
2682                                     cn : header
2683                                 },
2684                                 bdy,
2685                                 {
2686                                     cls : 'modal-footer',
2687                                     cn : [
2688                                         {
2689                                             tag: 'div',
2690                                             cls: 'btn-' + this.buttonPosition
2691                                         }
2692                                     ]
2693
2694                                 }
2695
2696
2697                             ]
2698
2699                         }
2700                     ]
2701
2702                 }
2703             ]
2704         };
2705
2706         if(this.animate){
2707             modal.cls += ' fade';
2708         }
2709
2710         return modal;
2711
2712     },
2713     getChildContainer : function() {
2714
2715          return this.bodyEl;
2716
2717     },
2718     getButtonContainer : function() {
2719          return this.el.select('.modal-footer div',true).first();
2720
2721     },
2722     initEvents : function()
2723     {
2724         if (this.allow_close) {
2725             this.closeEl.on('click', this.hide, this);
2726         }
2727         Roo.EventManager.onWindowResize(this.resize, this, true);
2728
2729
2730     },
2731
2732     resize : function()
2733     {
2734         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2735         if (this.fitwindow) {
2736             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2737             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2738             this.setSize(w,h);
2739         }
2740     },
2741
2742     setSize : function(w,h)
2743     {
2744         if (!w && !h) {
2745             return;
2746         }
2747         this.resizeTo(w,h);
2748     },
2749
2750     show : function() {
2751
2752         if (!this.rendered) {
2753             this.render();
2754         }
2755
2756         this.el.setStyle('display', 'block');
2757
2758         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2759             var _this = this;
2760             (function(){
2761                 this.el.addClass('in');
2762             }).defer(50, this);
2763         }else{
2764             this.el.addClass('in');
2765
2766         }
2767
2768         // not sure how we can show data in here..
2769         //if (this.tmpl) {
2770         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2771         //}
2772
2773         Roo.get(document.body).addClass("x-body-masked");
2774         
2775         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2776         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2777         this.maskEl.show();
2778         
2779         this.resize();
2780         
2781         this.fireEvent('show', this);
2782
2783         // set zindex here - otherwise it appears to be ignored...
2784         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2785
2786         (function () {
2787             this.items.forEach( function(e) {
2788                 e.layout ? e.layout() : false;
2789
2790             });
2791         }).defer(100,this);
2792
2793     },
2794     hide : function()
2795     {
2796         if(this.fireEvent("beforehide", this) !== false){
2797             this.maskEl.hide();
2798             Roo.get(document.body).removeClass("x-body-masked");
2799             this.el.removeClass('in');
2800             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2801
2802             if(this.animate){ // why
2803                 var _this = this;
2804                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2805             }else{
2806                 this.el.setStyle('display', 'none');
2807             }
2808             this.fireEvent('hide', this);
2809         }
2810     },
2811
2812     addButton : function(str, cb)
2813     {
2814
2815
2816         var b = Roo.apply({}, { html : str } );
2817         b.xns = b.xns || Roo.bootstrap;
2818         b.xtype = b.xtype || 'Button';
2819         if (typeof(b.listeners) == 'undefined') {
2820             b.listeners = { click : cb.createDelegate(this)  };
2821         }
2822
2823         var btn = Roo.factory(b);
2824
2825         btn.render(this.el.select('.modal-footer div').first());
2826
2827         return btn;
2828
2829     },
2830
2831     setDefaultButton : function(btn)
2832     {
2833         //this.el.select('.modal-footer').()
2834     },
2835     diff : false,
2836
2837     resizeTo: function(w,h)
2838     {
2839         // skip.. ?? why??
2840
2841         this.dialogEl.setWidth(w);
2842         if (this.diff === false) {
2843             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2844         }
2845
2846         this.bodyEl.setHeight(h-this.diff);
2847
2848         this.fireEvent('resize', this);
2849
2850     },
2851     setContentSize  : function(w, h)
2852     {
2853
2854     },
2855     onButtonClick: function(btn,e)
2856     {
2857         //Roo.log([a,b,c]);
2858         this.fireEvent('btnclick', btn.name, e);
2859     },
2860      /**
2861      * Set the title of the Dialog
2862      * @param {String} str new Title
2863      */
2864     setTitle: function(str) {
2865         this.titleEl.dom.innerHTML = str;
2866     },
2867     /**
2868      * Set the body of the Dialog
2869      * @param {String} str new Title
2870      */
2871     setBody: function(str) {
2872         this.bodyEl.dom.innerHTML = str;
2873     },
2874     /**
2875      * Set the body of the Dialog using the template
2876      * @param {Obj} data - apply this data to the template and replace the body contents.
2877      */
2878     applyBody: function(obj)
2879     {
2880         if (!this.tmpl) {
2881             Roo.log("Error - using apply Body without a template");
2882             //code
2883         }
2884         this.tmpl.overwrite(this.bodyEl, obj);
2885     }
2886
2887 });
2888
2889
2890 Roo.apply(Roo.bootstrap.Modal,  {
2891     /**
2892          * Button config that displays a single OK button
2893          * @type Object
2894          */
2895         OK :  [{
2896             name : 'ok',
2897             weight : 'primary',
2898             html : 'OK'
2899         }],
2900         /**
2901          * Button config that displays Yes and No buttons
2902          * @type Object
2903          */
2904         YESNO : [
2905             {
2906                 name  : 'no',
2907                 html : 'No'
2908             },
2909             {
2910                 name  :'yes',
2911                 weight : 'primary',
2912                 html : 'Yes'
2913             }
2914         ],
2915
2916         /**
2917          * Button config that displays OK and Cancel buttons
2918          * @type Object
2919          */
2920         OKCANCEL : [
2921             {
2922                name : 'cancel',
2923                 html : 'Cancel'
2924             },
2925             {
2926                 name : 'ok',
2927                 weight : 'primary',
2928                 html : 'OK'
2929             }
2930         ],
2931         /**
2932          * Button config that displays Yes, No and Cancel buttons
2933          * @type Object
2934          */
2935         YESNOCANCEL : [
2936             {
2937                 name : 'yes',
2938                 weight : 'primary',
2939                 html : 'Yes'
2940             },
2941             {
2942                 name : 'no',
2943                 html : 'No'
2944             },
2945             {
2946                 name : 'cancel',
2947                 html : 'Cancel'
2948             }
2949         ],
2950         
2951         zIndex : 10001
2952 });
2953 /*
2954  * - LGPL
2955  *
2956  * messagebox - can be used as a replace
2957  * 
2958  */
2959 /**
2960  * @class Roo.MessageBox
2961  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2962  * Example usage:
2963  *<pre><code>
2964 // Basic alert:
2965 Roo.Msg.alert('Status', 'Changes saved successfully.');
2966
2967 // Prompt for user data:
2968 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2969     if (btn == 'ok'){
2970         // process text value...
2971     }
2972 });
2973
2974 // Show a dialog using config options:
2975 Roo.Msg.show({
2976    title:'Save Changes?',
2977    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2978    buttons: Roo.Msg.YESNOCANCEL,
2979    fn: processResult,
2980    animEl: 'elId'
2981 });
2982 </code></pre>
2983  * @singleton
2984  */
2985 Roo.bootstrap.MessageBox = function(){
2986     var dlg, opt, mask, waitTimer;
2987     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2988     var buttons, activeTextEl, bwidth;
2989
2990     
2991     // private
2992     var handleButton = function(button){
2993         dlg.hide();
2994         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2995     };
2996
2997     // private
2998     var handleHide = function(){
2999         if(opt && opt.cls){
3000             dlg.el.removeClass(opt.cls);
3001         }
3002         //if(waitTimer){
3003         //    Roo.TaskMgr.stop(waitTimer);
3004         //    waitTimer = null;
3005         //}
3006     };
3007
3008     // private
3009     var updateButtons = function(b){
3010         var width = 0;
3011         if(!b){
3012             buttons["ok"].hide();
3013             buttons["cancel"].hide();
3014             buttons["yes"].hide();
3015             buttons["no"].hide();
3016             //dlg.footer.dom.style.display = 'none';
3017             return width;
3018         }
3019         dlg.footerEl.dom.style.display = '';
3020         for(var k in buttons){
3021             if(typeof buttons[k] != "function"){
3022                 if(b[k]){
3023                     buttons[k].show();
3024                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3025                     width += buttons[k].el.getWidth()+15;
3026                 }else{
3027                     buttons[k].hide();
3028                 }
3029             }
3030         }
3031         return width;
3032     };
3033
3034     // private
3035     var handleEsc = function(d, k, e){
3036         if(opt && opt.closable !== false){
3037             dlg.hide();
3038         }
3039         if(e){
3040             e.stopEvent();
3041         }
3042     };
3043
3044     return {
3045         /**
3046          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3047          * @return {Roo.BasicDialog} The BasicDialog element
3048          */
3049         getDialog : function(){
3050            if(!dlg){
3051                 dlg = new Roo.bootstrap.Modal( {
3052                     //draggable: true,
3053                     //resizable:false,
3054                     //constraintoviewport:false,
3055                     //fixedcenter:true,
3056                     //collapsible : false,
3057                     //shim:true,
3058                     //modal: true,
3059                 //    width: 'auto',
3060                   //  height:100,
3061                     //buttonAlign:"center",
3062                     closeClick : function(){
3063                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3064                             handleButton("no");
3065                         }else{
3066                             handleButton("cancel");
3067                         }
3068                     }
3069                 });
3070                 dlg.render();
3071                 dlg.on("hide", handleHide);
3072                 mask = dlg.mask;
3073                 //dlg.addKeyListener(27, handleEsc);
3074                 buttons = {};
3075                 this.buttons = buttons;
3076                 var bt = this.buttonText;
3077                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3078                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3079                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3080                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3081                 //Roo.log(buttons);
3082                 bodyEl = dlg.bodyEl.createChild({
3083
3084                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3085                         '<textarea class="roo-mb-textarea"></textarea>' +
3086                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3087                 });
3088                 msgEl = bodyEl.dom.firstChild;
3089                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3090                 textboxEl.enableDisplayMode();
3091                 textboxEl.addKeyListener([10,13], function(){
3092                     if(dlg.isVisible() && opt && opt.buttons){
3093                         if(opt.buttons.ok){
3094                             handleButton("ok");
3095                         }else if(opt.buttons.yes){
3096                             handleButton("yes");
3097                         }
3098                     }
3099                 });
3100                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3101                 textareaEl.enableDisplayMode();
3102                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3103                 progressEl.enableDisplayMode();
3104                 
3105                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3106                 //var pf = progressEl.dom.firstChild;
3107                 //if (pf) {
3108                     //pp = Roo.get(pf.firstChild);
3109                     //pp.setHeight(pf.offsetHeight);
3110                 //}
3111                 
3112             }
3113             return dlg;
3114         },
3115
3116         /**
3117          * Updates the message box body text
3118          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3119          * the XHTML-compliant non-breaking space character '&amp;#160;')
3120          * @return {Roo.MessageBox} This message box
3121          */
3122         updateText : function(text)
3123         {
3124             if(!dlg.isVisible() && !opt.width){
3125                 dlg.dialogEl.setWidth(this.maxWidth);
3126                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3127             }
3128             msgEl.innerHTML = text || '&#160;';
3129       
3130             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3131             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3132             var w = Math.max(
3133                     Math.min(opt.width || cw , this.maxWidth), 
3134                     Math.max(opt.minWidth || this.minWidth, bwidth)
3135             );
3136             if(opt.prompt){
3137                 activeTextEl.setWidth(w);
3138             }
3139             if(dlg.isVisible()){
3140                 dlg.fixedcenter = false;
3141             }
3142             // to big, make it scroll. = But as usual stupid IE does not support
3143             // !important..
3144             
3145             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3146                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3147                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3148             } else {
3149                 bodyEl.dom.style.height = '';
3150                 bodyEl.dom.style.overflowY = '';
3151             }
3152             if (cw > w) {
3153                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3154             } else {
3155                 bodyEl.dom.style.overflowX = '';
3156             }
3157             
3158             dlg.setContentSize(w, bodyEl.getHeight());
3159             if(dlg.isVisible()){
3160                 dlg.fixedcenter = true;
3161             }
3162             return this;
3163         },
3164
3165         /**
3166          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3167          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3168          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3169          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3170          * @return {Roo.MessageBox} This message box
3171          */
3172         updateProgress : function(value, text){
3173             if(text){
3174                 this.updateText(text);
3175             }
3176             if (pp) { // weird bug on my firefox - for some reason this is not defined
3177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3178             }
3179             return this;
3180         },        
3181
3182         /**
3183          * Returns true if the message box is currently displayed
3184          * @return {Boolean} True if the message box is visible, else false
3185          */
3186         isVisible : function(){
3187             return dlg && dlg.isVisible();  
3188         },
3189
3190         /**
3191          * Hides the message box if it is displayed
3192          */
3193         hide : function(){
3194             if(this.isVisible()){
3195                 dlg.hide();
3196             }  
3197         },
3198
3199         /**
3200          * Displays a new message box, or reinitializes an existing message box, based on the config options
3201          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3202          * The following config object properties are supported:
3203          * <pre>
3204 Property    Type             Description
3205 ----------  ---------------  ------------------------------------------------------------------------------------
3206 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3207                                    closes (defaults to undefined)
3208 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3209                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3210 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3211                                    progress and wait dialogs will ignore this property and always hide the
3212                                    close button as they can only be closed programmatically.
3213 cls               String           A custom CSS class to apply to the message box element
3214 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3215                                    displayed (defaults to 75)
3216 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3217                                    function will be btn (the name of the button that was clicked, if applicable,
3218                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3219                                    Progress and wait dialogs will ignore this option since they do not respond to
3220                                    user actions and can only be closed programmatically, so any required function
3221                                    should be called by the same code after it closes the dialog.
3222 icon              String           A CSS class that provides a background image to be used as an icon for
3223                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3224 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3225 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3226 modal             Boolean          False to allow user interaction with the page while the message box is
3227                                    displayed (defaults to true)
3228 msg               String           A string that will replace the existing message box body text (defaults
3229                                    to the XHTML-compliant non-breaking space character '&#160;')
3230 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3231 progress          Boolean          True to display a progress bar (defaults to false)
3232 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3233 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3234 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3235 title             String           The title text
3236 value             String           The string value to set into the active textbox element if displayed
3237 wait              Boolean          True to display a progress bar (defaults to false)
3238 width             Number           The width of the dialog in pixels
3239 </pre>
3240          *
3241          * Example usage:
3242          * <pre><code>
3243 Roo.Msg.show({
3244    title: 'Address',
3245    msg: 'Please enter your address:',
3246    width: 300,
3247    buttons: Roo.MessageBox.OKCANCEL,
3248    multiline: true,
3249    fn: saveAddress,
3250    animEl: 'addAddressBtn'
3251 });
3252 </code></pre>
3253          * @param {Object} config Configuration options
3254          * @return {Roo.MessageBox} This message box
3255          */
3256         show : function(options)
3257         {
3258             
3259             // this causes nightmares if you show one dialog after another
3260             // especially on callbacks..
3261              
3262             if(this.isVisible()){
3263                 
3264                 this.hide();
3265                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3266                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3267                 Roo.log("New Dialog Message:" +  options.msg )
3268                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3269                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3270                 
3271             }
3272             var d = this.getDialog();
3273             opt = options;
3274             d.setTitle(opt.title || "&#160;");
3275             d.closeEl.setDisplayed(opt.closable !== false);
3276             activeTextEl = textboxEl;
3277             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3278             if(opt.prompt){
3279                 if(opt.multiline){
3280                     textboxEl.hide();
3281                     textareaEl.show();
3282                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3283                         opt.multiline : this.defaultTextHeight);
3284                     activeTextEl = textareaEl;
3285                 }else{
3286                     textboxEl.show();
3287                     textareaEl.hide();
3288                 }
3289             }else{
3290                 textboxEl.hide();
3291                 textareaEl.hide();
3292             }
3293             progressEl.setDisplayed(opt.progress === true);
3294             this.updateProgress(0);
3295             activeTextEl.dom.value = opt.value || "";
3296             if(opt.prompt){
3297                 dlg.setDefaultButton(activeTextEl);
3298             }else{
3299                 var bs = opt.buttons;
3300                 var db = null;
3301                 if(bs && bs.ok){
3302                     db = buttons["ok"];
3303                 }else if(bs && bs.yes){
3304                     db = buttons["yes"];
3305                 }
3306                 dlg.setDefaultButton(db);
3307             }
3308             bwidth = updateButtons(opt.buttons);
3309             this.updateText(opt.msg);
3310             if(opt.cls){
3311                 d.el.addClass(opt.cls);
3312             }
3313             d.proxyDrag = opt.proxyDrag === true;
3314             d.modal = opt.modal !== false;
3315             d.mask = opt.modal !== false ? mask : false;
3316             if(!d.isVisible()){
3317                 // force it to the end of the z-index stack so it gets a cursor in FF
3318                 document.body.appendChild(dlg.el.dom);
3319                 d.animateTarget = null;
3320                 d.show(options.animEl);
3321             }
3322             return this;
3323         },
3324
3325         /**
3326          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3327          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3328          * and closing the message box when the process is complete.
3329          * @param {String} title The title bar text
3330          * @param {String} msg The message box body text
3331          * @return {Roo.MessageBox} This message box
3332          */
3333         progress : function(title, msg){
3334             this.show({
3335                 title : title,
3336                 msg : msg,
3337                 buttons: false,
3338                 progress:true,
3339                 closable:false,
3340                 minWidth: this.minProgressWidth,
3341                 modal : true
3342             });
3343             return this;
3344         },
3345
3346         /**
3347          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3348          * If a callback function is passed it will be called after the user clicks the button, and the
3349          * id of the button that was clicked will be passed as the only parameter to the callback
3350          * (could also be the top-right close button).
3351          * @param {String} title The title bar text
3352          * @param {String} msg The message box body text
3353          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3354          * @param {Object} scope (optional) The scope of the callback function
3355          * @return {Roo.MessageBox} This message box
3356          */
3357         alert : function(title, msg, fn, scope)
3358         {
3359             this.show({
3360                 title : title,
3361                 msg : msg,
3362                 buttons: this.OK,
3363                 fn: fn,
3364                 closable : false,
3365                 scope : scope,
3366                 modal : true
3367             });
3368             return this;
3369         },
3370
3371         /**
3372          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3373          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3374          * You are responsible for closing the message box when the process is complete.
3375          * @param {String} msg The message box body text
3376          * @param {String} title (optional) The title bar text
3377          * @return {Roo.MessageBox} This message box
3378          */
3379         wait : function(msg, title){
3380             this.show({
3381                 title : title,
3382                 msg : msg,
3383                 buttons: false,
3384                 closable:false,
3385                 progress:true,
3386                 modal:true,
3387                 width:300,
3388                 wait:true
3389             });
3390             waitTimer = Roo.TaskMgr.start({
3391                 run: function(i){
3392                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3393                 },
3394                 interval: 1000
3395             });
3396             return this;
3397         },
3398
3399         /**
3400          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3401          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3402          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3403          * @param {String} title The title bar text
3404          * @param {String} msg The message box body text
3405          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3406          * @param {Object} scope (optional) The scope of the callback function
3407          * @return {Roo.MessageBox} This message box
3408          */
3409         confirm : function(title, msg, fn, scope){
3410             this.show({
3411                 title : title,
3412                 msg : msg,
3413                 buttons: this.YESNO,
3414                 fn: fn,
3415                 scope : scope,
3416                 modal : true
3417             });
3418             return this;
3419         },
3420
3421         /**
3422          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3423          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3424          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3425          * (could also be the top-right close button) and the text that was entered will be passed as the two
3426          * parameters to the callback.
3427          * @param {String} title The title bar text
3428          * @param {String} msg The message box body text
3429          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3430          * @param {Object} scope (optional) The scope of the callback function
3431          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3432          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3433          * @return {Roo.MessageBox} This message box
3434          */
3435         prompt : function(title, msg, fn, scope, multiline){
3436             this.show({
3437                 title : title,
3438                 msg : msg,
3439                 buttons: this.OKCANCEL,
3440                 fn: fn,
3441                 minWidth:250,
3442                 scope : scope,
3443                 prompt:true,
3444                 multiline: multiline,
3445                 modal : true
3446             });
3447             return this;
3448         },
3449
3450         /**
3451          * Button config that displays a single OK button
3452          * @type Object
3453          */
3454         OK : {ok:true},
3455         /**
3456          * Button config that displays Yes and No buttons
3457          * @type Object
3458          */
3459         YESNO : {yes:true, no:true},
3460         /**
3461          * Button config that displays OK and Cancel buttons
3462          * @type Object
3463          */
3464         OKCANCEL : {ok:true, cancel:true},
3465         /**
3466          * Button config that displays Yes, No and Cancel buttons
3467          * @type Object
3468          */
3469         YESNOCANCEL : {yes:true, no:true, cancel:true},
3470
3471         /**
3472          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3473          * @type Number
3474          */
3475         defaultTextHeight : 75,
3476         /**
3477          * The maximum width in pixels of the message box (defaults to 600)
3478          * @type Number
3479          */
3480         maxWidth : 600,
3481         /**
3482          * The minimum width in pixels of the message box (defaults to 100)
3483          * @type Number
3484          */
3485         minWidth : 100,
3486         /**
3487          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3488          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3489          * @type Number
3490          */
3491         minProgressWidth : 250,
3492         /**
3493          * An object containing the default button text strings that can be overriden for localized language support.
3494          * Supported properties are: ok, cancel, yes and no.
3495          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3496          * @type Object
3497          */
3498         buttonText : {
3499             ok : "OK",
3500             cancel : "Cancel",
3501             yes : "Yes",
3502             no : "No"
3503         }
3504     };
3505 }();
3506
3507 /**
3508  * Shorthand for {@link Roo.MessageBox}
3509  */
3510 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3511 Roo.Msg = Roo.Msg || Roo.MessageBox;
3512 /*
3513  * - LGPL
3514  *
3515  * navbar
3516  * 
3517  */
3518
3519 /**
3520  * @class Roo.bootstrap.Navbar
3521  * @extends Roo.bootstrap.Component
3522  * Bootstrap Navbar class
3523
3524  * @constructor
3525  * Create a new Navbar
3526  * @param {Object} config The config object
3527  */
3528
3529
3530 Roo.bootstrap.Navbar = function(config){
3531     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3532     this.addEvents({
3533         // raw events
3534         /**
3535          * @event beforetoggle
3536          * Fire before toggle the menu
3537          * @param {Roo.EventObject} e
3538          */
3539         "beforetoggle" : true
3540     });
3541 };
3542
3543 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3544     
3545     
3546    
3547     // private
3548     navItems : false,
3549     loadMask : false,
3550     
3551     
3552     getAutoCreate : function(){
3553         
3554         
3555         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3556         
3557     },
3558     
3559     initEvents :function ()
3560     {
3561         //Roo.log(this.el.select('.navbar-toggle',true));
3562         this.el.select('.navbar-toggle',true).on('click', function() {
3563             if(this.fireEvent('beforetoggle', this) !== false){
3564                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3565             }
3566             
3567         }, this);
3568         
3569         var mark = {
3570             tag: "div",
3571             cls:"x-dlg-mask"
3572         };
3573         
3574         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3575         
3576         var size = this.el.getSize();
3577         this.maskEl.setSize(size.width, size.height);
3578         this.maskEl.enableDisplayMode("block");
3579         this.maskEl.hide();
3580         
3581         if(this.loadMask){
3582             this.maskEl.show();
3583         }
3584     },
3585     
3586     
3587     getChildContainer : function()
3588     {
3589         if (this.el.select('.collapse').getCount()) {
3590             return this.el.select('.collapse',true).first();
3591         }
3592         
3593         return this.el;
3594     },
3595     
3596     mask : function()
3597     {
3598         this.maskEl.show();
3599     },
3600     
3601     unmask : function()
3602     {
3603         this.maskEl.hide();
3604     } 
3605     
3606     
3607     
3608     
3609 });
3610
3611
3612
3613  
3614
3615  /*
3616  * - LGPL
3617  *
3618  * navbar
3619  * 
3620  */
3621
3622 /**
3623  * @class Roo.bootstrap.NavSimplebar
3624  * @extends Roo.bootstrap.Navbar
3625  * Bootstrap Sidebar class
3626  *
3627  * @cfg {Boolean} inverse is inverted color
3628  * 
3629  * @cfg {String} type (nav | pills | tabs)
3630  * @cfg {Boolean} arrangement stacked | justified
3631  * @cfg {String} align (left | right) alignment
3632  * 
3633  * @cfg {Boolean} main (true|false) main nav bar? default false
3634  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3635  * 
3636  * @cfg {String} tag (header|footer|nav|div) default is nav 
3637
3638  * 
3639  * 
3640  * 
3641  * @constructor
3642  * Create a new Sidebar
3643  * @param {Object} config The config object
3644  */
3645
3646
3647 Roo.bootstrap.NavSimplebar = function(config){
3648     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3649 };
3650
3651 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3652     
3653     inverse: false,
3654     
3655     type: false,
3656     arrangement: '',
3657     align : false,
3658     
3659     
3660     
3661     main : false,
3662     
3663     
3664     tag : false,
3665     
3666     
3667     getAutoCreate : function(){
3668         
3669         
3670         var cfg = {
3671             tag : this.tag || 'div',
3672             cls : 'navbar'
3673         };
3674           
3675         
3676         cfg.cn = [
3677             {
3678                 cls: 'nav',
3679                 tag : 'ul'
3680             }
3681         ];
3682         
3683          
3684         this.type = this.type || 'nav';
3685         if (['tabs','pills'].indexOf(this.type)!==-1) {
3686             cfg.cn[0].cls += ' nav-' + this.type
3687         
3688         
3689         } else {
3690             if (this.type!=='nav') {
3691                 Roo.log('nav type must be nav/tabs/pills')
3692             }
3693             cfg.cn[0].cls += ' navbar-nav'
3694         }
3695         
3696         
3697         
3698         
3699         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3700             cfg.cn[0].cls += ' nav-' + this.arrangement;
3701         }
3702         
3703         
3704         if (this.align === 'right') {
3705             cfg.cn[0].cls += ' navbar-right';
3706         }
3707         
3708         if (this.inverse) {
3709             cfg.cls += ' navbar-inverse';
3710             
3711         }
3712         
3713         
3714         return cfg;
3715     
3716         
3717     }
3718     
3719     
3720     
3721 });
3722
3723
3724
3725  
3726
3727  
3728        /*
3729  * - LGPL
3730  *
3731  * navbar
3732  * 
3733  */
3734
3735 /**
3736  * @class Roo.bootstrap.NavHeaderbar
3737  * @extends Roo.bootstrap.NavSimplebar
3738  * Bootstrap Sidebar class
3739  *
3740  * @cfg {String} brand what is brand
3741  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3742  * @cfg {String} brand_href href of the brand
3743  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3744  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3745  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3746  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3747  * 
3748  * @constructor
3749  * Create a new Sidebar
3750  * @param {Object} config The config object
3751  */
3752
3753
3754 Roo.bootstrap.NavHeaderbar = function(config){
3755     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3756       
3757 };
3758
3759 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3760     
3761     position: '',
3762     brand: '',
3763     brand_href: false,
3764     srButton : true,
3765     autohide : false,
3766     desktopCenter : false,
3767    
3768     
3769     getAutoCreate : function(){
3770         
3771         var   cfg = {
3772             tag: this.nav || 'nav',
3773             cls: 'navbar',
3774             role: 'navigation',
3775             cn: []
3776         };
3777         
3778         var cn = cfg.cn;
3779         if (this.desktopCenter) {
3780             cn.push({cls : 'container', cn : []});
3781             cn = cn[0].cn;
3782         }
3783         
3784         if(this.srButton){
3785             cn.push({
3786                 tag: 'div',
3787                 cls: 'navbar-header',
3788                 cn: [
3789                     {
3790                         tag: 'button',
3791                         type: 'button',
3792                         cls: 'navbar-toggle',
3793                         'data-toggle': 'collapse',
3794                         cn: [
3795                             {
3796                                 tag: 'span',
3797                                 cls: 'sr-only',
3798                                 html: 'Toggle navigation'
3799                             },
3800                             {
3801                                 tag: 'span',
3802                                 cls: 'icon-bar'
3803                             },
3804                             {
3805                                 tag: 'span',
3806                                 cls: 'icon-bar'
3807                             },
3808                             {
3809                                 tag: 'span',
3810                                 cls: 'icon-bar'
3811                             }
3812                         ]
3813                     }
3814                 ]
3815             });
3816         }
3817         
3818         cn.push({
3819             tag: 'div',
3820             cls: 'collapse navbar-collapse',
3821             cn : []
3822         });
3823         
3824         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3825         
3826         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3827             cfg.cls += ' navbar-' + this.position;
3828             
3829             // tag can override this..
3830             
3831             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3832         }
3833         
3834         if (this.brand !== '') {
3835             cn[0].cn.push({
3836                 tag: 'a',
3837                 href: this.brand_href ? this.brand_href : '#',
3838                 cls: 'navbar-brand',
3839                 cn: [
3840                 this.brand
3841                 ]
3842             });
3843         }
3844         
3845         if(this.main){
3846             cfg.cls += ' main-nav';
3847         }
3848         
3849         
3850         return cfg;
3851
3852         
3853     },
3854     getHeaderChildContainer : function()
3855     {
3856         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3857             return this.el.select('.navbar-header',true).first();
3858         }
3859         
3860         return this.getChildContainer();
3861     },
3862     
3863     
3864     initEvents : function()
3865     {
3866         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3867         
3868         if (this.autohide) {
3869             
3870             var prevScroll = 0;
3871             var ft = this.el;
3872             
3873             Roo.get(document).on('scroll',function(e) {
3874                 var ns = Roo.get(document).getScroll().top;
3875                 var os = prevScroll;
3876                 prevScroll = ns;
3877                 
3878                 if(ns > os){
3879                     ft.removeClass('slideDown');
3880                     ft.addClass('slideUp');
3881                     return;
3882                 }
3883                 ft.removeClass('slideUp');
3884                 ft.addClass('slideDown');
3885                  
3886               
3887           },this);
3888         }
3889     }    
3890     
3891 });
3892
3893
3894
3895  
3896
3897  /*
3898  * - LGPL
3899  *
3900  * navbar
3901  * 
3902  */
3903
3904 /**
3905  * @class Roo.bootstrap.NavSidebar
3906  * @extends Roo.bootstrap.Navbar
3907  * Bootstrap Sidebar class
3908  * 
3909  * @constructor
3910  * Create a new Sidebar
3911  * @param {Object} config The config object
3912  */
3913
3914
3915 Roo.bootstrap.NavSidebar = function(config){
3916     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3917 };
3918
3919 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3920     
3921     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3922     
3923     getAutoCreate : function(){
3924         
3925         
3926         return  {
3927             tag: 'div',
3928             cls: 'sidebar sidebar-nav'
3929         };
3930     
3931         
3932     }
3933     
3934     
3935     
3936 });
3937
3938
3939
3940  
3941
3942  /*
3943  * - LGPL
3944  *
3945  * nav group
3946  * 
3947  */
3948
3949 /**
3950  * @class Roo.bootstrap.NavGroup
3951  * @extends Roo.bootstrap.Component
3952  * Bootstrap NavGroup class
3953  * @cfg {String} align (left|right)
3954  * @cfg {Boolean} inverse
3955  * @cfg {String} type (nav|pills|tab) default nav
3956  * @cfg {String} navId - reference Id for navbar.
3957
3958  * 
3959  * @constructor
3960  * Create a new nav group
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.NavGroup = function(config){
3965     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3966     this.navItems = [];
3967    
3968     Roo.bootstrap.NavGroup.register(this);
3969      this.addEvents({
3970         /**
3971              * @event changed
3972              * Fires when the active item changes
3973              * @param {Roo.bootstrap.NavGroup} this
3974              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3975              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3976          */
3977         'changed': true
3978      });
3979     
3980 };
3981
3982 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3983     
3984     align: '',
3985     inverse: false,
3986     form: false,
3987     type: 'nav',
3988     navId : '',
3989     // private
3990     
3991     navItems : false, 
3992     
3993     getAutoCreate : function()
3994     {
3995         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3996         
3997         cfg = {
3998             tag : 'ul',
3999             cls: 'nav' 
4000         };
4001         
4002         if (['tabs','pills'].indexOf(this.type)!==-1) {
4003             cfg.cls += ' nav-' + this.type
4004         } else {
4005             if (this.type!=='nav') {
4006                 Roo.log('nav type must be nav/tabs/pills')
4007             }
4008             cfg.cls += ' navbar-nav'
4009         }
4010         
4011         if (this.parent().sidebar) {
4012             cfg = {
4013                 tag: 'ul',
4014                 cls: 'dashboard-menu sidebar-menu'
4015             };
4016             
4017             return cfg;
4018         }
4019         
4020         if (this.form === true) {
4021             cfg = {
4022                 tag: 'form',
4023                 cls: 'navbar-form'
4024             };
4025             
4026             if (this.align === 'right') {
4027                 cfg.cls += ' navbar-right';
4028             } else {
4029                 cfg.cls += ' navbar-left';
4030             }
4031         }
4032         
4033         if (this.align === 'right') {
4034             cfg.cls += ' navbar-right';
4035         }
4036         
4037         if (this.inverse) {
4038             cfg.cls += ' navbar-inverse';
4039             
4040         }
4041         
4042         
4043         return cfg;
4044     },
4045     /**
4046     * sets the active Navigation item
4047     * @param {Roo.bootstrap.NavItem} the new current navitem
4048     */
4049     setActiveItem : function(item)
4050     {
4051         var prev = false;
4052         Roo.each(this.navItems, function(v){
4053             if (v == item) {
4054                 return ;
4055             }
4056             if (v.isActive()) {
4057                 v.setActive(false, true);
4058                 prev = v;
4059                 
4060             }
4061             
4062         });
4063
4064         item.setActive(true, true);
4065         this.fireEvent('changed', this, item, prev);
4066         
4067         
4068     },
4069     /**
4070     * gets the active Navigation item
4071     * @return {Roo.bootstrap.NavItem} the current navitem
4072     */
4073     getActive : function()
4074     {
4075         
4076         var prev = false;
4077         Roo.each(this.navItems, function(v){
4078             
4079             if (v.isActive()) {
4080                 prev = v;
4081                 
4082             }
4083             
4084         });
4085         return prev;
4086     },
4087     
4088     indexOfNav : function()
4089     {
4090         
4091         var prev = false;
4092         Roo.each(this.navItems, function(v,i){
4093             
4094             if (v.isActive()) {
4095                 prev = i;
4096                 
4097             }
4098             
4099         });
4100         return prev;
4101     },
4102     /**
4103     * adds a Navigation item
4104     * @param {Roo.bootstrap.NavItem} the navitem to add
4105     */
4106     addItem : function(cfg)
4107     {
4108         var cn = new Roo.bootstrap.NavItem(cfg);
4109         this.register(cn);
4110         cn.parentId = this.id;
4111         cn.onRender(this.el, null);
4112         return cn;
4113     },
4114     /**
4115     * register a Navigation item
4116     * @param {Roo.bootstrap.NavItem} the navitem to add
4117     */
4118     register : function(item)
4119     {
4120         this.navItems.push( item);
4121         item.navId = this.navId;
4122     
4123     },
4124     
4125     /**
4126     * clear all the Navigation item
4127     */
4128    
4129     clearAll : function()
4130     {
4131         this.navItems = [];
4132         this.el.dom.innerHTML = '';
4133     },
4134     
4135     getNavItem: function(tabId)
4136     {
4137         var ret = false;
4138         Roo.each(this.navItems, function(e) {
4139             if (e.tabId == tabId) {
4140                ret =  e;
4141                return false;
4142             }
4143             return true;
4144             
4145         });
4146         return ret;
4147     },
4148     
4149     setActiveNext : function()
4150     {
4151         var i = this.indexOfNav(this.getActive());
4152         if (i > this.navItems.length) {
4153             return;
4154         }
4155         this.setActiveItem(this.navItems[i+1]);
4156     },
4157     setActivePrev : function()
4158     {
4159         var i = this.indexOfNav(this.getActive());
4160         if (i  < 1) {
4161             return;
4162         }
4163         this.setActiveItem(this.navItems[i-1]);
4164     },
4165     clearWasActive : function(except) {
4166         Roo.each(this.navItems, function(e) {
4167             if (e.tabId != except.tabId && e.was_active) {
4168                e.was_active = false;
4169                return false;
4170             }
4171             return true;
4172             
4173         });
4174     },
4175     getWasActive : function ()
4176     {
4177         var r = false;
4178         Roo.each(this.navItems, function(e) {
4179             if (e.was_active) {
4180                r = e;
4181                return false;
4182             }
4183             return true;
4184             
4185         });
4186         return r;
4187     }
4188     
4189     
4190 });
4191
4192  
4193 Roo.apply(Roo.bootstrap.NavGroup, {
4194     
4195     groups: {},
4196      /**
4197     * register a Navigation Group
4198     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4199     */
4200     register : function(navgrp)
4201     {
4202         this.groups[navgrp.navId] = navgrp;
4203         
4204     },
4205     /**
4206     * fetch a Navigation Group based on the navigation ID
4207     * @param {string} the navgroup to add
4208     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4209     */
4210     get: function(navId) {
4211         if (typeof(this.groups[navId]) == 'undefined') {
4212             return false;
4213             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4214         }
4215         return this.groups[navId] ;
4216     }
4217     
4218     
4219     
4220 });
4221
4222  /*
4223  * - LGPL
4224  *
4225  * row
4226  * 
4227  */
4228
4229 /**
4230  * @class Roo.bootstrap.NavItem
4231  * @extends Roo.bootstrap.Component
4232  * Bootstrap Navbar.NavItem class
4233  * @cfg {String} href  link to
4234  * @cfg {String} html content of button
4235  * @cfg {String} badge text inside badge
4236  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4237  * @cfg {String} glyphicon name of glyphicon
4238  * @cfg {String} icon name of font awesome icon
4239  * @cfg {Boolean} active Is item active
4240  * @cfg {Boolean} disabled Is item disabled
4241  
4242  * @cfg {Boolean} preventDefault (true | false) default false
4243  * @cfg {String} tabId the tab that this item activates.
4244  * @cfg {String} tagtype (a|span) render as a href or span?
4245  * @cfg {Boolean} animateRef (true|false) link to element default false  
4246   
4247  * @constructor
4248  * Create a new Navbar Item
4249  * @param {Object} config The config object
4250  */
4251 Roo.bootstrap.NavItem = function(config){
4252     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4253     this.addEvents({
4254         // raw events
4255         /**
4256          * @event click
4257          * The raw click event for the entire grid.
4258          * @param {Roo.EventObject} e
4259          */
4260         "click" : true,
4261          /**
4262             * @event changed
4263             * Fires when the active item active state changes
4264             * @param {Roo.bootstrap.NavItem} this
4265             * @param {boolean} state the new state
4266              
4267          */
4268         'changed': true,
4269         /**
4270             * @event scrollto
4271             * Fires when scroll to element
4272             * @param {Roo.bootstrap.NavItem} this
4273             * @param {Object} options
4274             * @param {Roo.EventObject} e
4275              
4276          */
4277         'scrollto': true
4278     });
4279    
4280 };
4281
4282 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4283     
4284     href: false,
4285     html: '',
4286     badge: '',
4287     icon: false,
4288     glyphicon: false,
4289     active: false,
4290     preventDefault : false,
4291     tabId : false,
4292     tagtype : 'a',
4293     disabled : false,
4294     animateRef : false,
4295     was_active : false,
4296     
4297     getAutoCreate : function(){
4298          
4299         var cfg = {
4300             tag: 'li',
4301             cls: 'nav-item'
4302             
4303         };
4304         
4305         if (this.active) {
4306             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4307         }
4308         if (this.disabled) {
4309             cfg.cls += ' disabled';
4310         }
4311         
4312         if (this.href || this.html || this.glyphicon || this.icon) {
4313             cfg.cn = [
4314                 {
4315                     tag: this.tagtype,
4316                     href : this.href || "#",
4317                     html: this.html || ''
4318                 }
4319             ];
4320             
4321             if (this.icon) {
4322                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4323             }
4324
4325             if(this.glyphicon) {
4326                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4327             }
4328             
4329             if (this.menu) {
4330                 
4331                 cfg.cn[0].html += " <span class='caret'></span>";
4332              
4333             }
4334             
4335             if (this.badge !== '') {
4336                  
4337                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4338             }
4339         }
4340         
4341         
4342         
4343         return cfg;
4344     },
4345     initEvents: function() 
4346     {
4347         if (typeof (this.menu) != 'undefined') {
4348             this.menu.parentType = this.xtype;
4349             this.menu.triggerEl = this.el;
4350             this.menu = this.addxtype(Roo.apply({}, this.menu));
4351         }
4352         
4353         this.el.select('a',true).on('click', this.onClick, this);
4354         
4355         if(this.tagtype == 'span'){
4356             this.el.select('span',true).on('click', this.onClick, this);
4357         }
4358        
4359         // at this point parent should be available..
4360         this.parent().register(this);
4361     },
4362     
4363     onClick : function(e)
4364     {
4365         if (e.getTarget('.dropdown-menu-item')) {
4366             // did you click on a menu itemm.... - then don't trigger onclick..
4367             return;
4368         }
4369         
4370         if(
4371                 this.preventDefault || 
4372                 this.href == '#' 
4373         ){
4374             Roo.log("NavItem - prevent Default?");
4375             e.preventDefault();
4376         }
4377         
4378         if (this.disabled) {
4379             return;
4380         }
4381         
4382         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4383         if (tg && tg.transition) {
4384             Roo.log("waiting for the transitionend");
4385             return;
4386         }
4387         
4388         
4389         
4390         //Roo.log("fire event clicked");
4391         if(this.fireEvent('click', this, e) === false){
4392             return;
4393         };
4394         
4395         if(this.tagtype == 'span'){
4396             return;
4397         }
4398         
4399         //Roo.log(this.href);
4400         var ael = this.el.select('a',true).first();
4401         //Roo.log(ael);
4402         
4403         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4404             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4405             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4406                 return; // ignore... - it's a 'hash' to another page.
4407             }
4408             Roo.log("NavItem - prevent Default?");
4409             e.preventDefault();
4410             this.scrollToElement(e);
4411         }
4412         
4413         
4414         var p =  this.parent();
4415    
4416         if (['tabs','pills'].indexOf(p.type)!==-1) {
4417             if (typeof(p.setActiveItem) !== 'undefined') {
4418                 p.setActiveItem(this);
4419             }
4420         }
4421         
4422         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4423         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4424             // remove the collapsed menu expand...
4425             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4426         }
4427     },
4428     
4429     isActive: function () {
4430         return this.active
4431     },
4432     setActive : function(state, fire, is_was_active)
4433     {
4434         if (this.active && !state && this.navId) {
4435             this.was_active = true;
4436             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4437             if (nv) {
4438                 nv.clearWasActive(this);
4439             }
4440             
4441         }
4442         this.active = state;
4443         
4444         if (!state ) {
4445             this.el.removeClass('active');
4446         } else if (!this.el.hasClass('active')) {
4447             this.el.addClass('active');
4448         }
4449         if (fire) {
4450             this.fireEvent('changed', this, state);
4451         }
4452         
4453         // show a panel if it's registered and related..
4454         
4455         if (!this.navId || !this.tabId || !state || is_was_active) {
4456             return;
4457         }
4458         
4459         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4460         if (!tg) {
4461             return;
4462         }
4463         var pan = tg.getPanelByName(this.tabId);
4464         if (!pan) {
4465             return;
4466         }
4467         // if we can not flip to new panel - go back to old nav highlight..
4468         if (false == tg.showPanel(pan)) {
4469             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4470             if (nv) {
4471                 var onav = nv.getWasActive();
4472                 if (onav) {
4473                     onav.setActive(true, false, true);
4474                 }
4475             }
4476             
4477         }
4478         
4479         
4480         
4481     },
4482      // this should not be here...
4483     setDisabled : function(state)
4484     {
4485         this.disabled = state;
4486         if (!state ) {
4487             this.el.removeClass('disabled');
4488         } else if (!this.el.hasClass('disabled')) {
4489             this.el.addClass('disabled');
4490         }
4491         
4492     },
4493     
4494     /**
4495      * Fetch the element to display the tooltip on.
4496      * @return {Roo.Element} defaults to this.el
4497      */
4498     tooltipEl : function()
4499     {
4500         return this.el.select('' + this.tagtype + '', true).first();
4501     },
4502     
4503     scrollToElement : function(e)
4504     {
4505         var c = document.body;
4506         
4507         /*
4508          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4509          */
4510         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4511             c = document.documentElement;
4512         }
4513         
4514         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4515         
4516         if(!target){
4517             return;
4518         }
4519
4520         var o = target.calcOffsetsTo(c);
4521         
4522         var options = {
4523             target : target,
4524             value : o[1]
4525         };
4526         
4527         this.fireEvent('scrollto', this, options, e);
4528         
4529         Roo.get(c).scrollTo('top', options.value, true);
4530         
4531         return;
4532     }
4533 });
4534  
4535
4536  /*
4537  * - LGPL
4538  *
4539  * sidebar item
4540  *
4541  *  li
4542  *    <span> icon </span>
4543  *    <span> text </span>
4544  *    <span>badge </span>
4545  */
4546
4547 /**
4548  * @class Roo.bootstrap.NavSidebarItem
4549  * @extends Roo.bootstrap.NavItem
4550  * Bootstrap Navbar.NavSidebarItem class
4551  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4552  * {bool} open is the menu open
4553  * @constructor
4554  * Create a new Navbar Button
4555  * @param {Object} config The config object
4556  */
4557 Roo.bootstrap.NavSidebarItem = function(config){
4558     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4559     this.addEvents({
4560         // raw events
4561         /**
4562          * @event click
4563          * The raw click event for the entire grid.
4564          * @param {Roo.EventObject} e
4565          */
4566         "click" : true,
4567          /**
4568             * @event changed
4569             * Fires when the active item active state changes
4570             * @param {Roo.bootstrap.NavSidebarItem} this
4571             * @param {boolean} state the new state
4572              
4573          */
4574         'changed': true
4575     });
4576    
4577 };
4578
4579 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4580     
4581     badgeWeight : 'default',
4582     
4583     open: false,
4584     
4585     getAutoCreate : function(){
4586         
4587         
4588         var a = {
4589                 tag: 'a',
4590                 href : this.href || '#',
4591                 cls: '',
4592                 html : '',
4593                 cn : []
4594         };
4595         var cfg = {
4596             tag: 'li',
4597             cls: '',
4598             cn: [ a ]
4599         };
4600         var span = {
4601             tag: 'span',
4602             html : this.html || ''
4603         };
4604         
4605         
4606         if (this.active) {
4607             cfg.cls += ' active';
4608         }
4609         
4610         if (this.disabled) {
4611             cfg.cls += ' disabled';
4612         }
4613         if (this.open) {
4614             cfg.cls += ' open x-open';
4615         }
4616         // left icon..
4617         if (this.glyphicon || this.icon) {
4618             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4619             a.cn.push({ tag : 'i', cls : c }) ;
4620         }
4621         // html..
4622         a.cn.push(span);
4623         // then badge..
4624         if (this.badge !== '') {
4625             
4626             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4627         }
4628         // fi
4629         if (this.menu) {
4630             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4631             a.cls += 'dropdown-toggle treeview' ;
4632         }
4633         
4634         return cfg;
4635          
4636            
4637     },
4638     
4639     initEvents : function()
4640     { 
4641         if (typeof (this.menu) != 'undefined') {
4642             this.menu.parentType = this.xtype;
4643             this.menu.triggerEl = this.el;
4644             this.menu = this.addxtype(Roo.apply({}, this.menu));
4645         }
4646         
4647         this.el.on('click', this.onClick, this);
4648        
4649     
4650         if(this.badge !== ''){
4651  
4652             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4653         }
4654         
4655     },
4656     
4657     onClick : function(e)
4658     {
4659         if(this.disabled){
4660             e.preventDefault();
4661             return;
4662         }
4663         
4664         if(this.preventDefault){
4665             e.preventDefault();
4666         }
4667         
4668         this.fireEvent('click', this);
4669     },
4670     
4671     disable : function()
4672     {
4673         this.setDisabled(true);
4674     },
4675     
4676     enable : function()
4677     {
4678         this.setDisabled(false);
4679     },
4680     
4681     setDisabled : function(state)
4682     {
4683         if(this.disabled == state){
4684             return;
4685         }
4686         
4687         this.disabled = state;
4688         
4689         if (state) {
4690             this.el.addClass('disabled');
4691             return;
4692         }
4693         
4694         this.el.removeClass('disabled');
4695         
4696         return;
4697     },
4698     
4699     setActive : function(state)
4700     {
4701         if(this.active == state){
4702             return;
4703         }
4704         
4705         this.active = state;
4706         
4707         if (state) {
4708             this.el.addClass('active');
4709             return;
4710         }
4711         
4712         this.el.removeClass('active');
4713         
4714         return;
4715     },
4716     
4717     isActive: function () 
4718     {
4719         return this.active;
4720     },
4721     
4722     setBadge : function(str)
4723     {
4724         if(!this.badgeEl){
4725             return;
4726         }
4727         
4728         this.badgeEl.dom.innerHTML = str;
4729     }
4730     
4731    
4732      
4733  
4734 });
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * row
4741  * 
4742  */
4743
4744 /**
4745  * @class Roo.bootstrap.Row
4746  * @extends Roo.bootstrap.Component
4747  * Bootstrap Row class (contains columns...)
4748  * 
4749  * @constructor
4750  * Create a new Row
4751  * @param {Object} config The config object
4752  */
4753
4754 Roo.bootstrap.Row = function(config){
4755     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4756 };
4757
4758 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4759     
4760     getAutoCreate : function(){
4761        return {
4762             cls: 'row clearfix'
4763        };
4764     }
4765     
4766     
4767 });
4768
4769  
4770
4771  /*
4772  * - LGPL
4773  *
4774  * element
4775  * 
4776  */
4777
4778 /**
4779  * @class Roo.bootstrap.Element
4780  * @extends Roo.bootstrap.Component
4781  * Bootstrap Element class
4782  * @cfg {String} html contents of the element
4783  * @cfg {String} tag tag of the element
4784  * @cfg {String} cls class of the element
4785  * @cfg {Boolean} preventDefault (true|false) default false
4786  * @cfg {Boolean} clickable (true|false) default false
4787  * 
4788  * @constructor
4789  * Create a new Element
4790  * @param {Object} config The config object
4791  */
4792
4793 Roo.bootstrap.Element = function(config){
4794     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4795     
4796     this.addEvents({
4797         // raw events
4798         /**
4799          * @event click
4800          * When a element is chick
4801          * @param {Roo.bootstrap.Element} this
4802          * @param {Roo.EventObject} e
4803          */
4804         "click" : true
4805     });
4806 };
4807
4808 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4809     
4810     tag: 'div',
4811     cls: '',
4812     html: '',
4813     preventDefault: false, 
4814     clickable: false,
4815     
4816     getAutoCreate : function(){
4817         
4818         var cfg = {
4819             tag: this.tag,
4820             cls: this.cls,
4821             html: this.html
4822         };
4823         
4824         return cfg;
4825     },
4826     
4827     initEvents: function() 
4828     {
4829         Roo.bootstrap.Element.superclass.initEvents.call(this);
4830         
4831         if(this.clickable){
4832             this.el.on('click', this.onClick, this);
4833         }
4834         
4835     },
4836     
4837     onClick : function(e)
4838     {
4839         if(this.preventDefault){
4840             e.preventDefault();
4841         }
4842         
4843         this.fireEvent('click', this, e);
4844     },
4845     
4846     getValue : function()
4847     {
4848         return this.el.dom.innerHTML;
4849     },
4850     
4851     setValue : function(value)
4852     {
4853         this.el.dom.innerHTML = value;
4854     }
4855    
4856 });
4857
4858  
4859
4860  /*
4861  * - LGPL
4862  *
4863  * pagination
4864  * 
4865  */
4866
4867 /**
4868  * @class Roo.bootstrap.Pagination
4869  * @extends Roo.bootstrap.Component
4870  * Bootstrap Pagination class
4871  * @cfg {String} size xs | sm | md | lg
4872  * @cfg {Boolean} inverse false | true
4873  * 
4874  * @constructor
4875  * Create a new Pagination
4876  * @param {Object} config The config object
4877  */
4878
4879 Roo.bootstrap.Pagination = function(config){
4880     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4881 };
4882
4883 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4884     
4885     cls: false,
4886     size: false,
4887     inverse: false,
4888     
4889     getAutoCreate : function(){
4890         var cfg = {
4891             tag: 'ul',
4892                 cls: 'pagination'
4893         };
4894         if (this.inverse) {
4895             cfg.cls += ' inverse';
4896         }
4897         if (this.html) {
4898             cfg.html=this.html;
4899         }
4900         if (this.cls) {
4901             cfg.cls += " " + this.cls;
4902         }
4903         return cfg;
4904     }
4905    
4906 });
4907
4908  
4909
4910  /*
4911  * - LGPL
4912  *
4913  * Pagination item
4914  * 
4915  */
4916
4917
4918 /**
4919  * @class Roo.bootstrap.PaginationItem
4920  * @extends Roo.bootstrap.Component
4921  * Bootstrap PaginationItem class
4922  * @cfg {String} html text
4923  * @cfg {String} href the link
4924  * @cfg {Boolean} preventDefault (true | false) default true
4925  * @cfg {Boolean} active (true | false) default false
4926  * @cfg {Boolean} disabled default false
4927  * 
4928  * 
4929  * @constructor
4930  * Create a new PaginationItem
4931  * @param {Object} config The config object
4932  */
4933
4934
4935 Roo.bootstrap.PaginationItem = function(config){
4936     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4937     this.addEvents({
4938         // raw events
4939         /**
4940          * @event click
4941          * The raw click event for the entire grid.
4942          * @param {Roo.EventObject} e
4943          */
4944         "click" : true
4945     });
4946 };
4947
4948 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4949     
4950     href : false,
4951     html : false,
4952     preventDefault: true,
4953     active : false,
4954     cls : false,
4955     disabled: false,
4956     
4957     getAutoCreate : function(){
4958         var cfg= {
4959             tag: 'li',
4960             cn: [
4961                 {
4962                     tag : 'a',
4963                     href : this.href ? this.href : '#',
4964                     html : this.html ? this.html : ''
4965                 }
4966             ]
4967         };
4968         
4969         if(this.cls){
4970             cfg.cls = this.cls;
4971         }
4972         
4973         if(this.disabled){
4974             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4975         }
4976         
4977         if(this.active){
4978             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4979         }
4980         
4981         return cfg;
4982     },
4983     
4984     initEvents: function() {
4985         
4986         this.el.on('click', this.onClick, this);
4987         
4988     },
4989     onClick : function(e)
4990     {
4991         Roo.log('PaginationItem on click ');
4992         if(this.preventDefault){
4993             e.preventDefault();
4994         }
4995         
4996         if(this.disabled){
4997             return;
4998         }
4999         
5000         this.fireEvent('click', this, e);
5001     }
5002    
5003 });
5004
5005  
5006
5007  /*
5008  * - LGPL
5009  *
5010  * slider
5011  * 
5012  */
5013
5014
5015 /**
5016  * @class Roo.bootstrap.Slider
5017  * @extends Roo.bootstrap.Component
5018  * Bootstrap Slider class
5019  *    
5020  * @constructor
5021  * Create a new Slider
5022  * @param {Object} config The config object
5023  */
5024
5025 Roo.bootstrap.Slider = function(config){
5026     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5027 };
5028
5029 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5030     
5031     getAutoCreate : function(){
5032         
5033         var cfg = {
5034             tag: 'div',
5035             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5036             cn: [
5037                 {
5038                     tag: 'a',
5039                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5040                 }
5041             ]
5042         };
5043         
5044         return cfg;
5045     }
5046    
5047 });
5048
5049  /*
5050  * Based on:
5051  * Ext JS Library 1.1.1
5052  * Copyright(c) 2006-2007, Ext JS, LLC.
5053  *
5054  * Originally Released Under LGPL - original licence link has changed is not relivant.
5055  *
5056  * Fork - LGPL
5057  * <script type="text/javascript">
5058  */
5059  
5060
5061 /**
5062  * @class Roo.grid.ColumnModel
5063  * @extends Roo.util.Observable
5064  * This is the default implementation of a ColumnModel used by the Grid. It defines
5065  * the columns in the grid.
5066  * <br>Usage:<br>
5067  <pre><code>
5068  var colModel = new Roo.grid.ColumnModel([
5069         {header: "Ticker", width: 60, sortable: true, locked: true},
5070         {header: "Company Name", width: 150, sortable: true},
5071         {header: "Market Cap.", width: 100, sortable: true},
5072         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5073         {header: "Employees", width: 100, sortable: true, resizable: false}
5074  ]);
5075  </code></pre>
5076  * <p>
5077  
5078  * The config options listed for this class are options which may appear in each
5079  * individual column definition.
5080  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5081  * @constructor
5082  * @param {Object} config An Array of column config objects. See this class's
5083  * config objects for details.
5084 */
5085 Roo.grid.ColumnModel = function(config){
5086         /**
5087      * The config passed into the constructor
5088      */
5089     this.config = config;
5090     this.lookup = {};
5091
5092     // if no id, create one
5093     // if the column does not have a dataIndex mapping,
5094     // map it to the order it is in the config
5095     for(var i = 0, len = config.length; i < len; i++){
5096         var c = config[i];
5097         if(typeof c.dataIndex == "undefined"){
5098             c.dataIndex = i;
5099         }
5100         if(typeof c.renderer == "string"){
5101             c.renderer = Roo.util.Format[c.renderer];
5102         }
5103         if(typeof c.id == "undefined"){
5104             c.id = Roo.id();
5105         }
5106         if(c.editor && c.editor.xtype){
5107             c.editor  = Roo.factory(c.editor, Roo.grid);
5108         }
5109         if(c.editor && c.editor.isFormField){
5110             c.editor = new Roo.grid.GridEditor(c.editor);
5111         }
5112         this.lookup[c.id] = c;
5113     }
5114
5115     /**
5116      * The width of columns which have no width specified (defaults to 100)
5117      * @type Number
5118      */
5119     this.defaultWidth = 100;
5120
5121     /**
5122      * Default sortable of columns which have no sortable specified (defaults to false)
5123      * @type Boolean
5124      */
5125     this.defaultSortable = false;
5126
5127     this.addEvents({
5128         /**
5129              * @event widthchange
5130              * Fires when the width of a column changes.
5131              * @param {ColumnModel} this
5132              * @param {Number} columnIndex The column index
5133              * @param {Number} newWidth The new width
5134              */
5135             "widthchange": true,
5136         /**
5137              * @event headerchange
5138              * Fires when the text of a header changes.
5139              * @param {ColumnModel} this
5140              * @param {Number} columnIndex The column index
5141              * @param {Number} newText The new header text
5142              */
5143             "headerchange": true,
5144         /**
5145              * @event hiddenchange
5146              * Fires when a column is hidden or "unhidden".
5147              * @param {ColumnModel} this
5148              * @param {Number} columnIndex The column index
5149              * @param {Boolean} hidden true if hidden, false otherwise
5150              */
5151             "hiddenchange": true,
5152             /**
5153          * @event columnmoved
5154          * Fires when a column is moved.
5155          * @param {ColumnModel} this
5156          * @param {Number} oldIndex
5157          * @param {Number} newIndex
5158          */
5159         "columnmoved" : true,
5160         /**
5161          * @event columlockchange
5162          * Fires when a column's locked state is changed
5163          * @param {ColumnModel} this
5164          * @param {Number} colIndex
5165          * @param {Boolean} locked true if locked
5166          */
5167         "columnlockchange" : true
5168     });
5169     Roo.grid.ColumnModel.superclass.constructor.call(this);
5170 };
5171 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5172     /**
5173      * @cfg {String} header The header text to display in the Grid view.
5174      */
5175     /**
5176      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5177      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5178      * specified, the column's index is used as an index into the Record's data Array.
5179      */
5180     /**
5181      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5182      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5183      */
5184     /**
5185      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5186      * Defaults to the value of the {@link #defaultSortable} property.
5187      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5188      */
5189     /**
5190      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5191      */
5192     /**
5193      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5194      */
5195     /**
5196      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5197      */
5198     /**
5199      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5200      */
5201     /**
5202      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5203      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5204      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5205      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5206      */
5207        /**
5208      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5209      */
5210     /**
5211      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5212      */
5213     /**
5214      * @cfg {String} cursor (Optional)
5215      */
5216     /**
5217      * @cfg {String} tooltip (Optional)
5218      */
5219     /**
5220      * @cfg {Number} xs (Optional)
5221      */
5222     /**
5223      * @cfg {Number} sm (Optional)
5224      */
5225     /**
5226      * @cfg {Number} md (Optional)
5227      */
5228     /**
5229      * @cfg {Number} lg (Optional)
5230      */
5231     /**
5232      * Returns the id of the column at the specified index.
5233      * @param {Number} index The column index
5234      * @return {String} the id
5235      */
5236     getColumnId : function(index){
5237         return this.config[index].id;
5238     },
5239
5240     /**
5241      * Returns the column for a specified id.
5242      * @param {String} id The column id
5243      * @return {Object} the column
5244      */
5245     getColumnById : function(id){
5246         return this.lookup[id];
5247     },
5248
5249     
5250     /**
5251      * Returns the column for a specified dataIndex.
5252      * @param {String} dataIndex The column dataIndex
5253      * @return {Object|Boolean} the column or false if not found
5254      */
5255     getColumnByDataIndex: function(dataIndex){
5256         var index = this.findColumnIndex(dataIndex);
5257         return index > -1 ? this.config[index] : false;
5258     },
5259     
5260     /**
5261      * Returns the index for a specified column id.
5262      * @param {String} id The column id
5263      * @return {Number} the index, or -1 if not found
5264      */
5265     getIndexById : function(id){
5266         for(var i = 0, len = this.config.length; i < len; i++){
5267             if(this.config[i].id == id){
5268                 return i;
5269             }
5270         }
5271         return -1;
5272     },
5273     
5274     /**
5275      * Returns the index for a specified column dataIndex.
5276      * @param {String} dataIndex The column dataIndex
5277      * @return {Number} the index, or -1 if not found
5278      */
5279     
5280     findColumnIndex : function(dataIndex){
5281         for(var i = 0, len = this.config.length; i < len; i++){
5282             if(this.config[i].dataIndex == dataIndex){
5283                 return i;
5284             }
5285         }
5286         return -1;
5287     },
5288     
5289     
5290     moveColumn : function(oldIndex, newIndex){
5291         var c = this.config[oldIndex];
5292         this.config.splice(oldIndex, 1);
5293         this.config.splice(newIndex, 0, c);
5294         this.dataMap = null;
5295         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5296     },
5297
5298     isLocked : function(colIndex){
5299         return this.config[colIndex].locked === true;
5300     },
5301
5302     setLocked : function(colIndex, value, suppressEvent){
5303         if(this.isLocked(colIndex) == value){
5304             return;
5305         }
5306         this.config[colIndex].locked = value;
5307         if(!suppressEvent){
5308             this.fireEvent("columnlockchange", this, colIndex, value);
5309         }
5310     },
5311
5312     getTotalLockedWidth : function(){
5313         var totalWidth = 0;
5314         for(var i = 0; i < this.config.length; i++){
5315             if(this.isLocked(i) && !this.isHidden(i)){
5316                 this.totalWidth += this.getColumnWidth(i);
5317             }
5318         }
5319         return totalWidth;
5320     },
5321
5322     getLockedCount : function(){
5323         for(var i = 0, len = this.config.length; i < len; i++){
5324             if(!this.isLocked(i)){
5325                 return i;
5326             }
5327         }
5328         
5329         return this.config.length;
5330     },
5331
5332     /**
5333      * Returns the number of columns.
5334      * @return {Number}
5335      */
5336     getColumnCount : function(visibleOnly){
5337         if(visibleOnly === true){
5338             var c = 0;
5339             for(var i = 0, len = this.config.length; i < len; i++){
5340                 if(!this.isHidden(i)){
5341                     c++;
5342                 }
5343             }
5344             return c;
5345         }
5346         return this.config.length;
5347     },
5348
5349     /**
5350      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5351      * @param {Function} fn
5352      * @param {Object} scope (optional)
5353      * @return {Array} result
5354      */
5355     getColumnsBy : function(fn, scope){
5356         var r = [];
5357         for(var i = 0, len = this.config.length; i < len; i++){
5358             var c = this.config[i];
5359             if(fn.call(scope||this, c, i) === true){
5360                 r[r.length] = c;
5361             }
5362         }
5363         return r;
5364     },
5365
5366     /**
5367      * Returns true if the specified column is sortable.
5368      * @param {Number} col The column index
5369      * @return {Boolean}
5370      */
5371     isSortable : function(col){
5372         if(typeof this.config[col].sortable == "undefined"){
5373             return this.defaultSortable;
5374         }
5375         return this.config[col].sortable;
5376     },
5377
5378     /**
5379      * Returns the rendering (formatting) function defined for the column.
5380      * @param {Number} col The column index.
5381      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5382      */
5383     getRenderer : function(col){
5384         if(!this.config[col].renderer){
5385             return Roo.grid.ColumnModel.defaultRenderer;
5386         }
5387         return this.config[col].renderer;
5388     },
5389
5390     /**
5391      * Sets the rendering (formatting) function for a column.
5392      * @param {Number} col The column index
5393      * @param {Function} fn The function to use to process the cell's raw data
5394      * to return HTML markup for the grid view. The render function is called with
5395      * the following parameters:<ul>
5396      * <li>Data value.</li>
5397      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5398      * <li>css A CSS style string to apply to the table cell.</li>
5399      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5400      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5401      * <li>Row index</li>
5402      * <li>Column index</li>
5403      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5404      */
5405     setRenderer : function(col, fn){
5406         this.config[col].renderer = fn;
5407     },
5408
5409     /**
5410      * Returns the width for the specified column.
5411      * @param {Number} col The column index
5412      * @return {Number}
5413      */
5414     getColumnWidth : function(col){
5415         return this.config[col].width * 1 || this.defaultWidth;
5416     },
5417
5418     /**
5419      * Sets the width for a column.
5420      * @param {Number} col The column index
5421      * @param {Number} width The new width
5422      */
5423     setColumnWidth : function(col, width, suppressEvent){
5424         this.config[col].width = width;
5425         this.totalWidth = null;
5426         if(!suppressEvent){
5427              this.fireEvent("widthchange", this, col, width);
5428         }
5429     },
5430
5431     /**
5432      * Returns the total width of all columns.
5433      * @param {Boolean} includeHidden True to include hidden column widths
5434      * @return {Number}
5435      */
5436     getTotalWidth : function(includeHidden){
5437         if(!this.totalWidth){
5438             this.totalWidth = 0;
5439             for(var i = 0, len = this.config.length; i < len; i++){
5440                 if(includeHidden || !this.isHidden(i)){
5441                     this.totalWidth += this.getColumnWidth(i);
5442                 }
5443             }
5444         }
5445         return this.totalWidth;
5446     },
5447
5448     /**
5449      * Returns the header for the specified column.
5450      * @param {Number} col The column index
5451      * @return {String}
5452      */
5453     getColumnHeader : function(col){
5454         return this.config[col].header;
5455     },
5456
5457     /**
5458      * Sets the header for a column.
5459      * @param {Number} col The column index
5460      * @param {String} header The new header
5461      */
5462     setColumnHeader : function(col, header){
5463         this.config[col].header = header;
5464         this.fireEvent("headerchange", this, col, header);
5465     },
5466
5467     /**
5468      * Returns the tooltip for the specified column.
5469      * @param {Number} col The column index
5470      * @return {String}
5471      */
5472     getColumnTooltip : function(col){
5473             return this.config[col].tooltip;
5474     },
5475     /**
5476      * Sets the tooltip for a column.
5477      * @param {Number} col The column index
5478      * @param {String} tooltip The new tooltip
5479      */
5480     setColumnTooltip : function(col, tooltip){
5481             this.config[col].tooltip = tooltip;
5482     },
5483
5484     /**
5485      * Returns the dataIndex for the specified column.
5486      * @param {Number} col The column index
5487      * @return {Number}
5488      */
5489     getDataIndex : function(col){
5490         return this.config[col].dataIndex;
5491     },
5492
5493     /**
5494      * Sets the dataIndex for a column.
5495      * @param {Number} col The column index
5496      * @param {Number} dataIndex The new dataIndex
5497      */
5498     setDataIndex : function(col, dataIndex){
5499         this.config[col].dataIndex = dataIndex;
5500     },
5501
5502     
5503     
5504     /**
5505      * Returns true if the cell is editable.
5506      * @param {Number} colIndex The column index
5507      * @param {Number} rowIndex The row index - this is nto actually used..?
5508      * @return {Boolean}
5509      */
5510     isCellEditable : function(colIndex, rowIndex){
5511         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5512     },
5513
5514     /**
5515      * Returns the editor defined for the cell/column.
5516      * return false or null to disable editing.
5517      * @param {Number} colIndex The column index
5518      * @param {Number} rowIndex The row index
5519      * @return {Object}
5520      */
5521     getCellEditor : function(colIndex, rowIndex){
5522         return this.config[colIndex].editor;
5523     },
5524
5525     /**
5526      * Sets if a column is editable.
5527      * @param {Number} col The column index
5528      * @param {Boolean} editable True if the column is editable
5529      */
5530     setEditable : function(col, editable){
5531         this.config[col].editable = editable;
5532     },
5533
5534
5535     /**
5536      * Returns true if the column is hidden.
5537      * @param {Number} colIndex The column index
5538      * @return {Boolean}
5539      */
5540     isHidden : function(colIndex){
5541         return this.config[colIndex].hidden;
5542     },
5543
5544
5545     /**
5546      * Returns true if the column width cannot be changed
5547      */
5548     isFixed : function(colIndex){
5549         return this.config[colIndex].fixed;
5550     },
5551
5552     /**
5553      * Returns true if the column can be resized
5554      * @return {Boolean}
5555      */
5556     isResizable : function(colIndex){
5557         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5558     },
5559     /**
5560      * Sets if a column is hidden.
5561      * @param {Number} colIndex The column index
5562      * @param {Boolean} hidden True if the column is hidden
5563      */
5564     setHidden : function(colIndex, hidden){
5565         this.config[colIndex].hidden = hidden;
5566         this.totalWidth = null;
5567         this.fireEvent("hiddenchange", this, colIndex, hidden);
5568     },
5569
5570     /**
5571      * Sets the editor for a column.
5572      * @param {Number} col The column index
5573      * @param {Object} editor The editor object
5574      */
5575     setEditor : function(col, editor){
5576         this.config[col].editor = editor;
5577     }
5578 });
5579
5580 Roo.grid.ColumnModel.defaultRenderer = function(value)
5581 {
5582     if(typeof value == "object") {
5583         return value;
5584     }
5585         if(typeof value == "string" && value.length < 1){
5586             return "&#160;";
5587         }
5588     
5589         return String.format("{0}", value);
5590 };
5591
5592 // Alias for backwards compatibility
5593 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5594 /*
5595  * Based on:
5596  * Ext JS Library 1.1.1
5597  * Copyright(c) 2006-2007, Ext JS, LLC.
5598  *
5599  * Originally Released Under LGPL - original licence link has changed is not relivant.
5600  *
5601  * Fork - LGPL
5602  * <script type="text/javascript">
5603  */
5604  
5605 /**
5606  * @class Roo.LoadMask
5607  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5608  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5609  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5610  * element's UpdateManager load indicator and will be destroyed after the initial load.
5611  * @constructor
5612  * Create a new LoadMask
5613  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5614  * @param {Object} config The config object
5615  */
5616 Roo.LoadMask = function(el, config){
5617     this.el = Roo.get(el);
5618     Roo.apply(this, config);
5619     if(this.store){
5620         this.store.on('beforeload', this.onBeforeLoad, this);
5621         this.store.on('load', this.onLoad, this);
5622         this.store.on('loadexception', this.onLoadException, this);
5623         this.removeMask = false;
5624     }else{
5625         var um = this.el.getUpdateManager();
5626         um.showLoadIndicator = false; // disable the default indicator
5627         um.on('beforeupdate', this.onBeforeLoad, this);
5628         um.on('update', this.onLoad, this);
5629         um.on('failure', this.onLoad, this);
5630         this.removeMask = true;
5631     }
5632 };
5633
5634 Roo.LoadMask.prototype = {
5635     /**
5636      * @cfg {Boolean} removeMask
5637      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5638      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5639      */
5640     /**
5641      * @cfg {String} msg
5642      * The text to display in a centered loading message box (defaults to 'Loading...')
5643      */
5644     msg : 'Loading...',
5645     /**
5646      * @cfg {String} msgCls
5647      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5648      */
5649     msgCls : 'x-mask-loading',
5650
5651     /**
5652      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5653      * @type Boolean
5654      */
5655     disabled: false,
5656
5657     /**
5658      * Disables the mask to prevent it from being displayed
5659      */
5660     disable : function(){
5661        this.disabled = true;
5662     },
5663
5664     /**
5665      * Enables the mask so that it can be displayed
5666      */
5667     enable : function(){
5668         this.disabled = false;
5669     },
5670     
5671     onLoadException : function()
5672     {
5673         Roo.log(arguments);
5674         
5675         if (typeof(arguments[3]) != 'undefined') {
5676             Roo.MessageBox.alert("Error loading",arguments[3]);
5677         } 
5678         /*
5679         try {
5680             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5681                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5682             }   
5683         } catch(e) {
5684             
5685         }
5686         */
5687     
5688         
5689         
5690         this.el.unmask(this.removeMask);
5691     },
5692     // private
5693     onLoad : function()
5694     {
5695         this.el.unmask(this.removeMask);
5696     },
5697
5698     // private
5699     onBeforeLoad : function(){
5700         if(!this.disabled){
5701             (function() { this.el.mask(this.msg, this.msgCls) }).defer(50, this);
5702         }
5703     },
5704
5705     // private
5706     destroy : function(){
5707         if(this.store){
5708             this.store.un('beforeload', this.onBeforeLoad, this);
5709             this.store.un('load', this.onLoad, this);
5710             this.store.un('loadexception', this.onLoadException, this);
5711         }else{
5712             var um = this.el.getUpdateManager();
5713             um.un('beforeupdate', this.onBeforeLoad, this);
5714             um.un('update', this.onLoad, this);
5715             um.un('failure', this.onLoad, this);
5716         }
5717     }
5718 };/*
5719  * - LGPL
5720  *
5721  * table
5722  * 
5723  */
5724
5725 /**
5726  * @class Roo.bootstrap.Table
5727  * @extends Roo.bootstrap.Component
5728  * Bootstrap Table class
5729  * @cfg {String} cls table class
5730  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5731  * @cfg {String} bgcolor Specifies the background color for a table
5732  * @cfg {Number} border Specifies whether the table cells should have borders or not
5733  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5734  * @cfg {Number} cellspacing Specifies the space between cells
5735  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5736  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5737  * @cfg {String} sortable Specifies that the table should be sortable
5738  * @cfg {String} summary Specifies a summary of the content of a table
5739  * @cfg {Number} width Specifies the width of a table
5740  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5741  * 
5742  * @cfg {boolean} striped Should the rows be alternative striped
5743  * @cfg {boolean} bordered Add borders to the table
5744  * @cfg {boolean} hover Add hover highlighting
5745  * @cfg {boolean} condensed Format condensed
5746  * @cfg {boolean} responsive Format condensed
5747  * @cfg {Boolean} loadMask (true|false) default false
5748  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5749  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5750  * @cfg {Boolean} rowSelection (true|false) default false
5751  * @cfg {Boolean} cellSelection (true|false) default false
5752  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5753  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5754  
5755  * 
5756  * @constructor
5757  * Create a new Table
5758  * @param {Object} config The config object
5759  */
5760
5761 Roo.bootstrap.Table = function(config){
5762     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5763     
5764   
5765     
5766     // BC...
5767     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5768     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5769     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5770     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5771     
5772     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5773     if (this.sm) {
5774         this.sm.grid = this;
5775         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5776         this.sm = this.selModel;
5777         this.sm.xmodule = this.xmodule || false;
5778     }
5779     
5780     if (this.cm && typeof(this.cm.config) == 'undefined') {
5781         this.colModel = new Roo.grid.ColumnModel(this.cm);
5782         this.cm = this.colModel;
5783         this.cm.xmodule = this.xmodule || false;
5784     }
5785     if (this.store) {
5786         this.store= Roo.factory(this.store, Roo.data);
5787         this.ds = this.store;
5788         this.ds.xmodule = this.xmodule || false;
5789          
5790     }
5791     if (this.footer && this.store) {
5792         this.footer.dataSource = this.ds;
5793         this.footer = Roo.factory(this.footer);
5794     }
5795     
5796     /** @private */
5797     this.addEvents({
5798         /**
5799          * @event cellclick
5800          * Fires when a cell is clicked
5801          * @param {Roo.bootstrap.Table} this
5802          * @param {Roo.Element} el
5803          * @param {Number} rowIndex
5804          * @param {Number} columnIndex
5805          * @param {Roo.EventObject} e
5806          */
5807         "cellclick" : true,
5808         /**
5809          * @event celldblclick
5810          * Fires when a cell is double clicked
5811          * @param {Roo.bootstrap.Table} this
5812          * @param {Roo.Element} el
5813          * @param {Number} rowIndex
5814          * @param {Number} columnIndex
5815          * @param {Roo.EventObject} e
5816          */
5817         "celldblclick" : true,
5818         /**
5819          * @event rowclick
5820          * Fires when a row is clicked
5821          * @param {Roo.bootstrap.Table} this
5822          * @param {Roo.Element} el
5823          * @param {Number} rowIndex
5824          * @param {Roo.EventObject} e
5825          */
5826         "rowclick" : true,
5827         /**
5828          * @event rowdblclick
5829          * Fires when a row is double clicked
5830          * @param {Roo.bootstrap.Table} this
5831          * @param {Roo.Element} el
5832          * @param {Number} rowIndex
5833          * @param {Roo.EventObject} e
5834          */
5835         "rowdblclick" : true,
5836         /**
5837          * @event mouseover
5838          * Fires when a mouseover occur
5839          * @param {Roo.bootstrap.Table} this
5840          * @param {Roo.Element} el
5841          * @param {Number} rowIndex
5842          * @param {Number} columnIndex
5843          * @param {Roo.EventObject} e
5844          */
5845         "mouseover" : true,
5846         /**
5847          * @event mouseout
5848          * Fires when a mouseout occur
5849          * @param {Roo.bootstrap.Table} this
5850          * @param {Roo.Element} el
5851          * @param {Number} rowIndex
5852          * @param {Number} columnIndex
5853          * @param {Roo.EventObject} e
5854          */
5855         "mouseout" : true,
5856         /**
5857          * @event rowclass
5858          * Fires when a row is rendered, so you can change add a style to it.
5859          * @param {Roo.bootstrap.Table} this
5860          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5861          */
5862         'rowclass' : true,
5863           /**
5864          * @event rowsrendered
5865          * Fires when all the  rows have been rendered
5866          * @param {Roo.bootstrap.Table} this
5867          */
5868         'rowsrendered' : true,
5869         /**
5870          * @event contextmenu
5871          * The raw contextmenu event for the entire grid.
5872          * @param {Roo.EventObject} e
5873          */
5874         "contextmenu" : true,
5875         /**
5876          * @event rowcontextmenu
5877          * Fires when a row is right clicked
5878          * @param {Roo.bootstrap.Table} this
5879          * @param {Number} rowIndex
5880          * @param {Roo.EventObject} e
5881          */
5882         "rowcontextmenu" : true,
5883         /**
5884          * @event cellcontextmenu
5885          * Fires when a cell is right clicked
5886          * @param {Roo.bootstrap.Table} this
5887          * @param {Number} rowIndex
5888          * @param {Number} cellIndex
5889          * @param {Roo.EventObject} e
5890          */
5891          "cellcontextmenu" : true,
5892          /**
5893          * @event headercontextmenu
5894          * Fires when a header is right clicked
5895          * @param {Roo.bootstrap.Table} this
5896          * @param {Number} columnIndex
5897          * @param {Roo.EventObject} e
5898          */
5899         "headercontextmenu" : true
5900     });
5901 };
5902
5903 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5904     
5905     cls: false,
5906     align: false,
5907     bgcolor: false,
5908     border: false,
5909     cellpadding: false,
5910     cellspacing: false,
5911     frame: false,
5912     rules: false,
5913     sortable: false,
5914     summary: false,
5915     width: false,
5916     striped : false,
5917     scrollBody : false,
5918     bordered: false,
5919     hover:  false,
5920     condensed : false,
5921     responsive : false,
5922     sm : false,
5923     cm : false,
5924     store : false,
5925     loadMask : false,
5926     footerShow : true,
5927     headerShow : true,
5928   
5929     rowSelection : false,
5930     cellSelection : false,
5931     layout : false,
5932     
5933     // Roo.Element - the tbody
5934     mainBody: false,
5935     // Roo.Element - thead element
5936     mainHead: false,
5937     
5938     container: false, // used by gridpanel...
5939     
5940     getAutoCreate : function()
5941     {
5942         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5943         
5944         cfg = {
5945             tag: 'table',
5946             cls : 'table',
5947             cn : []
5948         };
5949         if (this.scrollBody) {
5950             cfg.cls += ' table-body-fixed';
5951         }    
5952         if (this.striped) {
5953             cfg.cls += ' table-striped';
5954         }
5955         
5956         if (this.hover) {
5957             cfg.cls += ' table-hover';
5958         }
5959         if (this.bordered) {
5960             cfg.cls += ' table-bordered';
5961         }
5962         if (this.condensed) {
5963             cfg.cls += ' table-condensed';
5964         }
5965         if (this.responsive) {
5966             cfg.cls += ' table-responsive';
5967         }
5968         
5969         if (this.cls) {
5970             cfg.cls+=  ' ' +this.cls;
5971         }
5972         
5973         // this lot should be simplifed...
5974         
5975         if (this.align) {
5976             cfg.align=this.align;
5977         }
5978         if (this.bgcolor) {
5979             cfg.bgcolor=this.bgcolor;
5980         }
5981         if (this.border) {
5982             cfg.border=this.border;
5983         }
5984         if (this.cellpadding) {
5985             cfg.cellpadding=this.cellpadding;
5986         }
5987         if (this.cellspacing) {
5988             cfg.cellspacing=this.cellspacing;
5989         }
5990         if (this.frame) {
5991             cfg.frame=this.frame;
5992         }
5993         if (this.rules) {
5994             cfg.rules=this.rules;
5995         }
5996         if (this.sortable) {
5997             cfg.sortable=this.sortable;
5998         }
5999         if (this.summary) {
6000             cfg.summary=this.summary;
6001         }
6002         if (this.width) {
6003             cfg.width=this.width;
6004         }
6005         if (this.layout) {
6006             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6007         }
6008         
6009         if(this.store || this.cm){
6010             if(this.headerShow){
6011                 cfg.cn.push(this.renderHeader());
6012             }
6013             
6014             cfg.cn.push(this.renderBody());
6015             
6016             if(this.footerShow){
6017                 cfg.cn.push(this.renderFooter());
6018             }
6019             // where does this come from?
6020             //cfg.cls+=  ' TableGrid';
6021         }
6022         
6023         return { cn : [ cfg ] };
6024     },
6025     
6026     initEvents : function()
6027     {   
6028         if(!this.store || !this.cm){
6029             return;
6030         }
6031         if (this.selModel) {
6032             this.selModel.initEvents();
6033         }
6034         
6035         
6036         //Roo.log('initEvents with ds!!!!');
6037         
6038         this.mainBody = this.el.select('tbody', true).first();
6039         this.mainHead = this.el.select('thead', true).first();
6040         
6041         
6042         
6043         
6044         var _this = this;
6045         
6046         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6047             e.on('click', _this.sort, _this);
6048         });
6049         
6050         this.mainBody.on("click", this.onClick, this);
6051         this.mainBody.on("dblclick", this.onDblClick, this);
6052         
6053         // why is this done????? = it breaks dialogs??
6054         //this.parent().el.setStyle('position', 'relative');
6055         
6056         
6057         if (this.footer) {
6058             this.footer.parentId = this.id;
6059             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6060         } 
6061         
6062         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6063         
6064         this.store.on('load', this.onLoad, this);
6065         this.store.on('beforeload', this.onBeforeLoad, this);
6066         this.store.on('update', this.onUpdate, this);
6067         this.store.on('add', this.onAdd, this);
6068         this.store.on("clear", this.clear, this);
6069         
6070         this.el.on("contextmenu", this.onContextMenu, this);
6071         
6072         this.mainBody.on('scroll', this.onBodyScroll, this);
6073         
6074         
6075     },
6076     
6077     onContextMenu : function(e, t)
6078     {
6079         this.processEvent("contextmenu", e);
6080     },
6081     
6082     processEvent : function(name, e)
6083     {
6084         if (name != 'touchstart' ) {
6085             this.fireEvent(name, e);    
6086         }
6087         
6088         var t = e.getTarget();
6089         
6090         var cell = Roo.get(t);
6091         
6092         if(!cell){
6093             return;
6094         }
6095         
6096         if(cell.findParent('tfoot', false, true)){
6097             return;
6098         }
6099         
6100         if(cell.findParent('thead', false, true)){
6101             
6102             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6103                 cell = Roo.get(t).findParent('th', false, true);
6104                 if (!cell) {
6105                     Roo.log("failed to find th in thead?");
6106                     Roo.log(e.getTarget());
6107                     return;
6108                 }
6109             }
6110             
6111             var cellIndex = cell.dom.cellIndex;
6112             
6113             var ename = name == 'touchstart' ? 'click' : name;
6114             this.fireEvent("header" + ename, this, cellIndex, e);
6115             
6116             return;
6117         }
6118         
6119         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6120             cell = Roo.get(t).findParent('td', false, true);
6121             if (!cell) {
6122                 Roo.log("failed to find th in tbody?");
6123                 Roo.log(e.getTarget());
6124                 return;
6125             }
6126         }
6127         
6128         var row = cell.findParent('tr', false, true);
6129         var cellIndex = cell.dom.cellIndex;
6130         var rowIndex = row.dom.rowIndex - 1;
6131         
6132         if(row !== false){
6133             
6134             this.fireEvent("row" + name, this, rowIndex, e);
6135             
6136             if(cell !== false){
6137             
6138                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6139             }
6140         }
6141         
6142     },
6143     
6144     onMouseover : function(e, el)
6145     {
6146         var cell = Roo.get(el);
6147         
6148         if(!cell){
6149             return;
6150         }
6151         
6152         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6153             cell = cell.findParent('td', false, true);
6154         }
6155         
6156         var row = cell.findParent('tr', false, true);
6157         var cellIndex = cell.dom.cellIndex;
6158         var rowIndex = row.dom.rowIndex - 1; // start from 0
6159         
6160         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6161         
6162     },
6163     
6164     onMouseout : function(e, el)
6165     {
6166         var cell = Roo.get(el);
6167         
6168         if(!cell){
6169             return;
6170         }
6171         
6172         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6173             cell = cell.findParent('td', false, true);
6174         }
6175         
6176         var row = cell.findParent('tr', false, true);
6177         var cellIndex = cell.dom.cellIndex;
6178         var rowIndex = row.dom.rowIndex - 1; // start from 0
6179         
6180         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6181         
6182     },
6183     
6184     onClick : function(e, el)
6185     {
6186         var cell = Roo.get(el);
6187         
6188         if(!cell || (!this.cellSelection && !this.rowSelection)){
6189             return;
6190         }
6191         
6192         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6193             cell = cell.findParent('td', false, true);
6194         }
6195         
6196         if(!cell || typeof(cell) == 'undefined'){
6197             return;
6198         }
6199         
6200         var row = cell.findParent('tr', false, true);
6201         
6202         if(!row || typeof(row) == 'undefined'){
6203             return;
6204         }
6205         
6206         var cellIndex = cell.dom.cellIndex;
6207         var rowIndex = this.getRowIndex(row);
6208         
6209         // why??? - should these not be based on SelectionModel?
6210         if(this.cellSelection){
6211             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6212         }
6213         
6214         if(this.rowSelection){
6215             this.fireEvent('rowclick', this, row, rowIndex, e);
6216         }
6217         
6218         
6219     },
6220         
6221     onDblClick : function(e,el)
6222     {
6223         var cell = Roo.get(el);
6224         
6225         if(!cell || (!this.cellSelection && !this.rowSelection)){
6226             return;
6227         }
6228         
6229         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6230             cell = cell.findParent('td', false, true);
6231         }
6232         
6233         if(!cell || typeof(cell) == 'undefined'){
6234             return;
6235         }
6236         
6237         var row = cell.findParent('tr', false, true);
6238         
6239         if(!row || typeof(row) == 'undefined'){
6240             return;
6241         }
6242         
6243         var cellIndex = cell.dom.cellIndex;
6244         var rowIndex = this.getRowIndex(row);
6245         
6246         if(this.cellSelection){
6247             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6248         }
6249         
6250         if(this.rowSelection){
6251             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6252         }
6253     },
6254     
6255     sort : function(e,el)
6256     {
6257         var col = Roo.get(el);
6258         
6259         if(!col.hasClass('sortable')){
6260             return;
6261         }
6262         
6263         var sort = col.attr('sort');
6264         var dir = 'ASC';
6265         
6266         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6267             dir = 'DESC';
6268         }
6269         
6270         this.store.sortInfo = {field : sort, direction : dir};
6271         
6272         if (this.footer) {
6273             Roo.log("calling footer first");
6274             this.footer.onClick('first');
6275         } else {
6276         
6277             this.store.load({ params : { start : 0 } });
6278         }
6279     },
6280     
6281     renderHeader : function()
6282     {
6283         var header = {
6284             tag: 'thead',
6285             cn : []
6286         };
6287         
6288         var cm = this.cm;
6289         this.totalWidth = 0;
6290         
6291         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6292             
6293             var config = cm.config[i];
6294             
6295             var c = {
6296                 tag: 'th',
6297                 style : '',
6298                 html: cm.getColumnHeader(i)
6299             };
6300             
6301             var hh = '';
6302             
6303             if(typeof(config.sortable) != 'undefined' && config.sortable){
6304                 c.cls = 'sortable';
6305                 c.html = '<i class="glyphicon"></i>' + c.html;
6306             }
6307             
6308             if(typeof(config.lgHeader) != 'undefined'){
6309                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6310             }
6311             
6312             if(typeof(config.mdHeader) != 'undefined'){
6313                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6314             }
6315             
6316             if(typeof(config.smHeader) != 'undefined'){
6317                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6318             }
6319             
6320             if(typeof(config.xsHeader) != 'undefined'){
6321                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6322             }
6323             
6324             if(hh.length){
6325                 c.html = hh;
6326             }
6327             
6328             if(typeof(config.tooltip) != 'undefined'){
6329                 c.tooltip = config.tooltip;
6330             }
6331             
6332             if(typeof(config.colspan) != 'undefined'){
6333                 c.colspan = config.colspan;
6334             }
6335             
6336             if(typeof(config.hidden) != 'undefined' && config.hidden){
6337                 c.style += ' display:none;';
6338             }
6339             
6340             if(typeof(config.dataIndex) != 'undefined'){
6341                 c.sort = config.dataIndex;
6342             }
6343             
6344            
6345             
6346             if(typeof(config.align) != 'undefined' && config.align.length){
6347                 c.style += ' text-align:' + config.align + ';';
6348             }
6349             
6350             if(typeof(config.width) != 'undefined'){
6351                 c.style += ' width:' + config.width + 'px;';
6352                 this.totalWidth += config.width;
6353             } else {
6354                 this.totalWidth += 100; // assume minimum of 100 per column?
6355             }
6356             
6357             if(typeof(config.cls) != 'undefined'){
6358                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6359             }
6360             
6361             ['xs','sm','md','lg'].map(function(size){
6362                 
6363                 if(typeof(config[size]) == 'undefined'){
6364                     return;
6365                 }
6366                 
6367                 if (!config[size]) { // 0 = hidden
6368                     c.cls += ' hidden-' + size;
6369                     return;
6370                 }
6371                 
6372                 c.cls += ' col-' + size + '-' + config[size];
6373
6374             });
6375             
6376             header.cn.push(c)
6377         }
6378         
6379         return header;
6380     },
6381     
6382     renderBody : function()
6383     {
6384         var body = {
6385             tag: 'tbody',
6386             cn : [
6387                 {
6388                     tag: 'tr',
6389                     cn : [
6390                         {
6391                             tag : 'td',
6392                             colspan :  this.cm.getColumnCount()
6393                         }
6394                     ]
6395                 }
6396             ]
6397         };
6398         
6399         return body;
6400     },
6401     
6402     renderFooter : function()
6403     {
6404         var footer = {
6405             tag: 'tfoot',
6406             cn : [
6407                 {
6408                     tag: 'tr',
6409                     cn : [
6410                         {
6411                             tag : 'td',
6412                             colspan :  this.cm.getColumnCount()
6413                         }
6414                     ]
6415                 }
6416             ]
6417         };
6418         
6419         return footer;
6420     },
6421     
6422     
6423     
6424     onLoad : function()
6425     {
6426 //        Roo.log('ds onload');
6427         this.clear();
6428         
6429         var _this = this;
6430         var cm = this.cm;
6431         var ds = this.store;
6432         
6433         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6434             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6435             if (_this.store.sortInfo) {
6436                     
6437                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6438                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6439                 }
6440                 
6441                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6442                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6443                 }
6444             }
6445         });
6446         
6447         var tbody =  this.mainBody;
6448               
6449         if(ds.getCount() > 0){
6450             ds.data.each(function(d,rowIndex){
6451                 var row =  this.renderRow(cm, ds, rowIndex);
6452                 
6453                 tbody.createChild(row);
6454                 
6455                 var _this = this;
6456                 
6457                 if(row.cellObjects.length){
6458                     Roo.each(row.cellObjects, function(r){
6459                         _this.renderCellObject(r);
6460                     })
6461                 }
6462                 
6463             }, this);
6464         }
6465         
6466         Roo.each(this.el.select('tbody td', true).elements, function(e){
6467             e.on('mouseover', _this.onMouseover, _this);
6468         });
6469         
6470         Roo.each(this.el.select('tbody td', true).elements, function(e){
6471             e.on('mouseout', _this.onMouseout, _this);
6472         });
6473         this.fireEvent('rowsrendered', this);
6474         //if(this.loadMask){
6475         //    this.maskEl.hide();
6476         //}
6477         
6478         this.autoSize();
6479     },
6480     
6481     
6482     onUpdate : function(ds,record)
6483     {
6484         this.refreshRow(record);
6485         this.autoSize();
6486     },
6487     
6488     onRemove : function(ds, record, index, isUpdate){
6489         if(isUpdate !== true){
6490             this.fireEvent("beforerowremoved", this, index, record);
6491         }
6492         var bt = this.mainBody.dom;
6493         
6494         var rows = this.el.select('tbody > tr', true).elements;
6495         
6496         if(typeof(rows[index]) != 'undefined'){
6497             bt.removeChild(rows[index].dom);
6498         }
6499         
6500 //        if(bt.rows[index]){
6501 //            bt.removeChild(bt.rows[index]);
6502 //        }
6503         
6504         if(isUpdate !== true){
6505             //this.stripeRows(index);
6506             //this.syncRowHeights(index, index);
6507             //this.layout();
6508             this.fireEvent("rowremoved", this, index, record);
6509         }
6510     },
6511     
6512     onAdd : function(ds, records, rowIndex)
6513     {
6514         //Roo.log('on Add called');
6515         // - note this does not handle multiple adding very well..
6516         var bt = this.mainBody.dom;
6517         for (var i =0 ; i < records.length;i++) {
6518             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6519             //Roo.log(records[i]);
6520             //Roo.log(this.store.getAt(rowIndex+i));
6521             this.insertRow(this.store, rowIndex + i, false);
6522             return;
6523         }
6524         
6525     },
6526     
6527     
6528     refreshRow : function(record){
6529         var ds = this.store, index;
6530         if(typeof record == 'number'){
6531             index = record;
6532             record = ds.getAt(index);
6533         }else{
6534             index = ds.indexOf(record);
6535         }
6536         this.insertRow(ds, index, true);
6537         this.autoSize();
6538         this.onRemove(ds, record, index+1, true);
6539         this.autoSize();
6540         //this.syncRowHeights(index, index);
6541         //this.layout();
6542         this.fireEvent("rowupdated", this, index, record);
6543     },
6544     
6545     insertRow : function(dm, rowIndex, isUpdate){
6546         
6547         if(!isUpdate){
6548             this.fireEvent("beforerowsinserted", this, rowIndex);
6549         }
6550             //var s = this.getScrollState();
6551         var row = this.renderRow(this.cm, this.store, rowIndex);
6552         // insert before rowIndex..
6553         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6554         
6555         var _this = this;
6556                 
6557         if(row.cellObjects.length){
6558             Roo.each(row.cellObjects, function(r){
6559                 _this.renderCellObject(r);
6560             })
6561         }
6562             
6563         if(!isUpdate){
6564             this.fireEvent("rowsinserted", this, rowIndex);
6565             //this.syncRowHeights(firstRow, lastRow);
6566             //this.stripeRows(firstRow);
6567             //this.layout();
6568         }
6569         
6570     },
6571     
6572     
6573     getRowDom : function(rowIndex)
6574     {
6575         var rows = this.el.select('tbody > tr', true).elements;
6576         
6577         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6578         
6579     },
6580     // returns the object tree for a tr..
6581   
6582     
6583     renderRow : function(cm, ds, rowIndex) 
6584     {
6585         
6586         var d = ds.getAt(rowIndex);
6587         
6588         var row = {
6589             tag : 'tr',
6590             cn : []
6591         };
6592             
6593         var cellObjects = [];
6594         
6595         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6596             var config = cm.config[i];
6597             
6598             var renderer = cm.getRenderer(i);
6599             var value = '';
6600             var id = false;
6601             
6602             if(typeof(renderer) !== 'undefined'){
6603                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6604             }
6605             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6606             // and are rendered into the cells after the row is rendered - using the id for the element.
6607             
6608             if(typeof(value) === 'object'){
6609                 id = Roo.id();
6610                 cellObjects.push({
6611                     container : id,
6612                     cfg : value 
6613                 })
6614             }
6615             
6616             var rowcfg = {
6617                 record: d,
6618                 rowIndex : rowIndex,
6619                 colIndex : i,
6620                 rowClass : ''
6621             };
6622
6623             this.fireEvent('rowclass', this, rowcfg);
6624             
6625             var td = {
6626                 tag: 'td',
6627                 cls : rowcfg.rowClass,
6628                 style: '',
6629                 html: (typeof(value) === 'object') ? '' : value
6630             };
6631             
6632             if (id) {
6633                 td.id = id;
6634             }
6635             
6636             if(typeof(config.colspan) != 'undefined'){
6637                 td.colspan = config.colspan;
6638             }
6639             
6640             if(typeof(config.hidden) != 'undefined' && config.hidden){
6641                 td.style += ' display:none;';
6642             }
6643             
6644             if(typeof(config.align) != 'undefined' && config.align.length){
6645                 td.style += ' text-align:' + config.align + ';';
6646             }
6647             
6648             if(typeof(config.width) != 'undefined'){
6649                 td.style += ' width:' +  config.width + 'px;';
6650             }
6651             
6652             if(typeof(config.cursor) != 'undefined'){
6653                 td.style += ' cursor:' +  config.cursor + ';';
6654             }
6655             
6656             if(typeof(config.cls) != 'undefined'){
6657                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6658             }
6659             
6660             ['xs','sm','md','lg'].map(function(size){
6661                 
6662                 if(typeof(config[size]) == 'undefined'){
6663                     return;
6664                 }
6665                 
6666                 if (!config[size]) { // 0 = hidden
6667                     td.cls += ' hidden-' + size;
6668                     return;
6669                 }
6670                 
6671                 td.cls += ' col-' + size + '-' + config[size];
6672
6673             });
6674              
6675             row.cn.push(td);
6676            
6677         }
6678         
6679         row.cellObjects = cellObjects;
6680         
6681         return row;
6682           
6683     },
6684     
6685     
6686     
6687     onBeforeLoad : function()
6688     {
6689         //Roo.log('ds onBeforeLoad');
6690         
6691         //this.clear();
6692         
6693         //if(this.loadMask){
6694         //    this.maskEl.show();
6695         //}
6696     },
6697      /**
6698      * Remove all rows
6699      */
6700     clear : function()
6701     {
6702         this.el.select('tbody', true).first().dom.innerHTML = '';
6703     },
6704     /**
6705      * Show or hide a row.
6706      * @param {Number} rowIndex to show or hide
6707      * @param {Boolean} state hide
6708      */
6709     setRowVisibility : function(rowIndex, state)
6710     {
6711         var bt = this.mainBody.dom;
6712         
6713         var rows = this.el.select('tbody > tr', true).elements;
6714         
6715         if(typeof(rows[rowIndex]) == 'undefined'){
6716             return;
6717         }
6718         rows[rowIndex].dom.style.display = state ? '' : 'none';
6719     },
6720     
6721     
6722     getSelectionModel : function(){
6723         if(!this.selModel){
6724             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6725         }
6726         return this.selModel;
6727     },
6728     /*
6729      * Render the Roo.bootstrap object from renderder
6730      */
6731     renderCellObject : function(r)
6732     {
6733         var _this = this;
6734         
6735         var t = r.cfg.render(r.container);
6736         
6737         if(r.cfg.cn){
6738             Roo.each(r.cfg.cn, function(c){
6739                 var child = {
6740                     container: t.getChildContainer(),
6741                     cfg: c
6742                 };
6743                 _this.renderCellObject(child);
6744             })
6745         }
6746     },
6747     
6748     getRowIndex : function(row)
6749     {
6750         var rowIndex = -1;
6751         
6752         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6753             if(el != row){
6754                 return;
6755             }
6756             
6757             rowIndex = index;
6758         });
6759         
6760         return rowIndex;
6761     },
6762      /**
6763      * Returns the grid's underlying element = used by panel.Grid
6764      * @return {Element} The element
6765      */
6766     getGridEl : function(){
6767         return this.el;
6768     },
6769      /**
6770      * Forces a resize - used by panel.Grid
6771      * @return {Element} The element
6772      */
6773     autoSize : function()
6774     {
6775         //var ctr = Roo.get(this.container.dom.parentElement);
6776         var ctr = Roo.get(this.el.dom);
6777         
6778         var thd = this.getGridEl().select('thead',true).first();
6779         var tbd = this.getGridEl().select('tbody', true).first();
6780         var tfd = this.getGridEl().select('tfoot', true).first();
6781         
6782         var cw = ctr.getWidth();
6783         
6784         if (tbd) {
6785             
6786             tbd.setSize(ctr.getWidth(),
6787                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6788             );
6789             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6790             cw -= barsize;
6791         }
6792         cw = Math.max(cw, this.totalWidth);
6793         this.getGridEl().select('tr',true).setWidth(cw);
6794         // resize 'expandable coloumn?
6795         
6796         return; // we doe not have a view in this design..
6797         
6798     },
6799     onBodyScroll: function()
6800     {
6801         
6802         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6803         this.mainHead.setStyle({
6804                     'position' : 'relative',
6805                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6806         });
6807         
6808         
6809     }
6810 });
6811
6812  
6813
6814  /*
6815  * - LGPL
6816  *
6817  * table cell
6818  * 
6819  */
6820
6821 /**
6822  * @class Roo.bootstrap.TableCell
6823  * @extends Roo.bootstrap.Component
6824  * Bootstrap TableCell class
6825  * @cfg {String} html cell contain text
6826  * @cfg {String} cls cell class
6827  * @cfg {String} tag cell tag (td|th) default td
6828  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6829  * @cfg {String} align Aligns the content in a cell
6830  * @cfg {String} axis Categorizes cells
6831  * @cfg {String} bgcolor Specifies the background color of a cell
6832  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6833  * @cfg {Number} colspan Specifies the number of columns a cell should span
6834  * @cfg {String} headers Specifies one or more header cells a cell is related to
6835  * @cfg {Number} height Sets the height of a cell
6836  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6837  * @cfg {Number} rowspan Sets the number of rows a cell should span
6838  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6839  * @cfg {String} valign Vertical aligns the content in a cell
6840  * @cfg {Number} width Specifies the width of a cell
6841  * 
6842  * @constructor
6843  * Create a new TableCell
6844  * @param {Object} config The config object
6845  */
6846
6847 Roo.bootstrap.TableCell = function(config){
6848     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6849 };
6850
6851 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6852     
6853     html: false,
6854     cls: false,
6855     tag: false,
6856     abbr: false,
6857     align: false,
6858     axis: false,
6859     bgcolor: false,
6860     charoff: false,
6861     colspan: false,
6862     headers: false,
6863     height: false,
6864     nowrap: false,
6865     rowspan: false,
6866     scope: false,
6867     valign: false,
6868     width: false,
6869     
6870     
6871     getAutoCreate : function(){
6872         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6873         
6874         cfg = {
6875             tag: 'td'
6876         };
6877         
6878         if(this.tag){
6879             cfg.tag = this.tag;
6880         }
6881         
6882         if (this.html) {
6883             cfg.html=this.html
6884         }
6885         if (this.cls) {
6886             cfg.cls=this.cls
6887         }
6888         if (this.abbr) {
6889             cfg.abbr=this.abbr
6890         }
6891         if (this.align) {
6892             cfg.align=this.align
6893         }
6894         if (this.axis) {
6895             cfg.axis=this.axis
6896         }
6897         if (this.bgcolor) {
6898             cfg.bgcolor=this.bgcolor
6899         }
6900         if (this.charoff) {
6901             cfg.charoff=this.charoff
6902         }
6903         if (this.colspan) {
6904             cfg.colspan=this.colspan
6905         }
6906         if (this.headers) {
6907             cfg.headers=this.headers
6908         }
6909         if (this.height) {
6910             cfg.height=this.height
6911         }
6912         if (this.nowrap) {
6913             cfg.nowrap=this.nowrap
6914         }
6915         if (this.rowspan) {
6916             cfg.rowspan=this.rowspan
6917         }
6918         if (this.scope) {
6919             cfg.scope=this.scope
6920         }
6921         if (this.valign) {
6922             cfg.valign=this.valign
6923         }
6924         if (this.width) {
6925             cfg.width=this.width
6926         }
6927         
6928         
6929         return cfg;
6930     }
6931    
6932 });
6933
6934  
6935
6936  /*
6937  * - LGPL
6938  *
6939  * table row
6940  * 
6941  */
6942
6943 /**
6944  * @class Roo.bootstrap.TableRow
6945  * @extends Roo.bootstrap.Component
6946  * Bootstrap TableRow class
6947  * @cfg {String} cls row class
6948  * @cfg {String} align Aligns the content in a table row
6949  * @cfg {String} bgcolor Specifies a background color for a table row
6950  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6951  * @cfg {String} valign Vertical aligns the content in a table row
6952  * 
6953  * @constructor
6954  * Create a new TableRow
6955  * @param {Object} config The config object
6956  */
6957
6958 Roo.bootstrap.TableRow = function(config){
6959     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6960 };
6961
6962 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6963     
6964     cls: false,
6965     align: false,
6966     bgcolor: false,
6967     charoff: false,
6968     valign: false,
6969     
6970     getAutoCreate : function(){
6971         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6972         
6973         cfg = {
6974             tag: 'tr'
6975         };
6976             
6977         if(this.cls){
6978             cfg.cls = this.cls;
6979         }
6980         if(this.align){
6981             cfg.align = this.align;
6982         }
6983         if(this.bgcolor){
6984             cfg.bgcolor = this.bgcolor;
6985         }
6986         if(this.charoff){
6987             cfg.charoff = this.charoff;
6988         }
6989         if(this.valign){
6990             cfg.valign = this.valign;
6991         }
6992         
6993         return cfg;
6994     }
6995    
6996 });
6997
6998  
6999
7000  /*
7001  * - LGPL
7002  *
7003  * table body
7004  * 
7005  */
7006
7007 /**
7008  * @class Roo.bootstrap.TableBody
7009  * @extends Roo.bootstrap.Component
7010  * Bootstrap TableBody class
7011  * @cfg {String} cls element class
7012  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7013  * @cfg {String} align Aligns the content inside the element
7014  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7015  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7016  * 
7017  * @constructor
7018  * Create a new TableBody
7019  * @param {Object} config The config object
7020  */
7021
7022 Roo.bootstrap.TableBody = function(config){
7023     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7024 };
7025
7026 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7027     
7028     cls: false,
7029     tag: false,
7030     align: false,
7031     charoff: false,
7032     valign: false,
7033     
7034     getAutoCreate : function(){
7035         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7036         
7037         cfg = {
7038             tag: 'tbody'
7039         };
7040             
7041         if (this.cls) {
7042             cfg.cls=this.cls
7043         }
7044         if(this.tag){
7045             cfg.tag = this.tag;
7046         }
7047         
7048         if(this.align){
7049             cfg.align = this.align;
7050         }
7051         if(this.charoff){
7052             cfg.charoff = this.charoff;
7053         }
7054         if(this.valign){
7055             cfg.valign = this.valign;
7056         }
7057         
7058         return cfg;
7059     }
7060     
7061     
7062 //    initEvents : function()
7063 //    {
7064 //        
7065 //        if(!this.store){
7066 //            return;
7067 //        }
7068 //        
7069 //        this.store = Roo.factory(this.store, Roo.data);
7070 //        this.store.on('load', this.onLoad, this);
7071 //        
7072 //        this.store.load();
7073 //        
7074 //    },
7075 //    
7076 //    onLoad: function () 
7077 //    {   
7078 //        this.fireEvent('load', this);
7079 //    }
7080 //    
7081 //   
7082 });
7083
7084  
7085
7086  /*
7087  * Based on:
7088  * Ext JS Library 1.1.1
7089  * Copyright(c) 2006-2007, Ext JS, LLC.
7090  *
7091  * Originally Released Under LGPL - original licence link has changed is not relivant.
7092  *
7093  * Fork - LGPL
7094  * <script type="text/javascript">
7095  */
7096
7097 // as we use this in bootstrap.
7098 Roo.namespace('Roo.form');
7099  /**
7100  * @class Roo.form.Action
7101  * Internal Class used to handle form actions
7102  * @constructor
7103  * @param {Roo.form.BasicForm} el The form element or its id
7104  * @param {Object} config Configuration options
7105  */
7106
7107  
7108  
7109 // define the action interface
7110 Roo.form.Action = function(form, options){
7111     this.form = form;
7112     this.options = options || {};
7113 };
7114 /**
7115  * Client Validation Failed
7116  * @const 
7117  */
7118 Roo.form.Action.CLIENT_INVALID = 'client';
7119 /**
7120  * Server Validation Failed
7121  * @const 
7122  */
7123 Roo.form.Action.SERVER_INVALID = 'server';
7124  /**
7125  * Connect to Server Failed
7126  * @const 
7127  */
7128 Roo.form.Action.CONNECT_FAILURE = 'connect';
7129 /**
7130  * Reading Data from Server Failed
7131  * @const 
7132  */
7133 Roo.form.Action.LOAD_FAILURE = 'load';
7134
7135 Roo.form.Action.prototype = {
7136     type : 'default',
7137     failureType : undefined,
7138     response : undefined,
7139     result : undefined,
7140
7141     // interface method
7142     run : function(options){
7143
7144     },
7145
7146     // interface method
7147     success : function(response){
7148
7149     },
7150
7151     // interface method
7152     handleResponse : function(response){
7153
7154     },
7155
7156     // default connection failure
7157     failure : function(response){
7158         
7159         this.response = response;
7160         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7161         this.form.afterAction(this, false);
7162     },
7163
7164     processResponse : function(response){
7165         this.response = response;
7166         if(!response.responseText){
7167             return true;
7168         }
7169         this.result = this.handleResponse(response);
7170         return this.result;
7171     },
7172
7173     // utility functions used internally
7174     getUrl : function(appendParams){
7175         var url = this.options.url || this.form.url || this.form.el.dom.action;
7176         if(appendParams){
7177             var p = this.getParams();
7178             if(p){
7179                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7180             }
7181         }
7182         return url;
7183     },
7184
7185     getMethod : function(){
7186         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7187     },
7188
7189     getParams : function(){
7190         var bp = this.form.baseParams;
7191         var p = this.options.params;
7192         if(p){
7193             if(typeof p == "object"){
7194                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7195             }else if(typeof p == 'string' && bp){
7196                 p += '&' + Roo.urlEncode(bp);
7197             }
7198         }else if(bp){
7199             p = Roo.urlEncode(bp);
7200         }
7201         return p;
7202     },
7203
7204     createCallback : function(){
7205         return {
7206             success: this.success,
7207             failure: this.failure,
7208             scope: this,
7209             timeout: (this.form.timeout*1000),
7210             upload: this.form.fileUpload ? this.success : undefined
7211         };
7212     }
7213 };
7214
7215 Roo.form.Action.Submit = function(form, options){
7216     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7217 };
7218
7219 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7220     type : 'submit',
7221
7222     haveProgress : false,
7223     uploadComplete : false,
7224     
7225     // uploadProgress indicator.
7226     uploadProgress : function()
7227     {
7228         if (!this.form.progressUrl) {
7229             return;
7230         }
7231         
7232         if (!this.haveProgress) {
7233             Roo.MessageBox.progress("Uploading", "Uploading");
7234         }
7235         if (this.uploadComplete) {
7236            Roo.MessageBox.hide();
7237            return;
7238         }
7239         
7240         this.haveProgress = true;
7241    
7242         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7243         
7244         var c = new Roo.data.Connection();
7245         c.request({
7246             url : this.form.progressUrl,
7247             params: {
7248                 id : uid
7249             },
7250             method: 'GET',
7251             success : function(req){
7252                //console.log(data);
7253                 var rdata = false;
7254                 var edata;
7255                 try  {
7256                    rdata = Roo.decode(req.responseText)
7257                 } catch (e) {
7258                     Roo.log("Invalid data from server..");
7259                     Roo.log(edata);
7260                     return;
7261                 }
7262                 if (!rdata || !rdata.success) {
7263                     Roo.log(rdata);
7264                     Roo.MessageBox.alert(Roo.encode(rdata));
7265                     return;
7266                 }
7267                 var data = rdata.data;
7268                 
7269                 if (this.uploadComplete) {
7270                    Roo.MessageBox.hide();
7271                    return;
7272                 }
7273                    
7274                 if (data){
7275                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7276                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7277                     );
7278                 }
7279                 this.uploadProgress.defer(2000,this);
7280             },
7281        
7282             failure: function(data) {
7283                 Roo.log('progress url failed ');
7284                 Roo.log(data);
7285             },
7286             scope : this
7287         });
7288            
7289     },
7290     
7291     
7292     run : function()
7293     {
7294         // run get Values on the form, so it syncs any secondary forms.
7295         this.form.getValues();
7296         
7297         var o = this.options;
7298         var method = this.getMethod();
7299         var isPost = method == 'POST';
7300         if(o.clientValidation === false || this.form.isValid()){
7301             
7302             if (this.form.progressUrl) {
7303                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7304                     (new Date() * 1) + '' + Math.random());
7305                     
7306             } 
7307             
7308             
7309             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7310                 form:this.form.el.dom,
7311                 url:this.getUrl(!isPost),
7312                 method: method,
7313                 params:isPost ? this.getParams() : null,
7314                 isUpload: this.form.fileUpload
7315             }));
7316             
7317             this.uploadProgress();
7318
7319         }else if (o.clientValidation !== false){ // client validation failed
7320             this.failureType = Roo.form.Action.CLIENT_INVALID;
7321             this.form.afterAction(this, false);
7322         }
7323     },
7324
7325     success : function(response)
7326     {
7327         this.uploadComplete= true;
7328         if (this.haveProgress) {
7329             Roo.MessageBox.hide();
7330         }
7331         
7332         
7333         var result = this.processResponse(response);
7334         if(result === true || result.success){
7335             this.form.afterAction(this, true);
7336             return;
7337         }
7338         if(result.errors){
7339             this.form.markInvalid(result.errors);
7340             this.failureType = Roo.form.Action.SERVER_INVALID;
7341         }
7342         this.form.afterAction(this, false);
7343     },
7344     failure : function(response)
7345     {
7346         this.uploadComplete= true;
7347         if (this.haveProgress) {
7348             Roo.MessageBox.hide();
7349         }
7350         
7351         this.response = response;
7352         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7353         this.form.afterAction(this, false);
7354     },
7355     
7356     handleResponse : function(response){
7357         if(this.form.errorReader){
7358             var rs = this.form.errorReader.read(response);
7359             var errors = [];
7360             if(rs.records){
7361                 for(var i = 0, len = rs.records.length; i < len; i++) {
7362                     var r = rs.records[i];
7363                     errors[i] = r.data;
7364                 }
7365             }
7366             if(errors.length < 1){
7367                 errors = null;
7368             }
7369             return {
7370                 success : rs.success,
7371                 errors : errors
7372             };
7373         }
7374         var ret = false;
7375         try {
7376             ret = Roo.decode(response.responseText);
7377         } catch (e) {
7378             ret = {
7379                 success: false,
7380                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7381                 errors : []
7382             };
7383         }
7384         return ret;
7385         
7386     }
7387 });
7388
7389
7390 Roo.form.Action.Load = function(form, options){
7391     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7392     this.reader = this.form.reader;
7393 };
7394
7395 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7396     type : 'load',
7397
7398     run : function(){
7399         
7400         Roo.Ajax.request(Roo.apply(
7401                 this.createCallback(), {
7402                     method:this.getMethod(),
7403                     url:this.getUrl(false),
7404                     params:this.getParams()
7405         }));
7406     },
7407
7408     success : function(response){
7409         
7410         var result = this.processResponse(response);
7411         if(result === true || !result.success || !result.data){
7412             this.failureType = Roo.form.Action.LOAD_FAILURE;
7413             this.form.afterAction(this, false);
7414             return;
7415         }
7416         this.form.clearInvalid();
7417         this.form.setValues(result.data);
7418         this.form.afterAction(this, true);
7419     },
7420
7421     handleResponse : function(response){
7422         if(this.form.reader){
7423             var rs = this.form.reader.read(response);
7424             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7425             return {
7426                 success : rs.success,
7427                 data : data
7428             };
7429         }
7430         return Roo.decode(response.responseText);
7431     }
7432 });
7433
7434 Roo.form.Action.ACTION_TYPES = {
7435     'load' : Roo.form.Action.Load,
7436     'submit' : Roo.form.Action.Submit
7437 };/*
7438  * - LGPL
7439  *
7440  * form
7441  *
7442  */
7443
7444 /**
7445  * @class Roo.bootstrap.Form
7446  * @extends Roo.bootstrap.Component
7447  * Bootstrap Form class
7448  * @cfg {String} method  GET | POST (default POST)
7449  * @cfg {String} labelAlign top | left (default top)
7450  * @cfg {String} align left  | right - for navbars
7451  * @cfg {Boolean} loadMask load mask when submit (default true)
7452
7453  *
7454  * @constructor
7455  * Create a new Form
7456  * @param {Object} config The config object
7457  */
7458
7459
7460 Roo.bootstrap.Form = function(config){
7461     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7462     
7463     Roo.bootstrap.Form.popover.apply();
7464     
7465     this.addEvents({
7466         /**
7467          * @event clientvalidation
7468          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7469          * @param {Form} this
7470          * @param {Boolean} valid true if the form has passed client-side validation
7471          */
7472         clientvalidation: true,
7473         /**
7474          * @event beforeaction
7475          * Fires before any action is performed. Return false to cancel the action.
7476          * @param {Form} this
7477          * @param {Action} action The action to be performed
7478          */
7479         beforeaction: true,
7480         /**
7481          * @event actionfailed
7482          * Fires when an action fails.
7483          * @param {Form} this
7484          * @param {Action} action The action that failed
7485          */
7486         actionfailed : true,
7487         /**
7488          * @event actioncomplete
7489          * Fires when an action is completed.
7490          * @param {Form} this
7491          * @param {Action} action The action that completed
7492          */
7493         actioncomplete : true
7494     });
7495
7496 };
7497
7498 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7499
7500      /**
7501      * @cfg {String} method
7502      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7503      */
7504     method : 'POST',
7505     /**
7506      * @cfg {String} url
7507      * The URL to use for form actions if one isn't supplied in the action options.
7508      */
7509     /**
7510      * @cfg {Boolean} fileUpload
7511      * Set to true if this form is a file upload.
7512      */
7513
7514     /**
7515      * @cfg {Object} baseParams
7516      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7517      */
7518
7519     /**
7520      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7521      */
7522     timeout: 30,
7523     /**
7524      * @cfg {Sting} align (left|right) for navbar forms
7525      */
7526     align : 'left',
7527
7528     // private
7529     activeAction : null,
7530
7531     /**
7532      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7533      * element by passing it or its id or mask the form itself by passing in true.
7534      * @type Mixed
7535      */
7536     waitMsgTarget : false,
7537
7538     loadMask : true,
7539     
7540     /**
7541      * @cfg {Boolean} errorMask (true|false) default false
7542      */
7543     errorMask : false,
7544
7545     getAutoCreate : function(){
7546
7547         var cfg = {
7548             tag: 'form',
7549             method : this.method || 'POST',
7550             id : this.id || Roo.id(),
7551             cls : ''
7552         };
7553         if (this.parent().xtype.match(/^Nav/)) {
7554             cfg.cls = 'navbar-form navbar-' + this.align;
7555
7556         }
7557
7558         if (this.labelAlign == 'left' ) {
7559             cfg.cls += ' form-horizontal';
7560         }
7561
7562
7563         return cfg;
7564     },
7565     initEvents : function()
7566     {
7567         this.el.on('submit', this.onSubmit, this);
7568         // this was added as random key presses on the form where triggering form submit.
7569         this.el.on('keypress', function(e) {
7570             if (e.getCharCode() != 13) {
7571                 return true;
7572             }
7573             // we might need to allow it for textareas.. and some other items.
7574             // check e.getTarget().
7575
7576             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7577                 return true;
7578             }
7579
7580             Roo.log("keypress blocked");
7581
7582             e.preventDefault();
7583             return false;
7584         });
7585         
7586     },
7587     // private
7588     onSubmit : function(e){
7589         e.stopEvent();
7590     },
7591
7592      /**
7593      * Returns true if client-side validation on the form is successful.
7594      * @return Boolean
7595      */
7596     isValid : function(){
7597         var items = this.getItems();
7598         var valid = true;
7599         var target = false;
7600         
7601         items.each(function(f){
7602             
7603             if(f.validate()){
7604                 return;
7605             }
7606             valid = false;
7607
7608             if(!target && f.el.isVisible(true)){
7609                 target = f;
7610             }
7611            
7612         });
7613         
7614         if(this.errorMask && !valid){
7615             Roo.bootstrap.Form.popover.mask(this, target);
7616         }
7617         
7618         return valid;
7619     },
7620     
7621     /**
7622      * Returns true if any fields in this form have changed since their original load.
7623      * @return Boolean
7624      */
7625     isDirty : function(){
7626         var dirty = false;
7627         var items = this.getItems();
7628         items.each(function(f){
7629            if(f.isDirty()){
7630                dirty = true;
7631                return false;
7632            }
7633            return true;
7634         });
7635         return dirty;
7636     },
7637      /**
7638      * Performs a predefined action (submit or load) or custom actions you define on this form.
7639      * @param {String} actionName The name of the action type
7640      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7641      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7642      * accept other config options):
7643      * <pre>
7644 Property          Type             Description
7645 ----------------  ---------------  ----------------------------------------------------------------------------------
7646 url               String           The url for the action (defaults to the form's url)
7647 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7648 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7649 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7650                                    validate the form on the client (defaults to false)
7651      * </pre>
7652      * @return {BasicForm} this
7653      */
7654     doAction : function(action, options){
7655         if(typeof action == 'string'){
7656             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7657         }
7658         if(this.fireEvent('beforeaction', this, action) !== false){
7659             this.beforeAction(action);
7660             action.run.defer(100, action);
7661         }
7662         return this;
7663     },
7664
7665     // private
7666     beforeAction : function(action){
7667         var o = action.options;
7668
7669         if(this.loadMask){
7670             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7671         }
7672         // not really supported yet.. ??
7673
7674         //if(this.waitMsgTarget === true){
7675         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7676         //}else if(this.waitMsgTarget){
7677         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7678         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7679         //}else {
7680         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7681        // }
7682
7683     },
7684
7685     // private
7686     afterAction : function(action, success){
7687         this.activeAction = null;
7688         var o = action.options;
7689
7690         //if(this.waitMsgTarget === true){
7691             this.el.unmask();
7692         //}else if(this.waitMsgTarget){
7693         //    this.waitMsgTarget.unmask();
7694         //}else{
7695         //    Roo.MessageBox.updateProgress(1);
7696         //    Roo.MessageBox.hide();
7697        // }
7698         //
7699         if(success){
7700             if(o.reset){
7701                 this.reset();
7702             }
7703             Roo.callback(o.success, o.scope, [this, action]);
7704             this.fireEvent('actioncomplete', this, action);
7705
7706         }else{
7707
7708             // failure condition..
7709             // we have a scenario where updates need confirming.
7710             // eg. if a locking scenario exists..
7711             // we look for { errors : { needs_confirm : true }} in the response.
7712             if (
7713                 (typeof(action.result) != 'undefined')  &&
7714                 (typeof(action.result.errors) != 'undefined')  &&
7715                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7716            ){
7717                 var _t = this;
7718                 Roo.log("not supported yet");
7719                  /*
7720
7721                 Roo.MessageBox.confirm(
7722                     "Change requires confirmation",
7723                     action.result.errorMsg,
7724                     function(r) {
7725                         if (r != 'yes') {
7726                             return;
7727                         }
7728                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7729                     }
7730
7731                 );
7732                 */
7733
7734
7735                 return;
7736             }
7737
7738             Roo.callback(o.failure, o.scope, [this, action]);
7739             // show an error message if no failed handler is set..
7740             if (!this.hasListener('actionfailed')) {
7741                 Roo.log("need to add dialog support");
7742                 /*
7743                 Roo.MessageBox.alert("Error",
7744                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7745                         action.result.errorMsg :
7746                         "Saving Failed, please check your entries or try again"
7747                 );
7748                 */
7749             }
7750
7751             this.fireEvent('actionfailed', this, action);
7752         }
7753
7754     },
7755     /**
7756      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7757      * @param {String} id The value to search for
7758      * @return Field
7759      */
7760     findField : function(id){
7761         var items = this.getItems();
7762         var field = items.get(id);
7763         if(!field){
7764              items.each(function(f){
7765                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7766                     field = f;
7767                     return false;
7768                 }
7769                 return true;
7770             });
7771         }
7772         return field || null;
7773     },
7774      /**
7775      * Mark fields in this form invalid in bulk.
7776      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7777      * @return {BasicForm} this
7778      */
7779     markInvalid : function(errors){
7780         if(errors instanceof Array){
7781             for(var i = 0, len = errors.length; i < len; i++){
7782                 var fieldError = errors[i];
7783                 var f = this.findField(fieldError.id);
7784                 if(f){
7785                     f.markInvalid(fieldError.msg);
7786                 }
7787             }
7788         }else{
7789             var field, id;
7790             for(id in errors){
7791                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7792                     field.markInvalid(errors[id]);
7793                 }
7794             }
7795         }
7796         //Roo.each(this.childForms || [], function (f) {
7797         //    f.markInvalid(errors);
7798         //});
7799
7800         return this;
7801     },
7802
7803     /**
7804      * Set values for fields in this form in bulk.
7805      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7806      * @return {BasicForm} this
7807      */
7808     setValues : function(values){
7809         if(values instanceof Array){ // array of objects
7810             for(var i = 0, len = values.length; i < len; i++){
7811                 var v = values[i];
7812                 var f = this.findField(v.id);
7813                 if(f){
7814                     f.setValue(v.value);
7815                     if(this.trackResetOnLoad){
7816                         f.originalValue = f.getValue();
7817                     }
7818                 }
7819             }
7820         }else{ // object hash
7821             var field, id;
7822             for(id in values){
7823                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7824
7825                     if (field.setFromData &&
7826                         field.valueField &&
7827                         field.displayField &&
7828                         // combos' with local stores can
7829                         // be queried via setValue()
7830                         // to set their value..
7831                         (field.store && !field.store.isLocal)
7832                         ) {
7833                         // it's a combo
7834                         var sd = { };
7835                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7836                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7837                         field.setFromData(sd);
7838
7839                     } else {
7840                         field.setValue(values[id]);
7841                     }
7842
7843
7844                     if(this.trackResetOnLoad){
7845                         field.originalValue = field.getValue();
7846                     }
7847                 }
7848             }
7849         }
7850
7851         //Roo.each(this.childForms || [], function (f) {
7852         //    f.setValues(values);
7853         //});
7854
7855         return this;
7856     },
7857
7858     /**
7859      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7860      * they are returned as an array.
7861      * @param {Boolean} asString
7862      * @return {Object}
7863      */
7864     getValues : function(asString){
7865         //if (this.childForms) {
7866             // copy values from the child forms
7867         //    Roo.each(this.childForms, function (f) {
7868         //        this.setValues(f.getValues());
7869         //    }, this);
7870         //}
7871
7872
7873
7874         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7875         if(asString === true){
7876             return fs;
7877         }
7878         return Roo.urlDecode(fs);
7879     },
7880
7881     /**
7882      * Returns the fields in this form as an object with key/value pairs.
7883      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7884      * @return {Object}
7885      */
7886     getFieldValues : function(with_hidden)
7887     {
7888         var items = this.getItems();
7889         var ret = {};
7890         items.each(function(f){
7891             if (!f.getName()) {
7892                 return;
7893             }
7894             var v = f.getValue();
7895             if (f.inputType =='radio') {
7896                 if (typeof(ret[f.getName()]) == 'undefined') {
7897                     ret[f.getName()] = ''; // empty..
7898                 }
7899
7900                 if (!f.el.dom.checked) {
7901                     return;
7902
7903                 }
7904                 v = f.el.dom.value;
7905
7906             }
7907
7908             // not sure if this supported any more..
7909             if ((typeof(v) == 'object') && f.getRawValue) {
7910                 v = f.getRawValue() ; // dates..
7911             }
7912             // combo boxes where name != hiddenName...
7913             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7914                 ret[f.name] = f.getRawValue();
7915             }
7916             ret[f.getName()] = v;
7917         });
7918
7919         return ret;
7920     },
7921
7922     /**
7923      * Clears all invalid messages in this form.
7924      * @return {BasicForm} this
7925      */
7926     clearInvalid : function(){
7927         var items = this.getItems();
7928
7929         items.each(function(f){
7930            f.clearInvalid();
7931         });
7932
7933
7934
7935         return this;
7936     },
7937
7938     /**
7939      * Resets this form.
7940      * @return {BasicForm} this
7941      */
7942     reset : function(){
7943         var items = this.getItems();
7944         items.each(function(f){
7945             f.reset();
7946         });
7947
7948         Roo.each(this.childForms || [], function (f) {
7949             f.reset();
7950         });
7951
7952
7953         return this;
7954     },
7955     getItems : function()
7956     {
7957         var r=new Roo.util.MixedCollection(false, function(o){
7958             return o.id || (o.id = Roo.id());
7959         });
7960         var iter = function(el) {
7961             if (el.inputEl) {
7962                 r.add(el);
7963             }
7964             if (!el.items) {
7965                 return;
7966             }
7967             Roo.each(el.items,function(e) {
7968                 iter(e);
7969             });
7970
7971
7972         };
7973
7974         iter(this);
7975         return r;
7976
7977
7978
7979
7980     }
7981
7982 });
7983
7984 Roo.apply(Roo.bootstrap.Form, {
7985     
7986     popover : {
7987         
7988         padding : 5,
7989         
7990         isApplied : false,
7991         
7992         isMasked : false,
7993         
7994         form : false,
7995         
7996         target : false,
7997         
7998         toolTip : false,
7999         
8000         intervalID : false,
8001         
8002         maskEl : false,
8003         
8004         apply : function()
8005         {
8006             if(this.isApplied){
8007                 return;
8008             }
8009             
8010             this.maskEl = {
8011                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8012                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8013                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8014                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8015             };
8016             
8017             this.maskEl.top.enableDisplayMode("block");
8018             this.maskEl.left.enableDisplayMode("block");
8019             this.maskEl.bottom.enableDisplayMode("block");
8020             this.maskEl.right.enableDisplayMode("block");
8021             
8022             this.toolTip = new Roo.bootstrap.Tooltip({
8023                 cls : 'roo-form-error-popover',
8024                 alignment : {
8025                     'left' : ['r-l', [-2,0], 'right'],
8026                     'right' : ['l-r', [2,0], 'left'],
8027                     'bottom' : ['tl-bl', [0,2], 'top'],
8028                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8029                 }
8030             });
8031             
8032             this.toolTip.render(Roo.get(document.body));
8033
8034             this.toolTip.el.enableDisplayMode("block");
8035             
8036             Roo.get(document.body).on('click', function(){
8037                 this.unmask();
8038             }, this);
8039             
8040             this.isApplied = true
8041         },
8042         
8043         mask : function(form, target)
8044         {
8045             this.form = form;
8046             
8047             this.target = target;
8048             
8049             if(!this.form.errorMask || !target.el){
8050                 return;
8051             }
8052             
8053             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8054             
8055             var scrolled = scrollable.getScroll();
8056             
8057             var ot = this.target.el.calcOffsetsTo(scrollable);
8058             
8059             scrollTo = ot[1] - 100;
8060             
8061             scrollable.scrollTo('top', scrollTo);
8062             
8063             var box = this.target.el.getBox();
8064
8065             var zIndex = Roo.bootstrap.Modal.zIndex++;
8066
8067             this.maskEl.top.setStyle('position', 'fixed');
8068             this.maskEl.top.setStyle('z-index', zIndex);
8069             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8070             this.maskEl.top.setXY([0, 0]);
8071             this.maskEl.top.show();
8072
8073             this.maskEl.left.setStyle('position', 'fixed');
8074             this.maskEl.left.setStyle('z-index', zIndex);
8075             this.maskEl.left.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8076             this.maskEl.left.setXY([box.right + this.padding, box.y - this.padding]);
8077             this.maskEl.left.show();
8078
8079             this.maskEl.bottom.setStyle('position', 'fixed');
8080             this.maskEl.bottom.setStyle('z-index', zIndex);
8081             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8082             this.maskEl.bottom.setXY([0, box.bottom + this.padding]);
8083             this.maskEl.bottom.show();
8084
8085             this.maskEl.right.setStyle('position', 'fixed');
8086             this.maskEl.right.setStyle('z-index', zIndex);
8087             this.maskEl.right.setSize(box.x - this.padding, box.height + this.padding * 2);
8088             this.maskEl.right.setXY([0, box.y - this.padding]);
8089             this.maskEl.right.show();
8090
8091
8092             this.toolTip.bindEl = this.target.el;
8093
8094             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8095
8096             var tip = this.target.blankText;
8097
8098             if(this.target.getValue() !== '' && this.target.regexText.length){
8099                 tip = this.target.regexText;
8100             }
8101
8102             this.toolTip.show(tip);
8103
8104             this.intervalID = window.setInterval(function() {
8105                 Roo.bootstrap.Form.popover.unmask();
8106             }, 10000);
8107
8108             window.onwheel = function(){ return false;};
8109             
8110             (function(){ this.isMasked = true; }).defer(500, this);
8111                 
8112             
8113             
8114         },
8115         
8116         unmask : function()
8117         {
8118             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8119                 return;
8120             }
8121             
8122             this.maskEl.top.setStyle('position', 'absolute');
8123             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8124             this.maskEl.top.hide();
8125
8126             this.maskEl.left.setStyle('position', 'absolute');
8127             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8128             this.maskEl.left.hide();
8129
8130             this.maskEl.bottom.setStyle('position', 'absolute');
8131             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8132             this.maskEl.bottom.hide();
8133
8134             this.maskEl.right.setStyle('position', 'absolute');
8135             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8136             this.maskEl.right.hide();
8137             
8138             this.toolTip.hide();
8139             
8140             this.toolTip.el.hide();
8141             
8142             window.onwheel = function(){ return true;};
8143             
8144             if(this.intervalID){
8145                 window.clearInterval(this.intervalID);
8146                 this.intervalID = false;
8147             }
8148             
8149             this.isMasked = false;
8150             
8151         }
8152         
8153     }
8154     
8155 });
8156
8157 /*
8158  * Based on:
8159  * Ext JS Library 1.1.1
8160  * Copyright(c) 2006-2007, Ext JS, LLC.
8161  *
8162  * Originally Released Under LGPL - original licence link has changed is not relivant.
8163  *
8164  * Fork - LGPL
8165  * <script type="text/javascript">
8166  */
8167 /**
8168  * @class Roo.form.VTypes
8169  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8170  * @singleton
8171  */
8172 Roo.form.VTypes = function(){
8173     // closure these in so they are only created once.
8174     var alpha = /^[a-zA-Z_]+$/;
8175     var alphanum = /^[a-zA-Z0-9_]+$/;
8176     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8177     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8178
8179     // All these messages and functions are configurable
8180     return {
8181         /**
8182          * The function used to validate email addresses
8183          * @param {String} value The email address
8184          */
8185         'email' : function(v){
8186             return email.test(v);
8187         },
8188         /**
8189          * The error text to display when the email validation function returns false
8190          * @type String
8191          */
8192         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8193         /**
8194          * The keystroke filter mask to be applied on email input
8195          * @type RegExp
8196          */
8197         'emailMask' : /[a-z0-9_\.\-@]/i,
8198
8199         /**
8200          * The function used to validate URLs
8201          * @param {String} value The URL
8202          */
8203         'url' : function(v){
8204             return url.test(v);
8205         },
8206         /**
8207          * The error text to display when the url validation function returns false
8208          * @type String
8209          */
8210         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8211         
8212         /**
8213          * The function used to validate alpha values
8214          * @param {String} value The value
8215          */
8216         'alpha' : function(v){
8217             return alpha.test(v);
8218         },
8219         /**
8220          * The error text to display when the alpha validation function returns false
8221          * @type String
8222          */
8223         'alphaText' : 'This field should only contain letters and _',
8224         /**
8225          * The keystroke filter mask to be applied on alpha input
8226          * @type RegExp
8227          */
8228         'alphaMask' : /[a-z_]/i,
8229
8230         /**
8231          * The function used to validate alphanumeric values
8232          * @param {String} value The value
8233          */
8234         'alphanum' : function(v){
8235             return alphanum.test(v);
8236         },
8237         /**
8238          * The error text to display when the alphanumeric validation function returns false
8239          * @type String
8240          */
8241         'alphanumText' : 'This field should only contain letters, numbers and _',
8242         /**
8243          * The keystroke filter mask to be applied on alphanumeric input
8244          * @type RegExp
8245          */
8246         'alphanumMask' : /[a-z0-9_]/i
8247     };
8248 }();/*
8249  * - LGPL
8250  *
8251  * Input
8252  * 
8253  */
8254
8255 /**
8256  * @class Roo.bootstrap.Input
8257  * @extends Roo.bootstrap.Component
8258  * Bootstrap Input class
8259  * @cfg {Boolean} disabled is it disabled
8260  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8261  * @cfg {String} name name of the input
8262  * @cfg {string} fieldLabel - the label associated
8263  * @cfg {string} placeholder - placeholder to put in text.
8264  * @cfg {string}  before - input group add on before
8265  * @cfg {string} after - input group add on after
8266  * @cfg {string} size - (lg|sm) or leave empty..
8267  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8268  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8269  * @cfg {Number} md colspan out of 12 for computer-sized screens
8270  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8271  * @cfg {string} value default value of the input
8272  * @cfg {Number} labelWidth set the width of label 
8273  * @cfg {Number} labellg set the width of label (1-12)
8274  * @cfg {Number} labelmd set the width of label (1-12)
8275  * @cfg {Number} labelsm set the width of label (1-12)
8276  * @cfg {Number} labelxs set the width of label (1-12)
8277  * @cfg {String} labelAlign (top|left)
8278  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8279  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8280  * @cfg {String} indicatorpos (left|right) default left
8281
8282  * @cfg {String} align (left|center|right) Default left
8283  * @cfg {Boolean} forceFeedback (true|false) Default false
8284  * 
8285  * 
8286  * 
8287  * 
8288  * @constructor
8289  * Create a new Input
8290  * @param {Object} config The config object
8291  */
8292
8293 Roo.bootstrap.Input = function(config){
8294     
8295     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8296     
8297     this.addEvents({
8298         /**
8299          * @event focus
8300          * Fires when this field receives input focus.
8301          * @param {Roo.form.Field} this
8302          */
8303         focus : true,
8304         /**
8305          * @event blur
8306          * Fires when this field loses input focus.
8307          * @param {Roo.form.Field} this
8308          */
8309         blur : true,
8310         /**
8311          * @event specialkey
8312          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8313          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8314          * @param {Roo.form.Field} this
8315          * @param {Roo.EventObject} e The event object
8316          */
8317         specialkey : true,
8318         /**
8319          * @event change
8320          * Fires just before the field blurs if the field value has changed.
8321          * @param {Roo.form.Field} this
8322          * @param {Mixed} newValue The new value
8323          * @param {Mixed} oldValue The original value
8324          */
8325         change : true,
8326         /**
8327          * @event invalid
8328          * Fires after the field has been marked as invalid.
8329          * @param {Roo.form.Field} this
8330          * @param {String} msg The validation message
8331          */
8332         invalid : true,
8333         /**
8334          * @event valid
8335          * Fires after the field has been validated with no errors.
8336          * @param {Roo.form.Field} this
8337          */
8338         valid : true,
8339          /**
8340          * @event keyup
8341          * Fires after the key up
8342          * @param {Roo.form.Field} this
8343          * @param {Roo.EventObject}  e The event Object
8344          */
8345         keyup : true
8346     });
8347 };
8348
8349 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8350      /**
8351      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8352       automatic validation (defaults to "keyup").
8353      */
8354     validationEvent : "keyup",
8355      /**
8356      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8357      */
8358     validateOnBlur : true,
8359     /**
8360      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8361      */
8362     validationDelay : 250,
8363      /**
8364      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8365      */
8366     focusClass : "x-form-focus",  // not needed???
8367     
8368        
8369     /**
8370      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8371      */
8372     invalidClass : "has-warning",
8373     
8374     /**
8375      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8376      */
8377     validClass : "has-success",
8378     
8379     /**
8380      * @cfg {Boolean} hasFeedback (true|false) default true
8381      */
8382     hasFeedback : true,
8383     
8384     /**
8385      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8386      */
8387     invalidFeedbackClass : "glyphicon-warning-sign",
8388     
8389     /**
8390      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8391      */
8392     validFeedbackClass : "glyphicon-ok",
8393     
8394     /**
8395      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8396      */
8397     selectOnFocus : false,
8398     
8399      /**
8400      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8401      */
8402     maskRe : null,
8403        /**
8404      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8405      */
8406     vtype : null,
8407     
8408       /**
8409      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8410      */
8411     disableKeyFilter : false,
8412     
8413        /**
8414      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8415      */
8416     disabled : false,
8417      /**
8418      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8419      */
8420     allowBlank : true,
8421     /**
8422      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8423      */
8424     blankText : "Please complete this mandatory field",
8425     
8426      /**
8427      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8428      */
8429     minLength : 0,
8430     /**
8431      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8432      */
8433     maxLength : Number.MAX_VALUE,
8434     /**
8435      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8436      */
8437     minLengthText : "The minimum length for this field is {0}",
8438     /**
8439      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8440      */
8441     maxLengthText : "The maximum length for this field is {0}",
8442   
8443     
8444     /**
8445      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8446      * If available, this function will be called only after the basic validators all return true, and will be passed the
8447      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8448      */
8449     validator : null,
8450     /**
8451      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8452      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8453      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8454      */
8455     regex : null,
8456     /**
8457      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8458      */
8459     regexText : "",
8460     
8461     autocomplete: false,
8462     
8463     
8464     fieldLabel : '',
8465     inputType : 'text',
8466     
8467     name : false,
8468     placeholder: false,
8469     before : false,
8470     after : false,
8471     size : false,
8472     hasFocus : false,
8473     preventMark: false,
8474     isFormField : true,
8475     value : '',
8476     labelWidth : 2,
8477     labelAlign : false,
8478     readOnly : false,
8479     align : false,
8480     formatedValue : false,
8481     forceFeedback : false,
8482     
8483     indicatorpos : 'left',
8484     
8485     labellg : 0,
8486     labelmd : 0,
8487     labelsm : 0,
8488     labelxs : 0,
8489     
8490     parentLabelAlign : function()
8491     {
8492         var parent = this;
8493         while (parent.parent()) {
8494             parent = parent.parent();
8495             if (typeof(parent.labelAlign) !='undefined') {
8496                 return parent.labelAlign;
8497             }
8498         }
8499         return 'left';
8500         
8501     },
8502     
8503     getAutoCreate : function()
8504     {
8505         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8506         
8507         var id = Roo.id();
8508         
8509         var cfg = {};
8510         
8511         if(this.inputType != 'hidden'){
8512             cfg.cls = 'form-group' //input-group
8513         }
8514         
8515         var input =  {
8516             tag: 'input',
8517             id : id,
8518             type : this.inputType,
8519             value : this.value,
8520             cls : 'form-control',
8521             placeholder : this.placeholder || '',
8522             autocomplete : this.autocomplete || 'new-password'
8523         };
8524         
8525         if(this.align){
8526             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8527         }
8528         
8529         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8530             input.maxLength = this.maxLength;
8531         }
8532         
8533         if (this.disabled) {
8534             input.disabled=true;
8535         }
8536         
8537         if (this.readOnly) {
8538             input.readonly=true;
8539         }
8540         
8541         if (this.name) {
8542             input.name = this.name;
8543         }
8544         
8545         if (this.size) {
8546             input.cls += ' input-' + this.size;
8547         }
8548         
8549         var settings=this;
8550         ['xs','sm','md','lg'].map(function(size){
8551             if (settings[size]) {
8552                 cfg.cls += ' col-' + size + '-' + settings[size];
8553             }
8554         });
8555         
8556         var inputblock = input;
8557         
8558         var feedback = {
8559             tag: 'span',
8560             cls: 'glyphicon form-control-feedback'
8561         };
8562             
8563         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8564             
8565             inputblock = {
8566                 cls : 'has-feedback',
8567                 cn :  [
8568                     input,
8569                     feedback
8570                 ] 
8571             };  
8572         }
8573         
8574         if (this.before || this.after) {
8575             
8576             inputblock = {
8577                 cls : 'input-group',
8578                 cn :  [] 
8579             };
8580             
8581             if (this.before && typeof(this.before) == 'string') {
8582                 
8583                 inputblock.cn.push({
8584                     tag :'span',
8585                     cls : 'roo-input-before input-group-addon',
8586                     html : this.before
8587                 });
8588             }
8589             if (this.before && typeof(this.before) == 'object') {
8590                 this.before = Roo.factory(this.before);
8591                 
8592                 inputblock.cn.push({
8593                     tag :'span',
8594                     cls : 'roo-input-before input-group-' +
8595                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8596                 });
8597             }
8598             
8599             inputblock.cn.push(input);
8600             
8601             if (this.after && typeof(this.after) == 'string') {
8602                 inputblock.cn.push({
8603                     tag :'span',
8604                     cls : 'roo-input-after input-group-addon',
8605                     html : this.after
8606                 });
8607             }
8608             if (this.after && typeof(this.after) == 'object') {
8609                 this.after = Roo.factory(this.after);
8610                 
8611                 inputblock.cn.push({
8612                     tag :'span',
8613                     cls : 'roo-input-after input-group-' +
8614                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8615                 });
8616             }
8617             
8618             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8619                 inputblock.cls += ' has-feedback';
8620                 inputblock.cn.push(feedback);
8621             }
8622         };
8623         
8624         if (align ==='left' && this.fieldLabel.length) {
8625             
8626             cfg.cls += ' roo-form-group-label-left';
8627             
8628             cfg.cn = [
8629                 {
8630                     tag : 'i',
8631                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8632                     tooltip : 'This field is required'
8633                 },
8634                 {
8635                     tag: 'label',
8636                     'for' :  id,
8637                     cls : 'control-label',
8638                     html : this.fieldLabel
8639
8640                 },
8641                 {
8642                     cls : "", 
8643                     cn: [
8644                         inputblock
8645                     ]
8646                 }
8647             ];
8648             
8649             var labelCfg = cfg.cn[1];
8650             var contentCfg = cfg.cn[2];
8651             
8652             if(this.indicatorpos == 'right'){
8653                 cfg.cn = [
8654                     {
8655                         tag: 'label',
8656                         'for' :  id,
8657                         cls : 'control-label',
8658                         html : this.fieldLabel
8659
8660                     },
8661                     {
8662                         tag : 'i',
8663                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8664                         tooltip : 'This field is required'
8665                     },
8666                     {
8667                         cls : "",
8668                         cn: [
8669                             inputblock
8670                         ]
8671                     }
8672
8673                 ];
8674                 
8675                 labelCfg = cfg.cn[0];
8676                 contentCfg = cfg.cn[2];
8677             
8678             }
8679             
8680             if(this.labelWidth > 12){
8681                 labelCfg.style = "width: " + this.labelWidth + 'px';
8682             }
8683             
8684             if(this.labelWidth < 13 && this.labelmd == 0){
8685                 this.labelmd = this.labelWidth;
8686             }
8687             
8688             if(this.labellg > 0){
8689                 labelCfg.cls += ' col-lg-' + this.labellg;
8690                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8691             }
8692             
8693             if(this.labelmd > 0){
8694                 labelCfg.cls += ' col-md-' + this.labelmd;
8695                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8696             }
8697             
8698             if(this.labelsm > 0){
8699                 labelCfg.cls += ' col-sm-' + this.labelsm;
8700                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8701             }
8702             
8703             if(this.labelxs > 0){
8704                 labelCfg.cls += ' col-xs-' + this.labelxs;
8705                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8706             }
8707             
8708             
8709         } else if ( this.fieldLabel.length) {
8710                 
8711             cfg.cn = [
8712                 {
8713                     tag : 'i',
8714                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8715                     tooltip : 'This field is required'
8716                 },
8717                 {
8718                     tag: 'label',
8719                    //cls : 'input-group-addon',
8720                     html : this.fieldLabel
8721
8722                 },
8723
8724                inputblock
8725
8726            ];
8727            
8728            if(this.indicatorpos == 'right'){
8729                 
8730                 cfg.cn = [
8731                     {
8732                         tag: 'label',
8733                        //cls : 'input-group-addon',
8734                         html : this.fieldLabel
8735
8736                     },
8737                     {
8738                         tag : 'i',
8739                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8740                         tooltip : 'This field is required'
8741                     },
8742
8743                    inputblock
8744
8745                ];
8746
8747             }
8748
8749         } else {
8750             
8751             cfg.cn = [
8752
8753                     inputblock
8754
8755             ];
8756                 
8757                 
8758         };
8759         
8760         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8761            cfg.cls += ' navbar-form';
8762         }
8763         
8764         if (this.parentType === 'NavGroup') {
8765            cfg.cls += ' navbar-form';
8766            cfg.tag = 'li';
8767         }
8768         
8769         return cfg;
8770         
8771     },
8772     /**
8773      * return the real input element.
8774      */
8775     inputEl: function ()
8776     {
8777         return this.el.select('input.form-control',true).first();
8778     },
8779     
8780     tooltipEl : function()
8781     {
8782         return this.inputEl();
8783     },
8784     
8785     indicatorEl : function()
8786     {
8787         var indicator = this.el.select('i.roo-required-indicator',true).first();
8788         
8789         if(!indicator){
8790             return false;
8791         }
8792         
8793         return indicator;
8794         
8795     },
8796     
8797     setDisabled : function(v)
8798     {
8799         var i  = this.inputEl().dom;
8800         if (!v) {
8801             i.removeAttribute('disabled');
8802             return;
8803             
8804         }
8805         i.setAttribute('disabled','true');
8806     },
8807     initEvents : function()
8808     {
8809           
8810         this.inputEl().on("keydown" , this.fireKey,  this);
8811         this.inputEl().on("focus", this.onFocus,  this);
8812         this.inputEl().on("blur", this.onBlur,  this);
8813         
8814         this.inputEl().relayEvent('keyup', this);
8815         
8816         this.indicator = this.indicatorEl();
8817         
8818         if(this.indicator){
8819             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8820             this.indicator.hide();
8821         }
8822  
8823         // reference to original value for reset
8824         this.originalValue = this.getValue();
8825         //Roo.form.TextField.superclass.initEvents.call(this);
8826         if(this.validationEvent == 'keyup'){
8827             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8828             this.inputEl().on('keyup', this.filterValidation, this);
8829         }
8830         else if(this.validationEvent !== false){
8831             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8832         }
8833         
8834         if(this.selectOnFocus){
8835             this.on("focus", this.preFocus, this);
8836             
8837         }
8838         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8839             this.inputEl().on("keypress", this.filterKeys, this);
8840         } else {
8841             this.inputEl().relayEvent('keypress', this);
8842         }
8843        /* if(this.grow){
8844             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8845             this.el.on("click", this.autoSize,  this);
8846         }
8847         */
8848         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8849             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8850         }
8851         
8852         if (typeof(this.before) == 'object') {
8853             this.before.render(this.el.select('.roo-input-before',true).first());
8854         }
8855         if (typeof(this.after) == 'object') {
8856             this.after.render(this.el.select('.roo-input-after',true).first());
8857         }
8858         
8859         
8860     },
8861     filterValidation : function(e){
8862         if(!e.isNavKeyPress()){
8863             this.validationTask.delay(this.validationDelay);
8864         }
8865     },
8866      /**
8867      * Validates the field value
8868      * @return {Boolean} True if the value is valid, else false
8869      */
8870     validate : function(){
8871         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8872         if(this.disabled || this.validateValue(this.getRawValue())){
8873             this.markValid();
8874             return true;
8875         }
8876         
8877         this.markInvalid();
8878         return false;
8879     },
8880     
8881     
8882     /**
8883      * Validates a value according to the field's validation rules and marks the field as invalid
8884      * if the validation fails
8885      * @param {Mixed} value The value to validate
8886      * @return {Boolean} True if the value is valid, else false
8887      */
8888     validateValue : function(value){
8889         if(value.length < 1)  { // if it's blank
8890             if(this.allowBlank){
8891                 return true;
8892             }
8893             return false;
8894         }
8895         
8896         if(value.length < this.minLength){
8897             return false;
8898         }
8899         if(value.length > this.maxLength){
8900             return false;
8901         }
8902         if(this.vtype){
8903             var vt = Roo.form.VTypes;
8904             if(!vt[this.vtype](value, this)){
8905                 return false;
8906             }
8907         }
8908         if(typeof this.validator == "function"){
8909             var msg = this.validator(value);
8910             if(msg !== true){
8911                 return false;
8912             }
8913         }
8914         
8915         if(this.regex && !this.regex.test(value)){
8916             return false;
8917         }
8918         
8919         return true;
8920     },
8921
8922     
8923     
8924      // private
8925     fireKey : function(e){
8926         //Roo.log('field ' + e.getKey());
8927         if(e.isNavKeyPress()){
8928             this.fireEvent("specialkey", this, e);
8929         }
8930     },
8931     focus : function (selectText){
8932         if(this.rendered){
8933             this.inputEl().focus();
8934             if(selectText === true){
8935                 this.inputEl().dom.select();
8936             }
8937         }
8938         return this;
8939     } ,
8940     
8941     onFocus : function(){
8942         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8943            // this.el.addClass(this.focusClass);
8944         }
8945         if(!this.hasFocus){
8946             this.hasFocus = true;
8947             this.startValue = this.getValue();
8948             this.fireEvent("focus", this);
8949         }
8950     },
8951     
8952     beforeBlur : Roo.emptyFn,
8953
8954     
8955     // private
8956     onBlur : function(){
8957         this.beforeBlur();
8958         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8959             //this.el.removeClass(this.focusClass);
8960         }
8961         this.hasFocus = false;
8962         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8963             this.validate();
8964         }
8965         var v = this.getValue();
8966         if(String(v) !== String(this.startValue)){
8967             this.fireEvent('change', this, v, this.startValue);
8968         }
8969         this.fireEvent("blur", this);
8970     },
8971     
8972     /**
8973      * Resets the current field value to the originally loaded value and clears any validation messages
8974      */
8975     reset : function(){
8976         this.setValue(this.originalValue);
8977         this.validate();
8978     },
8979      /**
8980      * Returns the name of the field
8981      * @return {Mixed} name The name field
8982      */
8983     getName: function(){
8984         return this.name;
8985     },
8986      /**
8987      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8988      * @return {Mixed} value The field value
8989      */
8990     getValue : function(){
8991         
8992         var v = this.inputEl().getValue();
8993         
8994         return v;
8995     },
8996     /**
8997      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8998      * @return {Mixed} value The field value
8999      */
9000     getRawValue : function(){
9001         var v = this.inputEl().getValue();
9002         
9003         return v;
9004     },
9005     
9006     /**
9007      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9008      * @param {Mixed} value The value to set
9009      */
9010     setRawValue : function(v){
9011         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9012     },
9013     
9014     selectText : function(start, end){
9015         var v = this.getRawValue();
9016         if(v.length > 0){
9017             start = start === undefined ? 0 : start;
9018             end = end === undefined ? v.length : end;
9019             var d = this.inputEl().dom;
9020             if(d.setSelectionRange){
9021                 d.setSelectionRange(start, end);
9022             }else if(d.createTextRange){
9023                 var range = d.createTextRange();
9024                 range.moveStart("character", start);
9025                 range.moveEnd("character", v.length-end);
9026                 range.select();
9027             }
9028         }
9029     },
9030     
9031     /**
9032      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9033      * @param {Mixed} value The value to set
9034      */
9035     setValue : function(v){
9036         this.value = v;
9037         if(this.rendered){
9038             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9039             this.validate();
9040         }
9041     },
9042     
9043     /*
9044     processValue : function(value){
9045         if(this.stripCharsRe){
9046             var newValue = value.replace(this.stripCharsRe, '');
9047             if(newValue !== value){
9048                 this.setRawValue(newValue);
9049                 return newValue;
9050             }
9051         }
9052         return value;
9053     },
9054   */
9055     preFocus : function(){
9056         
9057         if(this.selectOnFocus){
9058             this.inputEl().dom.select();
9059         }
9060     },
9061     filterKeys : function(e){
9062         var k = e.getKey();
9063         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9064             return;
9065         }
9066         var c = e.getCharCode(), cc = String.fromCharCode(c);
9067         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9068             return;
9069         }
9070         if(!this.maskRe.test(cc)){
9071             e.stopEvent();
9072         }
9073     },
9074      /**
9075      * Clear any invalid styles/messages for this field
9076      */
9077     clearInvalid : function(){
9078         
9079         if(!this.el || this.preventMark){ // not rendered
9080             return;
9081         }
9082         
9083         if(this.indicator){
9084             this.indicator.hide();
9085         }
9086         
9087         this.el.removeClass(this.invalidClass);
9088         
9089         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9090             
9091             var feedback = this.el.select('.form-control-feedback', true).first();
9092             
9093             if(feedback){
9094                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9095             }
9096             
9097         }
9098         
9099         this.fireEvent('valid', this);
9100     },
9101     
9102      /**
9103      * Mark this field as valid
9104      */
9105     markValid : function()
9106     {
9107         if(!this.el  || this.preventMark){ // not rendered...
9108             return;
9109         }
9110         
9111         this.el.removeClass([this.invalidClass, this.validClass]);
9112         
9113         var feedback = this.el.select('.form-control-feedback', true).first();
9114             
9115         if(feedback){
9116             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9117         }
9118
9119         if(this.disabled){
9120             return;
9121         }
9122         
9123         if(this.allowBlank && !this.getRawValue().length){
9124             return;
9125         }
9126         
9127         if(this.indicator){
9128             this.indicator.hide();
9129         }
9130         
9131         this.el.addClass(this.validClass);
9132         
9133         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9134             
9135             var feedback = this.el.select('.form-control-feedback', true).first();
9136             
9137             if(feedback){
9138                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9139                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9140             }
9141             
9142         }
9143         
9144         this.fireEvent('valid', this);
9145     },
9146     
9147      /**
9148      * Mark this field as invalid
9149      * @param {String} msg The validation message
9150      */
9151     markInvalid : function(msg)
9152     {
9153         if(!this.el  || this.preventMark){ // not rendered
9154             return;
9155         }
9156         
9157         this.el.removeClass([this.invalidClass, this.validClass]);
9158         
9159         var feedback = this.el.select('.form-control-feedback', true).first();
9160             
9161         if(feedback){
9162             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9163         }
9164
9165         if(this.disabled){
9166             return;
9167         }
9168         
9169         if(this.allowBlank && !this.getRawValue().length){
9170             return;
9171         }
9172         
9173         if(this.indicator){
9174             this.indicator.show();
9175         }
9176         
9177         this.el.addClass(this.invalidClass);
9178         
9179         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9180             
9181             var feedback = this.el.select('.form-control-feedback', true).first();
9182             
9183             if(feedback){
9184                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9185                 
9186                 if(this.getValue().length || this.forceFeedback){
9187                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9188                 }
9189                 
9190             }
9191             
9192         }
9193         
9194         this.fireEvent('invalid', this, msg);
9195     },
9196     // private
9197     SafariOnKeyDown : function(event)
9198     {
9199         // this is a workaround for a password hang bug on chrome/ webkit.
9200         if (this.inputEl().dom.type != 'password') {
9201             return;
9202         }
9203         
9204         var isSelectAll = false;
9205         
9206         if(this.inputEl().dom.selectionEnd > 0){
9207             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9208         }
9209         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9210             event.preventDefault();
9211             this.setValue('');
9212             return;
9213         }
9214         
9215         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9216             
9217             event.preventDefault();
9218             // this is very hacky as keydown always get's upper case.
9219             //
9220             var cc = String.fromCharCode(event.getCharCode());
9221             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9222             
9223         }
9224     },
9225     adjustWidth : function(tag, w){
9226         tag = tag.toLowerCase();
9227         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9228             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9229                 if(tag == 'input'){
9230                     return w + 2;
9231                 }
9232                 if(tag == 'textarea'){
9233                     return w-2;
9234                 }
9235             }else if(Roo.isOpera){
9236                 if(tag == 'input'){
9237                     return w + 2;
9238                 }
9239                 if(tag == 'textarea'){
9240                     return w-2;
9241                 }
9242             }
9243         }
9244         return w;
9245     }
9246     
9247 });
9248
9249  
9250 /*
9251  * - LGPL
9252  *
9253  * Input
9254  * 
9255  */
9256
9257 /**
9258  * @class Roo.bootstrap.TextArea
9259  * @extends Roo.bootstrap.Input
9260  * Bootstrap TextArea class
9261  * @cfg {Number} cols Specifies the visible width of a text area
9262  * @cfg {Number} rows Specifies the visible number of lines in a text area
9263  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9264  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9265  * @cfg {string} html text
9266  * 
9267  * @constructor
9268  * Create a new TextArea
9269  * @param {Object} config The config object
9270  */
9271
9272 Roo.bootstrap.TextArea = function(config){
9273     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9274    
9275 };
9276
9277 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9278      
9279     cols : false,
9280     rows : 5,
9281     readOnly : false,
9282     warp : 'soft',
9283     resize : false,
9284     value: false,
9285     html: false,
9286     
9287     getAutoCreate : function(){
9288         
9289         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9290         
9291         var id = Roo.id();
9292         
9293         var cfg = {};
9294         
9295         var input =  {
9296             tag: 'textarea',
9297             id : id,
9298             warp : this.warp,
9299             rows : this.rows,
9300             value : this.value || '',
9301             html: this.html || '',
9302             cls : 'form-control',
9303             placeholder : this.placeholder || '' 
9304             
9305         };
9306         
9307         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9308             input.maxLength = this.maxLength;
9309         }
9310         
9311         if(this.resize){
9312             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9313         }
9314         
9315         if(this.cols){
9316             input.cols = this.cols;
9317         }
9318         
9319         if (this.readOnly) {
9320             input.readonly = true;
9321         }
9322         
9323         if (this.name) {
9324             input.name = this.name;
9325         }
9326         
9327         if (this.size) {
9328             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9329         }
9330         
9331         var settings=this;
9332         ['xs','sm','md','lg'].map(function(size){
9333             if (settings[size]) {
9334                 cfg.cls += ' col-' + size + '-' + settings[size];
9335             }
9336         });
9337         
9338         var inputblock = input;
9339         
9340         if(this.hasFeedback && !this.allowBlank){
9341             
9342             var feedback = {
9343                 tag: 'span',
9344                 cls: 'glyphicon form-control-feedback'
9345             };
9346
9347             inputblock = {
9348                 cls : 'has-feedback',
9349                 cn :  [
9350                     input,
9351                     feedback
9352                 ] 
9353             };  
9354         }
9355         
9356         
9357         if (this.before || this.after) {
9358             
9359             inputblock = {
9360                 cls : 'input-group',
9361                 cn :  [] 
9362             };
9363             if (this.before) {
9364                 inputblock.cn.push({
9365                     tag :'span',
9366                     cls : 'input-group-addon',
9367                     html : this.before
9368                 });
9369             }
9370             
9371             inputblock.cn.push(input);
9372             
9373             if(this.hasFeedback && !this.allowBlank){
9374                 inputblock.cls += ' has-feedback';
9375                 inputblock.cn.push(feedback);
9376             }
9377             
9378             if (this.after) {
9379                 inputblock.cn.push({
9380                     tag :'span',
9381                     cls : 'input-group-addon',
9382                     html : this.after
9383                 });
9384             }
9385             
9386         }
9387         
9388         if (align ==='left' && this.fieldLabel.length) {
9389             cfg.cn = [
9390                 {
9391                     tag: 'label',
9392                     'for' :  id,
9393                     cls : 'control-label',
9394                     html : this.fieldLabel
9395                 },
9396                 {
9397                     cls : "",
9398                     cn: [
9399                         inputblock
9400                     ]
9401                 }
9402
9403             ];
9404             
9405             if(this.labelWidth > 12){
9406                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9407             }
9408
9409             if(this.labelWidth < 13 && this.labelmd == 0){
9410                 this.labelmd = this.labelWidth;
9411             }
9412
9413             if(this.labellg > 0){
9414                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9415                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9416             }
9417
9418             if(this.labelmd > 0){
9419                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9420                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9421             }
9422
9423             if(this.labelsm > 0){
9424                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9425                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9426             }
9427
9428             if(this.labelxs > 0){
9429                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9430                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9431             }
9432             
9433         } else if ( this.fieldLabel.length) {
9434             cfg.cn = [
9435
9436                {
9437                    tag: 'label',
9438                    //cls : 'input-group-addon',
9439                    html : this.fieldLabel
9440
9441                },
9442
9443                inputblock
9444
9445            ];
9446
9447         } else {
9448
9449             cfg.cn = [
9450
9451                 inputblock
9452
9453             ];
9454                 
9455         }
9456         
9457         if (this.disabled) {
9458             input.disabled=true;
9459         }
9460         
9461         return cfg;
9462         
9463     },
9464     /**
9465      * return the real textarea element.
9466      */
9467     inputEl: function ()
9468     {
9469         return this.el.select('textarea.form-control',true).first();
9470     },
9471     
9472     /**
9473      * Clear any invalid styles/messages for this field
9474      */
9475     clearInvalid : function()
9476     {
9477         
9478         if(!this.el || this.preventMark){ // not rendered
9479             return;
9480         }
9481         
9482         var label = this.el.select('label', true).first();
9483         var icon = this.el.select('i.fa-star', true).first();
9484         
9485         if(label && icon){
9486             icon.remove();
9487         }
9488         
9489         this.el.removeClass(this.invalidClass);
9490         
9491         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9492             
9493             var feedback = this.el.select('.form-control-feedback', true).first();
9494             
9495             if(feedback){
9496                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9497             }
9498             
9499         }
9500         
9501         this.fireEvent('valid', this);
9502     },
9503     
9504      /**
9505      * Mark this field as valid
9506      */
9507     markValid : function()
9508     {
9509         if(!this.el  || this.preventMark){ // not rendered
9510             return;
9511         }
9512         
9513         this.el.removeClass([this.invalidClass, this.validClass]);
9514         
9515         var feedback = this.el.select('.form-control-feedback', true).first();
9516             
9517         if(feedback){
9518             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9519         }
9520
9521         if(this.disabled || this.allowBlank){
9522             return;
9523         }
9524         
9525         var label = this.el.select('label', true).first();
9526         var icon = this.el.select('i.fa-star', true).first();
9527         
9528         if(label && icon){
9529             icon.remove();
9530         }
9531         
9532         this.el.addClass(this.validClass);
9533         
9534         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9535             
9536             var feedback = this.el.select('.form-control-feedback', true).first();
9537             
9538             if(feedback){
9539                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9540                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9541             }
9542             
9543         }
9544         
9545         this.fireEvent('valid', this);
9546     },
9547     
9548      /**
9549      * Mark this field as invalid
9550      * @param {String} msg The validation message
9551      */
9552     markInvalid : function(msg)
9553     {
9554         if(!this.el  || this.preventMark){ // not rendered
9555             return;
9556         }
9557         
9558         this.el.removeClass([this.invalidClass, this.validClass]);
9559         
9560         var feedback = this.el.select('.form-control-feedback', true).first();
9561             
9562         if(feedback){
9563             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9564         }
9565
9566         if(this.disabled || this.allowBlank){
9567             return;
9568         }
9569         
9570         var label = this.el.select('label', true).first();
9571         var icon = this.el.select('i.fa-star', true).first();
9572         
9573         if(!this.getValue().length && label && !icon){
9574             this.el.createChild({
9575                 tag : 'i',
9576                 cls : 'text-danger fa fa-lg fa-star',
9577                 tooltip : 'This field is required',
9578                 style : 'margin-right:5px;'
9579             }, label, true);
9580         }
9581
9582         this.el.addClass(this.invalidClass);
9583         
9584         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9585             
9586             var feedback = this.el.select('.form-control-feedback', true).first();
9587             
9588             if(feedback){
9589                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9590                 
9591                 if(this.getValue().length || this.forceFeedback){
9592                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9593                 }
9594                 
9595             }
9596             
9597         }
9598         
9599         this.fireEvent('invalid', this, msg);
9600     }
9601 });
9602
9603  
9604 /*
9605  * - LGPL
9606  *
9607  * trigger field - base class for combo..
9608  * 
9609  */
9610  
9611 /**
9612  * @class Roo.bootstrap.TriggerField
9613  * @extends Roo.bootstrap.Input
9614  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9615  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9616  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9617  * for which you can provide a custom implementation.  For example:
9618  * <pre><code>
9619 var trigger = new Roo.bootstrap.TriggerField();
9620 trigger.onTriggerClick = myTriggerFn;
9621 trigger.applyTo('my-field');
9622 </code></pre>
9623  *
9624  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9625  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9626  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9627  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9628  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9629
9630  * @constructor
9631  * Create a new TriggerField.
9632  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9633  * to the base TextField)
9634  */
9635 Roo.bootstrap.TriggerField = function(config){
9636     this.mimicing = false;
9637     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9638 };
9639
9640 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9641     /**
9642      * @cfg {String} triggerClass A CSS class to apply to the trigger
9643      */
9644      /**
9645      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9646      */
9647     hideTrigger:false,
9648
9649     /**
9650      * @cfg {Boolean} removable (true|false) special filter default false
9651      */
9652     removable : false,
9653     
9654     /** @cfg {Boolean} grow @hide */
9655     /** @cfg {Number} growMin @hide */
9656     /** @cfg {Number} growMax @hide */
9657
9658     /**
9659      * @hide 
9660      * @method
9661      */
9662     autoSize: Roo.emptyFn,
9663     // private
9664     monitorTab : true,
9665     // private
9666     deferHeight : true,
9667
9668     
9669     actionMode : 'wrap',
9670     
9671     caret : false,
9672     
9673     
9674     getAutoCreate : function(){
9675        
9676         var align = this.labelAlign || this.parentLabelAlign();
9677         
9678         var id = Roo.id();
9679         
9680         var cfg = {
9681             cls: 'form-group' //input-group
9682         };
9683         
9684         
9685         var input =  {
9686             tag: 'input',
9687             id : id,
9688             type : this.inputType,
9689             cls : 'form-control',
9690             autocomplete: 'new-password',
9691             placeholder : this.placeholder || '' 
9692             
9693         };
9694         if (this.name) {
9695             input.name = this.name;
9696         }
9697         if (this.size) {
9698             input.cls += ' input-' + this.size;
9699         }
9700         
9701         if (this.disabled) {
9702             input.disabled=true;
9703         }
9704         
9705         var inputblock = input;
9706         
9707         if(this.hasFeedback && !this.allowBlank){
9708             
9709             var feedback = {
9710                 tag: 'span',
9711                 cls: 'glyphicon form-control-feedback'
9712             };
9713             
9714             if(this.removable && !this.editable && !this.tickable){
9715                 inputblock = {
9716                     cls : 'has-feedback',
9717                     cn :  [
9718                         inputblock,
9719                         {
9720                             tag: 'button',
9721                             html : 'x',
9722                             cls : 'roo-combo-removable-btn close'
9723                         },
9724                         feedback
9725                     ] 
9726                 };
9727             } else {
9728                 inputblock = {
9729                     cls : 'has-feedback',
9730                     cn :  [
9731                         inputblock,
9732                         feedback
9733                     ] 
9734                 };
9735             }
9736
9737         } else {
9738             if(this.removable && !this.editable && !this.tickable){
9739                 inputblock = {
9740                     cls : 'roo-removable',
9741                     cn :  [
9742                         inputblock,
9743                         {
9744                             tag: 'button',
9745                             html : 'x',
9746                             cls : 'roo-combo-removable-btn close'
9747                         }
9748                     ] 
9749                 };
9750             }
9751         }
9752         
9753         if (this.before || this.after) {
9754             
9755             inputblock = {
9756                 cls : 'input-group',
9757                 cn :  [] 
9758             };
9759             if (this.before) {
9760                 inputblock.cn.push({
9761                     tag :'span',
9762                     cls : 'input-group-addon',
9763                     html : this.before
9764                 });
9765             }
9766             
9767             inputblock.cn.push(input);
9768             
9769             if(this.hasFeedback && !this.allowBlank){
9770                 inputblock.cls += ' has-feedback';
9771                 inputblock.cn.push(feedback);
9772             }
9773             
9774             if (this.after) {
9775                 inputblock.cn.push({
9776                     tag :'span',
9777                     cls : 'input-group-addon',
9778                     html : this.after
9779                 });
9780             }
9781             
9782         };
9783         
9784         var box = {
9785             tag: 'div',
9786             cn: [
9787                 {
9788                     tag: 'input',
9789                     type : 'hidden',
9790                     cls: 'form-hidden-field'
9791                 },
9792                 inputblock
9793             ]
9794             
9795         };
9796         
9797         if(this.multiple){
9798             box = {
9799                 tag: 'div',
9800                 cn: [
9801                     {
9802                         tag: 'input',
9803                         type : 'hidden',
9804                         cls: 'form-hidden-field'
9805                     },
9806                     {
9807                         tag: 'ul',
9808                         cls: 'roo-select2-choices',
9809                         cn:[
9810                             {
9811                                 tag: 'li',
9812                                 cls: 'roo-select2-search-field',
9813                                 cn: [
9814
9815                                     inputblock
9816                                 ]
9817                             }
9818                         ]
9819                     }
9820                 ]
9821             }
9822         };
9823         
9824         var combobox = {
9825             cls: 'roo-select2-container input-group',
9826             cn: [
9827                 box
9828 //                {
9829 //                    tag: 'ul',
9830 //                    cls: 'typeahead typeahead-long dropdown-menu',
9831 //                    style: 'display:none'
9832 //                }
9833             ]
9834         };
9835         
9836         if(!this.multiple && this.showToggleBtn){
9837             
9838             var caret = {
9839                         tag: 'span',
9840                         cls: 'caret'
9841              };
9842             if (this.caret != false) {
9843                 caret = {
9844                      tag: 'i',
9845                      cls: 'fa fa-' + this.caret
9846                 };
9847                 
9848             }
9849             
9850             combobox.cn.push({
9851                 tag :'span',
9852                 cls : 'input-group-addon btn dropdown-toggle',
9853                 cn : [
9854                     caret,
9855                     {
9856                         tag: 'span',
9857                         cls: 'combobox-clear',
9858                         cn  : [
9859                             {
9860                                 tag : 'i',
9861                                 cls: 'icon-remove'
9862                             }
9863                         ]
9864                     }
9865                 ]
9866
9867             })
9868         }
9869         
9870         if(this.multiple){
9871             combobox.cls += ' roo-select2-container-multi';
9872         }
9873         
9874         if (align ==='left' && this.fieldLabel.length) {
9875             
9876             cfg.cls += ' roo-form-group-label-left';
9877
9878             cfg.cn = [
9879                 {
9880                     tag : 'i',
9881                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9882                     tooltip : 'This field is required'
9883                 },
9884                 {
9885                     tag: 'label',
9886                     'for' :  id,
9887                     cls : 'control-label',
9888                     html : this.fieldLabel
9889
9890                 },
9891                 {
9892                     cls : "", 
9893                     cn: [
9894                         combobox
9895                     ]
9896                 }
9897
9898             ];
9899             
9900             var labelCfg = cfg.cn[1];
9901             var contentCfg = cfg.cn[2];
9902             
9903             if(this.indicatorpos == 'right'){
9904                 cfg.cn = [
9905                     {
9906                         tag: 'label',
9907                         'for' :  id,
9908                         cls : 'control-label',
9909                         cn : [
9910                             {
9911                                 tag : 'span',
9912                                 html : this.fieldLabel
9913                             },
9914                             {
9915                                 tag : 'i',
9916                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9917                                 tooltip : 'This field is required'
9918                             }
9919                         ]
9920                     },
9921                     {
9922                         cls : "", 
9923                         cn: [
9924                             combobox
9925                         ]
9926                     }
9927
9928                 ];
9929                 
9930                 labelCfg = cfg.cn[0];
9931                 contentCfg = cfg.cn[1];
9932             }
9933             
9934             if(this.labelWidth > 12){
9935                 labelCfg.style = "width: " + this.labelWidth + 'px';
9936             }
9937             
9938             if(this.labelWidth < 13 && this.labelmd == 0){
9939                 this.labelmd = this.labelWidth;
9940             }
9941             
9942             if(this.labellg > 0){
9943                 labelCfg.cls += ' col-lg-' + this.labellg;
9944                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9945             }
9946             
9947             if(this.labelmd > 0){
9948                 labelCfg.cls += ' col-md-' + this.labelmd;
9949                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9950             }
9951             
9952             if(this.labelsm > 0){
9953                 labelCfg.cls += ' col-sm-' + this.labelsm;
9954                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9955             }
9956             
9957             if(this.labelxs > 0){
9958                 labelCfg.cls += ' col-xs-' + this.labelxs;
9959                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9960             }
9961             
9962         } else if ( this.fieldLabel.length) {
9963 //                Roo.log(" label");
9964             cfg.cn = [
9965                 {
9966                    tag : 'i',
9967                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9968                    tooltip : 'This field is required'
9969                },
9970                {
9971                    tag: 'label',
9972                    //cls : 'input-group-addon',
9973                    html : this.fieldLabel
9974
9975                },
9976
9977                combobox
9978
9979             ];
9980             
9981             if(this.indicatorpos == 'right'){
9982                 
9983                 cfg.cn = [
9984                     {
9985                        tag: 'label',
9986                        cn : [
9987                            {
9988                                tag : 'span',
9989                                html : this.fieldLabel
9990                            },
9991                            {
9992                               tag : 'i',
9993                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9994                               tooltip : 'This field is required'
9995                            }
9996                        ]
9997
9998                     },
9999                     combobox
10000
10001                 ];
10002
10003             }
10004
10005         } else {
10006             
10007 //                Roo.log(" no label && no align");
10008                 cfg = combobox
10009                      
10010                 
10011         }
10012         
10013         var settings=this;
10014         ['xs','sm','md','lg'].map(function(size){
10015             if (settings[size]) {
10016                 cfg.cls += ' col-' + size + '-' + settings[size];
10017             }
10018         });
10019         
10020         return cfg;
10021         
10022     },
10023     
10024     
10025     
10026     // private
10027     onResize : function(w, h){
10028 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10029 //        if(typeof w == 'number'){
10030 //            var x = w - this.trigger.getWidth();
10031 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10032 //            this.trigger.setStyle('left', x+'px');
10033 //        }
10034     },
10035
10036     // private
10037     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10038
10039     // private
10040     getResizeEl : function(){
10041         return this.inputEl();
10042     },
10043
10044     // private
10045     getPositionEl : function(){
10046         return this.inputEl();
10047     },
10048
10049     // private
10050     alignErrorIcon : function(){
10051         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10052     },
10053
10054     // private
10055     initEvents : function(){
10056         
10057         this.createList();
10058         
10059         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10060         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10061         if(!this.multiple && this.showToggleBtn){
10062             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10063             if(this.hideTrigger){
10064                 this.trigger.setDisplayed(false);
10065             }
10066             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10067         }
10068         
10069         if(this.multiple){
10070             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10071         }
10072         
10073         if(this.removable && !this.editable && !this.tickable){
10074             var close = this.closeTriggerEl();
10075             
10076             if(close){
10077                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10078                 close.on('click', this.removeBtnClick, this, close);
10079             }
10080         }
10081         
10082         //this.trigger.addClassOnOver('x-form-trigger-over');
10083         //this.trigger.addClassOnClick('x-form-trigger-click');
10084         
10085         //if(!this.width){
10086         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10087         //}
10088     },
10089     
10090     closeTriggerEl : function()
10091     {
10092         var close = this.el.select('.roo-combo-removable-btn', true).first();
10093         return close ? close : false;
10094     },
10095     
10096     removeBtnClick : function(e, h, el)
10097     {
10098         e.preventDefault();
10099         
10100         if(this.fireEvent("remove", this) !== false){
10101             this.reset();
10102             this.fireEvent("afterremove", this)
10103         }
10104     },
10105     
10106     createList : function()
10107     {
10108         this.list = Roo.get(document.body).createChild({
10109             tag: 'ul',
10110             cls: 'typeahead typeahead-long dropdown-menu',
10111             style: 'display:none'
10112         });
10113         
10114         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10115         
10116     },
10117
10118     // private
10119     initTrigger : function(){
10120        
10121     },
10122
10123     // private
10124     onDestroy : function(){
10125         if(this.trigger){
10126             this.trigger.removeAllListeners();
10127           //  this.trigger.remove();
10128         }
10129         //if(this.wrap){
10130         //    this.wrap.remove();
10131         //}
10132         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10133     },
10134
10135     // private
10136     onFocus : function(){
10137         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10138         /*
10139         if(!this.mimicing){
10140             this.wrap.addClass('x-trigger-wrap-focus');
10141             this.mimicing = true;
10142             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10143             if(this.monitorTab){
10144                 this.el.on("keydown", this.checkTab, this);
10145             }
10146         }
10147         */
10148     },
10149
10150     // private
10151     checkTab : function(e){
10152         if(e.getKey() == e.TAB){
10153             this.triggerBlur();
10154         }
10155     },
10156
10157     // private
10158     onBlur : function(){
10159         // do nothing
10160     },
10161
10162     // private
10163     mimicBlur : function(e, t){
10164         /*
10165         if(!this.wrap.contains(t) && this.validateBlur()){
10166             this.triggerBlur();
10167         }
10168         */
10169     },
10170
10171     // private
10172     triggerBlur : function(){
10173         this.mimicing = false;
10174         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10175         if(this.monitorTab){
10176             this.el.un("keydown", this.checkTab, this);
10177         }
10178         //this.wrap.removeClass('x-trigger-wrap-focus');
10179         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10180     },
10181
10182     // private
10183     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10184     validateBlur : function(e, t){
10185         return true;
10186     },
10187
10188     // private
10189     onDisable : function(){
10190         this.inputEl().dom.disabled = true;
10191         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10192         //if(this.wrap){
10193         //    this.wrap.addClass('x-item-disabled');
10194         //}
10195     },
10196
10197     // private
10198     onEnable : function(){
10199         this.inputEl().dom.disabled = false;
10200         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10201         //if(this.wrap){
10202         //    this.el.removeClass('x-item-disabled');
10203         //}
10204     },
10205
10206     // private
10207     onShow : function(){
10208         var ae = this.getActionEl();
10209         
10210         if(ae){
10211             ae.dom.style.display = '';
10212             ae.dom.style.visibility = 'visible';
10213         }
10214     },
10215
10216     // private
10217     
10218     onHide : function(){
10219         var ae = this.getActionEl();
10220         ae.dom.style.display = 'none';
10221     },
10222
10223     /**
10224      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10225      * by an implementing function.
10226      * @method
10227      * @param {EventObject} e
10228      */
10229     onTriggerClick : Roo.emptyFn
10230 });
10231  /*
10232  * Based on:
10233  * Ext JS Library 1.1.1
10234  * Copyright(c) 2006-2007, Ext JS, LLC.
10235  *
10236  * Originally Released Under LGPL - original licence link has changed is not relivant.
10237  *
10238  * Fork - LGPL
10239  * <script type="text/javascript">
10240  */
10241
10242
10243 /**
10244  * @class Roo.data.SortTypes
10245  * @singleton
10246  * Defines the default sorting (casting?) comparison functions used when sorting data.
10247  */
10248 Roo.data.SortTypes = {
10249     /**
10250      * Default sort that does nothing
10251      * @param {Mixed} s The value being converted
10252      * @return {Mixed} The comparison value
10253      */
10254     none : function(s){
10255         return s;
10256     },
10257     
10258     /**
10259      * The regular expression used to strip tags
10260      * @type {RegExp}
10261      * @property
10262      */
10263     stripTagsRE : /<\/?[^>]+>/gi,
10264     
10265     /**
10266      * Strips all HTML tags to sort on text only
10267      * @param {Mixed} s The value being converted
10268      * @return {String} The comparison value
10269      */
10270     asText : function(s){
10271         return String(s).replace(this.stripTagsRE, "");
10272     },
10273     
10274     /**
10275      * Strips all HTML tags to sort on text only - Case insensitive
10276      * @param {Mixed} s The value being converted
10277      * @return {String} The comparison value
10278      */
10279     asUCText : function(s){
10280         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10281     },
10282     
10283     /**
10284      * Case insensitive string
10285      * @param {Mixed} s The value being converted
10286      * @return {String} The comparison value
10287      */
10288     asUCString : function(s) {
10289         return String(s).toUpperCase();
10290     },
10291     
10292     /**
10293      * Date sorting
10294      * @param {Mixed} s The value being converted
10295      * @return {Number} The comparison value
10296      */
10297     asDate : function(s) {
10298         if(!s){
10299             return 0;
10300         }
10301         if(s instanceof Date){
10302             return s.getTime();
10303         }
10304         return Date.parse(String(s));
10305     },
10306     
10307     /**
10308      * Float sorting
10309      * @param {Mixed} s The value being converted
10310      * @return {Float} The comparison value
10311      */
10312     asFloat : function(s) {
10313         var val = parseFloat(String(s).replace(/,/g, ""));
10314         if(isNaN(val)) {
10315             val = 0;
10316         }
10317         return val;
10318     },
10319     
10320     /**
10321      * Integer sorting
10322      * @param {Mixed} s The value being converted
10323      * @return {Number} The comparison value
10324      */
10325     asInt : function(s) {
10326         var val = parseInt(String(s).replace(/,/g, ""));
10327         if(isNaN(val)) {
10328             val = 0;
10329         }
10330         return val;
10331     }
10332 };/*
10333  * Based on:
10334  * Ext JS Library 1.1.1
10335  * Copyright(c) 2006-2007, Ext JS, LLC.
10336  *
10337  * Originally Released Under LGPL - original licence link has changed is not relivant.
10338  *
10339  * Fork - LGPL
10340  * <script type="text/javascript">
10341  */
10342
10343 /**
10344 * @class Roo.data.Record
10345  * Instances of this class encapsulate both record <em>definition</em> information, and record
10346  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10347  * to access Records cached in an {@link Roo.data.Store} object.<br>
10348  * <p>
10349  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10350  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10351  * objects.<br>
10352  * <p>
10353  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10354  * @constructor
10355  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10356  * {@link #create}. The parameters are the same.
10357  * @param {Array} data An associative Array of data values keyed by the field name.
10358  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10359  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10360  * not specified an integer id is generated.
10361  */
10362 Roo.data.Record = function(data, id){
10363     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10364     this.data = data;
10365 };
10366
10367 /**
10368  * Generate a constructor for a specific record layout.
10369  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10370  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10371  * Each field definition object may contain the following properties: <ul>
10372  * <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,
10373  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10374  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10375  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10376  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10377  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10378  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10379  * this may be omitted.</p></li>
10380  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10381  * <ul><li>auto (Default, implies no conversion)</li>
10382  * <li>string</li>
10383  * <li>int</li>
10384  * <li>float</li>
10385  * <li>boolean</li>
10386  * <li>date</li></ul></p></li>
10387  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10388  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10389  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10390  * by the Reader into an object that will be stored in the Record. It is passed the
10391  * following parameters:<ul>
10392  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10393  * </ul></p></li>
10394  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10395  * </ul>
10396  * <br>usage:<br><pre><code>
10397 var TopicRecord = Roo.data.Record.create(
10398     {name: 'title', mapping: 'topic_title'},
10399     {name: 'author', mapping: 'username'},
10400     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10401     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10402     {name: 'lastPoster', mapping: 'user2'},
10403     {name: 'excerpt', mapping: 'post_text'}
10404 );
10405
10406 var myNewRecord = new TopicRecord({
10407     title: 'Do my job please',
10408     author: 'noobie',
10409     totalPosts: 1,
10410     lastPost: new Date(),
10411     lastPoster: 'Animal',
10412     excerpt: 'No way dude!'
10413 });
10414 myStore.add(myNewRecord);
10415 </code></pre>
10416  * @method create
10417  * @static
10418  */
10419 Roo.data.Record.create = function(o){
10420     var f = function(){
10421         f.superclass.constructor.apply(this, arguments);
10422     };
10423     Roo.extend(f, Roo.data.Record);
10424     var p = f.prototype;
10425     p.fields = new Roo.util.MixedCollection(false, function(field){
10426         return field.name;
10427     });
10428     for(var i = 0, len = o.length; i < len; i++){
10429         p.fields.add(new Roo.data.Field(o[i]));
10430     }
10431     f.getField = function(name){
10432         return p.fields.get(name);  
10433     };
10434     return f;
10435 };
10436
10437 Roo.data.Record.AUTO_ID = 1000;
10438 Roo.data.Record.EDIT = 'edit';
10439 Roo.data.Record.REJECT = 'reject';
10440 Roo.data.Record.COMMIT = 'commit';
10441
10442 Roo.data.Record.prototype = {
10443     /**
10444      * Readonly flag - true if this record has been modified.
10445      * @type Boolean
10446      */
10447     dirty : false,
10448     editing : false,
10449     error: null,
10450     modified: null,
10451
10452     // private
10453     join : function(store){
10454         this.store = store;
10455     },
10456
10457     /**
10458      * Set the named field to the specified value.
10459      * @param {String} name The name of the field to set.
10460      * @param {Object} value The value to set the field to.
10461      */
10462     set : function(name, value){
10463         if(this.data[name] == value){
10464             return;
10465         }
10466         this.dirty = true;
10467         if(!this.modified){
10468             this.modified = {};
10469         }
10470         if(typeof this.modified[name] == 'undefined'){
10471             this.modified[name] = this.data[name];
10472         }
10473         this.data[name] = value;
10474         if(!this.editing && this.store){
10475             this.store.afterEdit(this);
10476         }       
10477     },
10478
10479     /**
10480      * Get the value of the named field.
10481      * @param {String} name The name of the field to get the value of.
10482      * @return {Object} The value of the field.
10483      */
10484     get : function(name){
10485         return this.data[name]; 
10486     },
10487
10488     // private
10489     beginEdit : function(){
10490         this.editing = true;
10491         this.modified = {}; 
10492     },
10493
10494     // private
10495     cancelEdit : function(){
10496         this.editing = false;
10497         delete this.modified;
10498     },
10499
10500     // private
10501     endEdit : function(){
10502         this.editing = false;
10503         if(this.dirty && this.store){
10504             this.store.afterEdit(this);
10505         }
10506     },
10507
10508     /**
10509      * Usually called by the {@link Roo.data.Store} which owns the Record.
10510      * Rejects all changes made to the Record since either creation, or the last commit operation.
10511      * Modified fields are reverted to their original values.
10512      * <p>
10513      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10514      * of reject operations.
10515      */
10516     reject : function(){
10517         var m = this.modified;
10518         for(var n in m){
10519             if(typeof m[n] != "function"){
10520                 this.data[n] = m[n];
10521             }
10522         }
10523         this.dirty = false;
10524         delete this.modified;
10525         this.editing = false;
10526         if(this.store){
10527             this.store.afterReject(this);
10528         }
10529     },
10530
10531     /**
10532      * Usually called by the {@link Roo.data.Store} which owns the Record.
10533      * Commits all changes made to the Record since either creation, or the last commit operation.
10534      * <p>
10535      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10536      * of commit operations.
10537      */
10538     commit : function(){
10539         this.dirty = false;
10540         delete this.modified;
10541         this.editing = false;
10542         if(this.store){
10543             this.store.afterCommit(this);
10544         }
10545     },
10546
10547     // private
10548     hasError : function(){
10549         return this.error != null;
10550     },
10551
10552     // private
10553     clearError : function(){
10554         this.error = null;
10555     },
10556
10557     /**
10558      * Creates a copy of this record.
10559      * @param {String} id (optional) A new record id if you don't want to use this record's id
10560      * @return {Record}
10561      */
10562     copy : function(newId) {
10563         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10564     }
10565 };/*
10566  * Based on:
10567  * Ext JS Library 1.1.1
10568  * Copyright(c) 2006-2007, Ext JS, LLC.
10569  *
10570  * Originally Released Under LGPL - original licence link has changed is not relivant.
10571  *
10572  * Fork - LGPL
10573  * <script type="text/javascript">
10574  */
10575
10576
10577
10578 /**
10579  * @class Roo.data.Store
10580  * @extends Roo.util.Observable
10581  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10582  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10583  * <p>
10584  * 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
10585  * has no knowledge of the format of the data returned by the Proxy.<br>
10586  * <p>
10587  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10588  * instances from the data object. These records are cached and made available through accessor functions.
10589  * @constructor
10590  * Creates a new Store.
10591  * @param {Object} config A config object containing the objects needed for the Store to access data,
10592  * and read the data into Records.
10593  */
10594 Roo.data.Store = function(config){
10595     this.data = new Roo.util.MixedCollection(false);
10596     this.data.getKey = function(o){
10597         return o.id;
10598     };
10599     this.baseParams = {};
10600     // private
10601     this.paramNames = {
10602         "start" : "start",
10603         "limit" : "limit",
10604         "sort" : "sort",
10605         "dir" : "dir",
10606         "multisort" : "_multisort"
10607     };
10608
10609     if(config && config.data){
10610         this.inlineData = config.data;
10611         delete config.data;
10612     }
10613
10614     Roo.apply(this, config);
10615     
10616     if(this.reader){ // reader passed
10617         this.reader = Roo.factory(this.reader, Roo.data);
10618         this.reader.xmodule = this.xmodule || false;
10619         if(!this.recordType){
10620             this.recordType = this.reader.recordType;
10621         }
10622         if(this.reader.onMetaChange){
10623             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10624         }
10625     }
10626
10627     if(this.recordType){
10628         this.fields = this.recordType.prototype.fields;
10629     }
10630     this.modified = [];
10631
10632     this.addEvents({
10633         /**
10634          * @event datachanged
10635          * Fires when the data cache has changed, and a widget which is using this Store
10636          * as a Record cache should refresh its view.
10637          * @param {Store} this
10638          */
10639         datachanged : true,
10640         /**
10641          * @event metachange
10642          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10643          * @param {Store} this
10644          * @param {Object} meta The JSON metadata
10645          */
10646         metachange : true,
10647         /**
10648          * @event add
10649          * Fires when Records have been added to the Store
10650          * @param {Store} this
10651          * @param {Roo.data.Record[]} records The array of Records added
10652          * @param {Number} index The index at which the record(s) were added
10653          */
10654         add : true,
10655         /**
10656          * @event remove
10657          * Fires when a Record has been removed from the Store
10658          * @param {Store} this
10659          * @param {Roo.data.Record} record The Record that was removed
10660          * @param {Number} index The index at which the record was removed
10661          */
10662         remove : true,
10663         /**
10664          * @event update
10665          * Fires when a Record has been updated
10666          * @param {Store} this
10667          * @param {Roo.data.Record} record The Record that was updated
10668          * @param {String} operation The update operation being performed.  Value may be one of:
10669          * <pre><code>
10670  Roo.data.Record.EDIT
10671  Roo.data.Record.REJECT
10672  Roo.data.Record.COMMIT
10673          * </code></pre>
10674          */
10675         update : true,
10676         /**
10677          * @event clear
10678          * Fires when the data cache has been cleared.
10679          * @param {Store} this
10680          */
10681         clear : true,
10682         /**
10683          * @event beforeload
10684          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10685          * the load action will be canceled.
10686          * @param {Store} this
10687          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10688          */
10689         beforeload : true,
10690         /**
10691          * @event beforeloadadd
10692          * Fires after a new set of Records has been loaded.
10693          * @param {Store} this
10694          * @param {Roo.data.Record[]} records The Records that were loaded
10695          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10696          */
10697         beforeloadadd : true,
10698         /**
10699          * @event load
10700          * Fires after a new set of Records has been loaded, before they are added to the store.
10701          * @param {Store} this
10702          * @param {Roo.data.Record[]} records The Records that were loaded
10703          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10704          * @params {Object} return from reader
10705          */
10706         load : true,
10707         /**
10708          * @event loadexception
10709          * Fires if an exception occurs in the Proxy during loading.
10710          * Called with the signature of the Proxy's "loadexception" event.
10711          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10712          * 
10713          * @param {Proxy} 
10714          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10715          * @param {Object} load options 
10716          * @param {Object} jsonData from your request (normally this contains the Exception)
10717          */
10718         loadexception : true
10719     });
10720     
10721     if(this.proxy){
10722         this.proxy = Roo.factory(this.proxy, Roo.data);
10723         this.proxy.xmodule = this.xmodule || false;
10724         this.relayEvents(this.proxy,  ["loadexception"]);
10725     }
10726     this.sortToggle = {};
10727     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10728
10729     Roo.data.Store.superclass.constructor.call(this);
10730
10731     if(this.inlineData){
10732         this.loadData(this.inlineData);
10733         delete this.inlineData;
10734     }
10735 };
10736
10737 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10738      /**
10739     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10740     * without a remote query - used by combo/forms at present.
10741     */
10742     
10743     /**
10744     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10745     */
10746     /**
10747     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10748     */
10749     /**
10750     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10751     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10752     */
10753     /**
10754     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10755     * on any HTTP request
10756     */
10757     /**
10758     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10759     */
10760     /**
10761     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10762     */
10763     multiSort: false,
10764     /**
10765     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10766     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10767     */
10768     remoteSort : false,
10769
10770     /**
10771     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10772      * loaded or when a record is removed. (defaults to false).
10773     */
10774     pruneModifiedRecords : false,
10775
10776     // private
10777     lastOptions : null,
10778
10779     /**
10780      * Add Records to the Store and fires the add event.
10781      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10782      */
10783     add : function(records){
10784         records = [].concat(records);
10785         for(var i = 0, len = records.length; i < len; i++){
10786             records[i].join(this);
10787         }
10788         var index = this.data.length;
10789         this.data.addAll(records);
10790         this.fireEvent("add", this, records, index);
10791     },
10792
10793     /**
10794      * Remove a Record from the Store and fires the remove event.
10795      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10796      */
10797     remove : function(record){
10798         var index = this.data.indexOf(record);
10799         this.data.removeAt(index);
10800         if(this.pruneModifiedRecords){
10801             this.modified.remove(record);
10802         }
10803         this.fireEvent("remove", this, record, index);
10804     },
10805
10806     /**
10807      * Remove all Records from the Store and fires the clear event.
10808      */
10809     removeAll : function(){
10810         this.data.clear();
10811         if(this.pruneModifiedRecords){
10812             this.modified = [];
10813         }
10814         this.fireEvent("clear", this);
10815     },
10816
10817     /**
10818      * Inserts Records to the Store at the given index and fires the add event.
10819      * @param {Number} index The start index at which to insert the passed Records.
10820      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10821      */
10822     insert : function(index, records){
10823         records = [].concat(records);
10824         for(var i = 0, len = records.length; i < len; i++){
10825             this.data.insert(index, records[i]);
10826             records[i].join(this);
10827         }
10828         this.fireEvent("add", this, records, index);
10829     },
10830
10831     /**
10832      * Get the index within the cache of the passed Record.
10833      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10834      * @return {Number} The index of the passed Record. Returns -1 if not found.
10835      */
10836     indexOf : function(record){
10837         return this.data.indexOf(record);
10838     },
10839
10840     /**
10841      * Get the index within the cache of the Record with the passed id.
10842      * @param {String} id The id of the Record to find.
10843      * @return {Number} The index of the Record. Returns -1 if not found.
10844      */
10845     indexOfId : function(id){
10846         return this.data.indexOfKey(id);
10847     },
10848
10849     /**
10850      * Get the Record with the specified id.
10851      * @param {String} id The id of the Record to find.
10852      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10853      */
10854     getById : function(id){
10855         return this.data.key(id);
10856     },
10857
10858     /**
10859      * Get the Record at the specified index.
10860      * @param {Number} index The index of the Record to find.
10861      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10862      */
10863     getAt : function(index){
10864         return this.data.itemAt(index);
10865     },
10866
10867     /**
10868      * Returns a range of Records between specified indices.
10869      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10870      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10871      * @return {Roo.data.Record[]} An array of Records
10872      */
10873     getRange : function(start, end){
10874         return this.data.getRange(start, end);
10875     },
10876
10877     // private
10878     storeOptions : function(o){
10879         o = Roo.apply({}, o);
10880         delete o.callback;
10881         delete o.scope;
10882         this.lastOptions = o;
10883     },
10884
10885     /**
10886      * Loads the Record cache from the configured Proxy using the configured Reader.
10887      * <p>
10888      * If using remote paging, then the first load call must specify the <em>start</em>
10889      * and <em>limit</em> properties in the options.params property to establish the initial
10890      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10891      * <p>
10892      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10893      * and this call will return before the new data has been loaded. Perform any post-processing
10894      * in a callback function, or in a "load" event handler.</strong>
10895      * <p>
10896      * @param {Object} options An object containing properties which control loading options:<ul>
10897      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10898      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10899      * passed the following arguments:<ul>
10900      * <li>r : Roo.data.Record[]</li>
10901      * <li>options: Options object from the load call</li>
10902      * <li>success: Boolean success indicator</li></ul></li>
10903      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10904      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10905      * </ul>
10906      */
10907     load : function(options){
10908         options = options || {};
10909         if(this.fireEvent("beforeload", this, options) !== false){
10910             this.storeOptions(options);
10911             var p = Roo.apply(options.params || {}, this.baseParams);
10912             // if meta was not loaded from remote source.. try requesting it.
10913             if (!this.reader.metaFromRemote) {
10914                 p._requestMeta = 1;
10915             }
10916             if(this.sortInfo && this.remoteSort){
10917                 var pn = this.paramNames;
10918                 p[pn["sort"]] = this.sortInfo.field;
10919                 p[pn["dir"]] = this.sortInfo.direction;
10920             }
10921             if (this.multiSort) {
10922                 var pn = this.paramNames;
10923                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10924             }
10925             
10926             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10927         }
10928     },
10929
10930     /**
10931      * Reloads the Record cache from the configured Proxy using the configured Reader and
10932      * the options from the last load operation performed.
10933      * @param {Object} options (optional) An object containing properties which may override the options
10934      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10935      * the most recently used options are reused).
10936      */
10937     reload : function(options){
10938         this.load(Roo.applyIf(options||{}, this.lastOptions));
10939     },
10940
10941     // private
10942     // Called as a callback by the Reader during a load operation.
10943     loadRecords : function(o, options, success){
10944         if(!o || success === false){
10945             if(success !== false){
10946                 this.fireEvent("load", this, [], options, o);
10947             }
10948             if(options.callback){
10949                 options.callback.call(options.scope || this, [], options, false);
10950             }
10951             return;
10952         }
10953         // if data returned failure - throw an exception.
10954         if (o.success === false) {
10955             // show a message if no listener is registered.
10956             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10957                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10958             }
10959             // loadmask wil be hooked into this..
10960             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10961             return;
10962         }
10963         var r = o.records, t = o.totalRecords || r.length;
10964         
10965         this.fireEvent("beforeloadadd", this, r, options, o);
10966         
10967         if(!options || options.add !== true){
10968             if(this.pruneModifiedRecords){
10969                 this.modified = [];
10970             }
10971             for(var i = 0, len = r.length; i < len; i++){
10972                 r[i].join(this);
10973             }
10974             if(this.snapshot){
10975                 this.data = this.snapshot;
10976                 delete this.snapshot;
10977             }
10978             this.data.clear();
10979             this.data.addAll(r);
10980             this.totalLength = t;
10981             this.applySort();
10982             this.fireEvent("datachanged", this);
10983         }else{
10984             this.totalLength = Math.max(t, this.data.length+r.length);
10985             this.add(r);
10986         }
10987         this.fireEvent("load", this, r, options, o);
10988         if(options.callback){
10989             options.callback.call(options.scope || this, r, options, true);
10990         }
10991     },
10992
10993
10994     /**
10995      * Loads data from a passed data block. A Reader which understands the format of the data
10996      * must have been configured in the constructor.
10997      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10998      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10999      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11000      */
11001     loadData : function(o, append){
11002         var r = this.reader.readRecords(o);
11003         this.loadRecords(r, {add: append}, true);
11004     },
11005
11006     /**
11007      * Gets the number of cached records.
11008      * <p>
11009      * <em>If using paging, this may not be the total size of the dataset. If the data object
11010      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11011      * the data set size</em>
11012      */
11013     getCount : function(){
11014         return this.data.length || 0;
11015     },
11016
11017     /**
11018      * Gets the total number of records in the dataset as returned by the server.
11019      * <p>
11020      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11021      * the dataset size</em>
11022      */
11023     getTotalCount : function(){
11024         return this.totalLength || 0;
11025     },
11026
11027     /**
11028      * Returns the sort state of the Store as an object with two properties:
11029      * <pre><code>
11030  field {String} The name of the field by which the Records are sorted
11031  direction {String} The sort order, "ASC" or "DESC"
11032      * </code></pre>
11033      */
11034     getSortState : function(){
11035         return this.sortInfo;
11036     },
11037
11038     // private
11039     applySort : function(){
11040         if(this.sortInfo && !this.remoteSort){
11041             var s = this.sortInfo, f = s.field;
11042             var st = this.fields.get(f).sortType;
11043             var fn = function(r1, r2){
11044                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11045                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11046             };
11047             this.data.sort(s.direction, fn);
11048             if(this.snapshot && this.snapshot != this.data){
11049                 this.snapshot.sort(s.direction, fn);
11050             }
11051         }
11052     },
11053
11054     /**
11055      * Sets the default sort column and order to be used by the next load operation.
11056      * @param {String} fieldName The name of the field to sort by.
11057      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11058      */
11059     setDefaultSort : function(field, dir){
11060         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11061     },
11062
11063     /**
11064      * Sort the Records.
11065      * If remote sorting is used, the sort is performed on the server, and the cache is
11066      * reloaded. If local sorting is used, the cache is sorted internally.
11067      * @param {String} fieldName The name of the field to sort by.
11068      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11069      */
11070     sort : function(fieldName, dir){
11071         var f = this.fields.get(fieldName);
11072         if(!dir){
11073             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11074             
11075             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11076                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11077             }else{
11078                 dir = f.sortDir;
11079             }
11080         }
11081         this.sortToggle[f.name] = dir;
11082         this.sortInfo = {field: f.name, direction: dir};
11083         if(!this.remoteSort){
11084             this.applySort();
11085             this.fireEvent("datachanged", this);
11086         }else{
11087             this.load(this.lastOptions);
11088         }
11089     },
11090
11091     /**
11092      * Calls the specified function for each of the Records in the cache.
11093      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11094      * Returning <em>false</em> aborts and exits the iteration.
11095      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11096      */
11097     each : function(fn, scope){
11098         this.data.each(fn, scope);
11099     },
11100
11101     /**
11102      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11103      * (e.g., during paging).
11104      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11105      */
11106     getModifiedRecords : function(){
11107         return this.modified;
11108     },
11109
11110     // private
11111     createFilterFn : function(property, value, anyMatch){
11112         if(!value.exec){ // not a regex
11113             value = String(value);
11114             if(value.length == 0){
11115                 return false;
11116             }
11117             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11118         }
11119         return function(r){
11120             return value.test(r.data[property]);
11121         };
11122     },
11123
11124     /**
11125      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11126      * @param {String} property A field on your records
11127      * @param {Number} start The record index to start at (defaults to 0)
11128      * @param {Number} end The last record index to include (defaults to length - 1)
11129      * @return {Number} The sum
11130      */
11131     sum : function(property, start, end){
11132         var rs = this.data.items, v = 0;
11133         start = start || 0;
11134         end = (end || end === 0) ? end : rs.length-1;
11135
11136         for(var i = start; i <= end; i++){
11137             v += (rs[i].data[property] || 0);
11138         }
11139         return v;
11140     },
11141
11142     /**
11143      * Filter the records by a specified property.
11144      * @param {String} field A field on your records
11145      * @param {String/RegExp} value Either a string that the field
11146      * should start with or a RegExp to test against the field
11147      * @param {Boolean} anyMatch True to match any part not just the beginning
11148      */
11149     filter : function(property, value, anyMatch){
11150         var fn = this.createFilterFn(property, value, anyMatch);
11151         return fn ? this.filterBy(fn) : this.clearFilter();
11152     },
11153
11154     /**
11155      * Filter by a function. The specified function will be called with each
11156      * record in this data source. If the function returns true the record is included,
11157      * otherwise it is filtered.
11158      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11159      * @param {Object} scope (optional) The scope of the function (defaults to this)
11160      */
11161     filterBy : function(fn, scope){
11162         this.snapshot = this.snapshot || this.data;
11163         this.data = this.queryBy(fn, scope||this);
11164         this.fireEvent("datachanged", this);
11165     },
11166
11167     /**
11168      * Query the records by a specified property.
11169      * @param {String} field A field on your records
11170      * @param {String/RegExp} value Either a string that the field
11171      * should start with or a RegExp to test against the field
11172      * @param {Boolean} anyMatch True to match any part not just the beginning
11173      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11174      */
11175     query : function(property, value, anyMatch){
11176         var fn = this.createFilterFn(property, value, anyMatch);
11177         return fn ? this.queryBy(fn) : this.data.clone();
11178     },
11179
11180     /**
11181      * Query by a function. The specified function will be called with each
11182      * record in this data source. If the function returns true the record is included
11183      * in the results.
11184      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11185      * @param {Object} scope (optional) The scope of the function (defaults to this)
11186       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11187      **/
11188     queryBy : function(fn, scope){
11189         var data = this.snapshot || this.data;
11190         return data.filterBy(fn, scope||this);
11191     },
11192
11193     /**
11194      * Collects unique values for a particular dataIndex from this store.
11195      * @param {String} dataIndex The property to collect
11196      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11197      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11198      * @return {Array} An array of the unique values
11199      **/
11200     collect : function(dataIndex, allowNull, bypassFilter){
11201         var d = (bypassFilter === true && this.snapshot) ?
11202                 this.snapshot.items : this.data.items;
11203         var v, sv, r = [], l = {};
11204         for(var i = 0, len = d.length; i < len; i++){
11205             v = d[i].data[dataIndex];
11206             sv = String(v);
11207             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11208                 l[sv] = true;
11209                 r[r.length] = v;
11210             }
11211         }
11212         return r;
11213     },
11214
11215     /**
11216      * Revert to a view of the Record cache with no filtering applied.
11217      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11218      */
11219     clearFilter : function(suppressEvent){
11220         if(this.snapshot && this.snapshot != this.data){
11221             this.data = this.snapshot;
11222             delete this.snapshot;
11223             if(suppressEvent !== true){
11224                 this.fireEvent("datachanged", this);
11225             }
11226         }
11227     },
11228
11229     // private
11230     afterEdit : function(record){
11231         if(this.modified.indexOf(record) == -1){
11232             this.modified.push(record);
11233         }
11234         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11235     },
11236     
11237     // private
11238     afterReject : function(record){
11239         this.modified.remove(record);
11240         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11241     },
11242
11243     // private
11244     afterCommit : function(record){
11245         this.modified.remove(record);
11246         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11247     },
11248
11249     /**
11250      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11251      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11252      */
11253     commitChanges : function(){
11254         var m = this.modified.slice(0);
11255         this.modified = [];
11256         for(var i = 0, len = m.length; i < len; i++){
11257             m[i].commit();
11258         }
11259     },
11260
11261     /**
11262      * Cancel outstanding changes on all changed records.
11263      */
11264     rejectChanges : function(){
11265         var m = this.modified.slice(0);
11266         this.modified = [];
11267         for(var i = 0, len = m.length; i < len; i++){
11268             m[i].reject();
11269         }
11270     },
11271
11272     onMetaChange : function(meta, rtype, o){
11273         this.recordType = rtype;
11274         this.fields = rtype.prototype.fields;
11275         delete this.snapshot;
11276         this.sortInfo = meta.sortInfo || this.sortInfo;
11277         this.modified = [];
11278         this.fireEvent('metachange', this, this.reader.meta);
11279     },
11280     
11281     moveIndex : function(data, type)
11282     {
11283         var index = this.indexOf(data);
11284         
11285         var newIndex = index + type;
11286         
11287         this.remove(data);
11288         
11289         this.insert(newIndex, data);
11290         
11291     }
11292 });/*
11293  * Based on:
11294  * Ext JS Library 1.1.1
11295  * Copyright(c) 2006-2007, Ext JS, LLC.
11296  *
11297  * Originally Released Under LGPL - original licence link has changed is not relivant.
11298  *
11299  * Fork - LGPL
11300  * <script type="text/javascript">
11301  */
11302
11303 /**
11304  * @class Roo.data.SimpleStore
11305  * @extends Roo.data.Store
11306  * Small helper class to make creating Stores from Array data easier.
11307  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11308  * @cfg {Array} fields An array of field definition objects, or field name strings.
11309  * @cfg {Array} data The multi-dimensional array of data
11310  * @constructor
11311  * @param {Object} config
11312  */
11313 Roo.data.SimpleStore = function(config){
11314     Roo.data.SimpleStore.superclass.constructor.call(this, {
11315         isLocal : true,
11316         reader: new Roo.data.ArrayReader({
11317                 id: config.id
11318             },
11319             Roo.data.Record.create(config.fields)
11320         ),
11321         proxy : new Roo.data.MemoryProxy(config.data)
11322     });
11323     this.load();
11324 };
11325 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11326  * Based on:
11327  * Ext JS Library 1.1.1
11328  * Copyright(c) 2006-2007, Ext JS, LLC.
11329  *
11330  * Originally Released Under LGPL - original licence link has changed is not relivant.
11331  *
11332  * Fork - LGPL
11333  * <script type="text/javascript">
11334  */
11335
11336 /**
11337 /**
11338  * @extends Roo.data.Store
11339  * @class Roo.data.JsonStore
11340  * Small helper class to make creating Stores for JSON data easier. <br/>
11341 <pre><code>
11342 var store = new Roo.data.JsonStore({
11343     url: 'get-images.php',
11344     root: 'images',
11345     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11346 });
11347 </code></pre>
11348  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11349  * JsonReader and HttpProxy (unless inline data is provided).</b>
11350  * @cfg {Array} fields An array of field definition objects, or field name strings.
11351  * @constructor
11352  * @param {Object} config
11353  */
11354 Roo.data.JsonStore = function(c){
11355     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11356         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11357         reader: new Roo.data.JsonReader(c, c.fields)
11358     }));
11359 };
11360 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11361  * Based on:
11362  * Ext JS Library 1.1.1
11363  * Copyright(c) 2006-2007, Ext JS, LLC.
11364  *
11365  * Originally Released Under LGPL - original licence link has changed is not relivant.
11366  *
11367  * Fork - LGPL
11368  * <script type="text/javascript">
11369  */
11370
11371  
11372 Roo.data.Field = function(config){
11373     if(typeof config == "string"){
11374         config = {name: config};
11375     }
11376     Roo.apply(this, config);
11377     
11378     if(!this.type){
11379         this.type = "auto";
11380     }
11381     
11382     var st = Roo.data.SortTypes;
11383     // named sortTypes are supported, here we look them up
11384     if(typeof this.sortType == "string"){
11385         this.sortType = st[this.sortType];
11386     }
11387     
11388     // set default sortType for strings and dates
11389     if(!this.sortType){
11390         switch(this.type){
11391             case "string":
11392                 this.sortType = st.asUCString;
11393                 break;
11394             case "date":
11395                 this.sortType = st.asDate;
11396                 break;
11397             default:
11398                 this.sortType = st.none;
11399         }
11400     }
11401
11402     // define once
11403     var stripRe = /[\$,%]/g;
11404
11405     // prebuilt conversion function for this field, instead of
11406     // switching every time we're reading a value
11407     if(!this.convert){
11408         var cv, dateFormat = this.dateFormat;
11409         switch(this.type){
11410             case "":
11411             case "auto":
11412             case undefined:
11413                 cv = function(v){ return v; };
11414                 break;
11415             case "string":
11416                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11417                 break;
11418             case "int":
11419                 cv = function(v){
11420                     return v !== undefined && v !== null && v !== '' ?
11421                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11422                     };
11423                 break;
11424             case "float":
11425                 cv = function(v){
11426                     return v !== undefined && v !== null && v !== '' ?
11427                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11428                     };
11429                 break;
11430             case "bool":
11431             case "boolean":
11432                 cv = function(v){ return v === true || v === "true" || v == 1; };
11433                 break;
11434             case "date":
11435                 cv = function(v){
11436                     if(!v){
11437                         return '';
11438                     }
11439                     if(v instanceof Date){
11440                         return v;
11441                     }
11442                     if(dateFormat){
11443                         if(dateFormat == "timestamp"){
11444                             return new Date(v*1000);
11445                         }
11446                         return Date.parseDate(v, dateFormat);
11447                     }
11448                     var parsed = Date.parse(v);
11449                     return parsed ? new Date(parsed) : null;
11450                 };
11451              break;
11452             
11453         }
11454         this.convert = cv;
11455     }
11456 };
11457
11458 Roo.data.Field.prototype = {
11459     dateFormat: null,
11460     defaultValue: "",
11461     mapping: null,
11462     sortType : null,
11463     sortDir : "ASC"
11464 };/*
11465  * Based on:
11466  * Ext JS Library 1.1.1
11467  * Copyright(c) 2006-2007, Ext JS, LLC.
11468  *
11469  * Originally Released Under LGPL - original licence link has changed is not relivant.
11470  *
11471  * Fork - LGPL
11472  * <script type="text/javascript">
11473  */
11474  
11475 // Base class for reading structured data from a data source.  This class is intended to be
11476 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11477
11478 /**
11479  * @class Roo.data.DataReader
11480  * Base class for reading structured data from a data source.  This class is intended to be
11481  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11482  */
11483
11484 Roo.data.DataReader = function(meta, recordType){
11485     
11486     this.meta = meta;
11487     
11488     this.recordType = recordType instanceof Array ? 
11489         Roo.data.Record.create(recordType) : recordType;
11490 };
11491
11492 Roo.data.DataReader.prototype = {
11493      /**
11494      * Create an empty record
11495      * @param {Object} data (optional) - overlay some values
11496      * @return {Roo.data.Record} record created.
11497      */
11498     newRow :  function(d) {
11499         var da =  {};
11500         this.recordType.prototype.fields.each(function(c) {
11501             switch( c.type) {
11502                 case 'int' : da[c.name] = 0; break;
11503                 case 'date' : da[c.name] = new Date(); break;
11504                 case 'float' : da[c.name] = 0.0; break;
11505                 case 'boolean' : da[c.name] = false; break;
11506                 default : da[c.name] = ""; break;
11507             }
11508             
11509         });
11510         return new this.recordType(Roo.apply(da, d));
11511     }
11512     
11513 };/*
11514  * Based on:
11515  * Ext JS Library 1.1.1
11516  * Copyright(c) 2006-2007, Ext JS, LLC.
11517  *
11518  * Originally Released Under LGPL - original licence link has changed is not relivant.
11519  *
11520  * Fork - LGPL
11521  * <script type="text/javascript">
11522  */
11523
11524 /**
11525  * @class Roo.data.DataProxy
11526  * @extends Roo.data.Observable
11527  * This class is an abstract base class for implementations which provide retrieval of
11528  * unformatted data objects.<br>
11529  * <p>
11530  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11531  * (of the appropriate type which knows how to parse the data object) to provide a block of
11532  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11533  * <p>
11534  * Custom implementations must implement the load method as described in
11535  * {@link Roo.data.HttpProxy#load}.
11536  */
11537 Roo.data.DataProxy = function(){
11538     this.addEvents({
11539         /**
11540          * @event beforeload
11541          * Fires before a network request is made to retrieve a data object.
11542          * @param {Object} This DataProxy object.
11543          * @param {Object} params The params parameter to the load function.
11544          */
11545         beforeload : true,
11546         /**
11547          * @event load
11548          * Fires before the load method's callback is called.
11549          * @param {Object} This DataProxy object.
11550          * @param {Object} o The data object.
11551          * @param {Object} arg The callback argument object passed to the load function.
11552          */
11553         load : true,
11554         /**
11555          * @event loadexception
11556          * Fires if an Exception occurs during data retrieval.
11557          * @param {Object} This DataProxy object.
11558          * @param {Object} o The data object.
11559          * @param {Object} arg The callback argument object passed to the load function.
11560          * @param {Object} e The Exception.
11561          */
11562         loadexception : true
11563     });
11564     Roo.data.DataProxy.superclass.constructor.call(this);
11565 };
11566
11567 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11568
11569     /**
11570      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11571      */
11572 /*
11573  * Based on:
11574  * Ext JS Library 1.1.1
11575  * Copyright(c) 2006-2007, Ext JS, LLC.
11576  *
11577  * Originally Released Under LGPL - original licence link has changed is not relivant.
11578  *
11579  * Fork - LGPL
11580  * <script type="text/javascript">
11581  */
11582 /**
11583  * @class Roo.data.MemoryProxy
11584  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11585  * to the Reader when its load method is called.
11586  * @constructor
11587  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11588  */
11589 Roo.data.MemoryProxy = function(data){
11590     if (data.data) {
11591         data = data.data;
11592     }
11593     Roo.data.MemoryProxy.superclass.constructor.call(this);
11594     this.data = data;
11595 };
11596
11597 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11598     
11599     /**
11600      * Load data from the requested source (in this case an in-memory
11601      * data object passed to the constructor), read the data object into
11602      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11603      * process that block using the passed callback.
11604      * @param {Object} params This parameter is not used by the MemoryProxy class.
11605      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11606      * object into a block of Roo.data.Records.
11607      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11608      * The function must be passed <ul>
11609      * <li>The Record block object</li>
11610      * <li>The "arg" argument from the load function</li>
11611      * <li>A boolean success indicator</li>
11612      * </ul>
11613      * @param {Object} scope The scope in which to call the callback
11614      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11615      */
11616     load : function(params, reader, callback, scope, arg){
11617         params = params || {};
11618         var result;
11619         try {
11620             result = reader.readRecords(this.data);
11621         }catch(e){
11622             this.fireEvent("loadexception", this, arg, null, e);
11623             callback.call(scope, null, arg, false);
11624             return;
11625         }
11626         callback.call(scope, result, arg, true);
11627     },
11628     
11629     // private
11630     update : function(params, records){
11631         
11632     }
11633 });/*
11634  * Based on:
11635  * Ext JS Library 1.1.1
11636  * Copyright(c) 2006-2007, Ext JS, LLC.
11637  *
11638  * Originally Released Under LGPL - original licence link has changed is not relivant.
11639  *
11640  * Fork - LGPL
11641  * <script type="text/javascript">
11642  */
11643 /**
11644  * @class Roo.data.HttpProxy
11645  * @extends Roo.data.DataProxy
11646  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11647  * configured to reference a certain URL.<br><br>
11648  * <p>
11649  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11650  * from which the running page was served.<br><br>
11651  * <p>
11652  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11653  * <p>
11654  * Be aware that to enable the browser to parse an XML document, the server must set
11655  * the Content-Type header in the HTTP response to "text/xml".
11656  * @constructor
11657  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11658  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11659  * will be used to make the request.
11660  */
11661 Roo.data.HttpProxy = function(conn){
11662     Roo.data.HttpProxy.superclass.constructor.call(this);
11663     // is conn a conn config or a real conn?
11664     this.conn = conn;
11665     this.useAjax = !conn || !conn.events;
11666   
11667 };
11668
11669 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11670     // thse are take from connection...
11671     
11672     /**
11673      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11674      */
11675     /**
11676      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11677      * extra parameters to each request made by this object. (defaults to undefined)
11678      */
11679     /**
11680      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11681      *  to each request made by this object. (defaults to undefined)
11682      */
11683     /**
11684      * @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)
11685      */
11686     /**
11687      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11688      */
11689      /**
11690      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11691      * @type Boolean
11692      */
11693   
11694
11695     /**
11696      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11697      * @type Boolean
11698      */
11699     /**
11700      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11701      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11702      * a finer-grained basis than the DataProxy events.
11703      */
11704     getConnection : function(){
11705         return this.useAjax ? Roo.Ajax : this.conn;
11706     },
11707
11708     /**
11709      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11710      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11711      * process that block using the passed callback.
11712      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11713      * for the request to the remote server.
11714      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11715      * object into a block of Roo.data.Records.
11716      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11717      * The function must be passed <ul>
11718      * <li>The Record block object</li>
11719      * <li>The "arg" argument from the load function</li>
11720      * <li>A boolean success indicator</li>
11721      * </ul>
11722      * @param {Object} scope The scope in which to call the callback
11723      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11724      */
11725     load : function(params, reader, callback, scope, arg){
11726         if(this.fireEvent("beforeload", this, params) !== false){
11727             var  o = {
11728                 params : params || {},
11729                 request: {
11730                     callback : callback,
11731                     scope : scope,
11732                     arg : arg
11733                 },
11734                 reader: reader,
11735                 callback : this.loadResponse,
11736                 scope: this
11737             };
11738             if(this.useAjax){
11739                 Roo.applyIf(o, this.conn);
11740                 if(this.activeRequest){
11741                     Roo.Ajax.abort(this.activeRequest);
11742                 }
11743                 this.activeRequest = Roo.Ajax.request(o);
11744             }else{
11745                 this.conn.request(o);
11746             }
11747         }else{
11748             callback.call(scope||this, null, arg, false);
11749         }
11750     },
11751
11752     // private
11753     loadResponse : function(o, success, response){
11754         delete this.activeRequest;
11755         if(!success){
11756             this.fireEvent("loadexception", this, o, response);
11757             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11758             return;
11759         }
11760         var result;
11761         try {
11762             result = o.reader.read(response);
11763         }catch(e){
11764             this.fireEvent("loadexception", this, o, response, e);
11765             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11766             return;
11767         }
11768         
11769         this.fireEvent("load", this, o, o.request.arg);
11770         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11771     },
11772
11773     // private
11774     update : function(dataSet){
11775
11776     },
11777
11778     // private
11779     updateResponse : function(dataSet){
11780
11781     }
11782 });/*
11783  * Based on:
11784  * Ext JS Library 1.1.1
11785  * Copyright(c) 2006-2007, Ext JS, LLC.
11786  *
11787  * Originally Released Under LGPL - original licence link has changed is not relivant.
11788  *
11789  * Fork - LGPL
11790  * <script type="text/javascript">
11791  */
11792
11793 /**
11794  * @class Roo.data.ScriptTagProxy
11795  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11796  * other than the originating domain of the running page.<br><br>
11797  * <p>
11798  * <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
11799  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11800  * <p>
11801  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11802  * source code that is used as the source inside a &lt;script> tag.<br><br>
11803  * <p>
11804  * In order for the browser to process the returned data, the server must wrap the data object
11805  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11806  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11807  * depending on whether the callback name was passed:
11808  * <p>
11809  * <pre><code>
11810 boolean scriptTag = false;
11811 String cb = request.getParameter("callback");
11812 if (cb != null) {
11813     scriptTag = true;
11814     response.setContentType("text/javascript");
11815 } else {
11816     response.setContentType("application/x-json");
11817 }
11818 Writer out = response.getWriter();
11819 if (scriptTag) {
11820     out.write(cb + "(");
11821 }
11822 out.print(dataBlock.toJsonString());
11823 if (scriptTag) {
11824     out.write(");");
11825 }
11826 </pre></code>
11827  *
11828  * @constructor
11829  * @param {Object} config A configuration object.
11830  */
11831 Roo.data.ScriptTagProxy = function(config){
11832     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11833     Roo.apply(this, config);
11834     this.head = document.getElementsByTagName("head")[0];
11835 };
11836
11837 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11838
11839 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11840     /**
11841      * @cfg {String} url The URL from which to request the data object.
11842      */
11843     /**
11844      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11845      */
11846     timeout : 30000,
11847     /**
11848      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11849      * the server the name of the callback function set up by the load call to process the returned data object.
11850      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11851      * javascript output which calls this named function passing the data object as its only parameter.
11852      */
11853     callbackParam : "callback",
11854     /**
11855      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11856      * name to the request.
11857      */
11858     nocache : true,
11859
11860     /**
11861      * Load data from the configured URL, read the data object into
11862      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11863      * process that block using the passed callback.
11864      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11865      * for the request to the remote server.
11866      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11867      * object into a block of Roo.data.Records.
11868      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11869      * The function must be passed <ul>
11870      * <li>The Record block object</li>
11871      * <li>The "arg" argument from the load function</li>
11872      * <li>A boolean success indicator</li>
11873      * </ul>
11874      * @param {Object} scope The scope in which to call the callback
11875      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11876      */
11877     load : function(params, reader, callback, scope, arg){
11878         if(this.fireEvent("beforeload", this, params) !== false){
11879
11880             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11881
11882             var url = this.url;
11883             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11884             if(this.nocache){
11885                 url += "&_dc=" + (new Date().getTime());
11886             }
11887             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11888             var trans = {
11889                 id : transId,
11890                 cb : "stcCallback"+transId,
11891                 scriptId : "stcScript"+transId,
11892                 params : params,
11893                 arg : arg,
11894                 url : url,
11895                 callback : callback,
11896                 scope : scope,
11897                 reader : reader
11898             };
11899             var conn = this;
11900
11901             window[trans.cb] = function(o){
11902                 conn.handleResponse(o, trans);
11903             };
11904
11905             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11906
11907             if(this.autoAbort !== false){
11908                 this.abort();
11909             }
11910
11911             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11912
11913             var script = document.createElement("script");
11914             script.setAttribute("src", url);
11915             script.setAttribute("type", "text/javascript");
11916             script.setAttribute("id", trans.scriptId);
11917             this.head.appendChild(script);
11918
11919             this.trans = trans;
11920         }else{
11921             callback.call(scope||this, null, arg, false);
11922         }
11923     },
11924
11925     // private
11926     isLoading : function(){
11927         return this.trans ? true : false;
11928     },
11929
11930     /**
11931      * Abort the current server request.
11932      */
11933     abort : function(){
11934         if(this.isLoading()){
11935             this.destroyTrans(this.trans);
11936         }
11937     },
11938
11939     // private
11940     destroyTrans : function(trans, isLoaded){
11941         this.head.removeChild(document.getElementById(trans.scriptId));
11942         clearTimeout(trans.timeoutId);
11943         if(isLoaded){
11944             window[trans.cb] = undefined;
11945             try{
11946                 delete window[trans.cb];
11947             }catch(e){}
11948         }else{
11949             // if hasn't been loaded, wait for load to remove it to prevent script error
11950             window[trans.cb] = function(){
11951                 window[trans.cb] = undefined;
11952                 try{
11953                     delete window[trans.cb];
11954                 }catch(e){}
11955             };
11956         }
11957     },
11958
11959     // private
11960     handleResponse : function(o, trans){
11961         this.trans = false;
11962         this.destroyTrans(trans, true);
11963         var result;
11964         try {
11965             result = trans.reader.readRecords(o);
11966         }catch(e){
11967             this.fireEvent("loadexception", this, o, trans.arg, e);
11968             trans.callback.call(trans.scope||window, null, trans.arg, false);
11969             return;
11970         }
11971         this.fireEvent("load", this, o, trans.arg);
11972         trans.callback.call(trans.scope||window, result, trans.arg, true);
11973     },
11974
11975     // private
11976     handleFailure : function(trans){
11977         this.trans = false;
11978         this.destroyTrans(trans, false);
11979         this.fireEvent("loadexception", this, null, trans.arg);
11980         trans.callback.call(trans.scope||window, null, trans.arg, false);
11981     }
11982 });/*
11983  * Based on:
11984  * Ext JS Library 1.1.1
11985  * Copyright(c) 2006-2007, Ext JS, LLC.
11986  *
11987  * Originally Released Under LGPL - original licence link has changed is not relivant.
11988  *
11989  * Fork - LGPL
11990  * <script type="text/javascript">
11991  */
11992
11993 /**
11994  * @class Roo.data.JsonReader
11995  * @extends Roo.data.DataReader
11996  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11997  * based on mappings in a provided Roo.data.Record constructor.
11998  * 
11999  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12000  * in the reply previously. 
12001  * 
12002  * <p>
12003  * Example code:
12004  * <pre><code>
12005 var RecordDef = Roo.data.Record.create([
12006     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12007     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12008 ]);
12009 var myReader = new Roo.data.JsonReader({
12010     totalProperty: "results",    // The property which contains the total dataset size (optional)
12011     root: "rows",                // The property which contains an Array of row objects
12012     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12013 }, RecordDef);
12014 </code></pre>
12015  * <p>
12016  * This would consume a JSON file like this:
12017  * <pre><code>
12018 { 'results': 2, 'rows': [
12019     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12020     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12021 }
12022 </code></pre>
12023  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12024  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12025  * paged from the remote server.
12026  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12027  * @cfg {String} root name of the property which contains the Array of row objects.
12028  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12029  * @cfg {Array} fields Array of field definition objects
12030  * @constructor
12031  * Create a new JsonReader
12032  * @param {Object} meta Metadata configuration options
12033  * @param {Object} recordType Either an Array of field definition objects,
12034  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12035  */
12036 Roo.data.JsonReader = function(meta, recordType){
12037     
12038     meta = meta || {};
12039     // set some defaults:
12040     Roo.applyIf(meta, {
12041         totalProperty: 'total',
12042         successProperty : 'success',
12043         root : 'data',
12044         id : 'id'
12045     });
12046     
12047     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12048 };
12049 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12050     
12051     /**
12052      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12053      * Used by Store query builder to append _requestMeta to params.
12054      * 
12055      */
12056     metaFromRemote : false,
12057     /**
12058      * This method is only used by a DataProxy which has retrieved data from a remote server.
12059      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12060      * @return {Object} data A data block which is used by an Roo.data.Store object as
12061      * a cache of Roo.data.Records.
12062      */
12063     read : function(response){
12064         var json = response.responseText;
12065        
12066         var o = /* eval:var:o */ eval("("+json+")");
12067         if(!o) {
12068             throw {message: "JsonReader.read: Json object not found"};
12069         }
12070         
12071         if(o.metaData){
12072             
12073             delete this.ef;
12074             this.metaFromRemote = true;
12075             this.meta = o.metaData;
12076             this.recordType = Roo.data.Record.create(o.metaData.fields);
12077             this.onMetaChange(this.meta, this.recordType, o);
12078         }
12079         return this.readRecords(o);
12080     },
12081
12082     // private function a store will implement
12083     onMetaChange : function(meta, recordType, o){
12084
12085     },
12086
12087     /**
12088          * @ignore
12089          */
12090     simpleAccess: function(obj, subsc) {
12091         return obj[subsc];
12092     },
12093
12094         /**
12095          * @ignore
12096          */
12097     getJsonAccessor: function(){
12098         var re = /[\[\.]/;
12099         return function(expr) {
12100             try {
12101                 return(re.test(expr))
12102                     ? new Function("obj", "return obj." + expr)
12103                     : function(obj){
12104                         return obj[expr];
12105                     };
12106             } catch(e){}
12107             return Roo.emptyFn;
12108         };
12109     }(),
12110
12111     /**
12112      * Create a data block containing Roo.data.Records from an XML document.
12113      * @param {Object} o An object which contains an Array of row objects in the property specified
12114      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12115      * which contains the total size of the dataset.
12116      * @return {Object} data A data block which is used by an Roo.data.Store object as
12117      * a cache of Roo.data.Records.
12118      */
12119     readRecords : function(o){
12120         /**
12121          * After any data loads, the raw JSON data is available for further custom processing.
12122          * @type Object
12123          */
12124         this.o = o;
12125         var s = this.meta, Record = this.recordType,
12126             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12127
12128 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12129         if (!this.ef) {
12130             if(s.totalProperty) {
12131                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12132                 }
12133                 if(s.successProperty) {
12134                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12135                 }
12136                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12137                 if (s.id) {
12138                         var g = this.getJsonAccessor(s.id);
12139                         this.getId = function(rec) {
12140                                 var r = g(rec);  
12141                                 return (r === undefined || r === "") ? null : r;
12142                         };
12143                 } else {
12144                         this.getId = function(){return null;};
12145                 }
12146             this.ef = [];
12147             for(var jj = 0; jj < fl; jj++){
12148                 f = fi[jj];
12149                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12150                 this.ef[jj] = this.getJsonAccessor(map);
12151             }
12152         }
12153
12154         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12155         if(s.totalProperty){
12156             var vt = parseInt(this.getTotal(o), 10);
12157             if(!isNaN(vt)){
12158                 totalRecords = vt;
12159             }
12160         }
12161         if(s.successProperty){
12162             var vs = this.getSuccess(o);
12163             if(vs === false || vs === 'false'){
12164                 success = false;
12165             }
12166         }
12167         var records = [];
12168         for(var i = 0; i < c; i++){
12169                 var n = root[i];
12170             var values = {};
12171             var id = this.getId(n);
12172             for(var j = 0; j < fl; j++){
12173                 f = fi[j];
12174             var v = this.ef[j](n);
12175             if (!f.convert) {
12176                 Roo.log('missing convert for ' + f.name);
12177                 Roo.log(f);
12178                 continue;
12179             }
12180             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12181             }
12182             var record = new Record(values, id);
12183             record.json = n;
12184             records[i] = record;
12185         }
12186         return {
12187             raw : o,
12188             success : success,
12189             records : records,
12190             totalRecords : totalRecords
12191         };
12192     }
12193 });/*
12194  * Based on:
12195  * Ext JS Library 1.1.1
12196  * Copyright(c) 2006-2007, Ext JS, LLC.
12197  *
12198  * Originally Released Under LGPL - original licence link has changed is not relivant.
12199  *
12200  * Fork - LGPL
12201  * <script type="text/javascript">
12202  */
12203
12204 /**
12205  * @class Roo.data.ArrayReader
12206  * @extends Roo.data.DataReader
12207  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12208  * Each element of that Array represents a row of data fields. The
12209  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12210  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12211  * <p>
12212  * Example code:.
12213  * <pre><code>
12214 var RecordDef = Roo.data.Record.create([
12215     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12216     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12217 ]);
12218 var myReader = new Roo.data.ArrayReader({
12219     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12220 }, RecordDef);
12221 </code></pre>
12222  * <p>
12223  * This would consume an Array like this:
12224  * <pre><code>
12225 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12226   </code></pre>
12227  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12228  * @constructor
12229  * Create a new JsonReader
12230  * @param {Object} meta Metadata configuration options.
12231  * @param {Object} recordType Either an Array of field definition objects
12232  * as specified to {@link Roo.data.Record#create},
12233  * or an {@link Roo.data.Record} object
12234  * created using {@link Roo.data.Record#create}.
12235  */
12236 Roo.data.ArrayReader = function(meta, recordType){
12237     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12238 };
12239
12240 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12241     /**
12242      * Create a data block containing Roo.data.Records from an XML document.
12243      * @param {Object} o An Array of row objects which represents the dataset.
12244      * @return {Object} data A data block which is used by an Roo.data.Store object as
12245      * a cache of Roo.data.Records.
12246      */
12247     readRecords : function(o){
12248         var sid = this.meta ? this.meta.id : null;
12249         var recordType = this.recordType, fields = recordType.prototype.fields;
12250         var records = [];
12251         var root = o;
12252             for(var i = 0; i < root.length; i++){
12253                     var n = root[i];
12254                 var values = {};
12255                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12256                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12257                 var f = fields.items[j];
12258                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12259                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12260                 v = f.convert(v);
12261                 values[f.name] = v;
12262             }
12263                 var record = new recordType(values, id);
12264                 record.json = n;
12265                 records[records.length] = record;
12266             }
12267             return {
12268                 records : records,
12269                 totalRecords : records.length
12270             };
12271     }
12272 });/*
12273  * - LGPL
12274  * * 
12275  */
12276
12277 /**
12278  * @class Roo.bootstrap.ComboBox
12279  * @extends Roo.bootstrap.TriggerField
12280  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12281  * @cfg {Boolean} append (true|false) default false
12282  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12283  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12284  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12285  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12286  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12287  * @cfg {Boolean} animate default true
12288  * @cfg {Boolean} emptyResultText only for touch device
12289  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12290  * @constructor
12291  * Create a new ComboBox.
12292  * @param {Object} config Configuration options
12293  */
12294 Roo.bootstrap.ComboBox = function(config){
12295     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12296     this.addEvents({
12297         /**
12298          * @event expand
12299          * Fires when the dropdown list is expanded
12300              * @param {Roo.bootstrap.ComboBox} combo This combo box
12301              */
12302         'expand' : true,
12303         /**
12304          * @event collapse
12305          * Fires when the dropdown list is collapsed
12306              * @param {Roo.bootstrap.ComboBox} combo This combo box
12307              */
12308         'collapse' : true,
12309         /**
12310          * @event beforeselect
12311          * Fires before a list item is selected. Return false to cancel the selection.
12312              * @param {Roo.bootstrap.ComboBox} combo This combo box
12313              * @param {Roo.data.Record} record The data record returned from the underlying store
12314              * @param {Number} index The index of the selected item in the dropdown list
12315              */
12316         'beforeselect' : true,
12317         /**
12318          * @event select
12319          * Fires when a list item is selected
12320              * @param {Roo.bootstrap.ComboBox} combo This combo box
12321              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12322              * @param {Number} index The index of the selected item in the dropdown list
12323              */
12324         'select' : true,
12325         /**
12326          * @event beforequery
12327          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12328          * The event object passed has these properties:
12329              * @param {Roo.bootstrap.ComboBox} combo This combo box
12330              * @param {String} query The query
12331              * @param {Boolean} forceAll true to force "all" query
12332              * @param {Boolean} cancel true to cancel the query
12333              * @param {Object} e The query event object
12334              */
12335         'beforequery': true,
12336          /**
12337          * @event add
12338          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12339              * @param {Roo.bootstrap.ComboBox} combo This combo box
12340              */
12341         'add' : true,
12342         /**
12343          * @event edit
12344          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12345              * @param {Roo.bootstrap.ComboBox} combo This combo box
12346              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12347              */
12348         'edit' : true,
12349         /**
12350          * @event remove
12351          * Fires when the remove value from the combobox array
12352              * @param {Roo.bootstrap.ComboBox} combo This combo box
12353              */
12354         'remove' : true,
12355         /**
12356          * @event afterremove
12357          * Fires when the remove value from the combobox array
12358              * @param {Roo.bootstrap.ComboBox} combo This combo box
12359              */
12360         'afterremove' : true,
12361         /**
12362          * @event specialfilter
12363          * Fires when specialfilter
12364             * @param {Roo.bootstrap.ComboBox} combo This combo box
12365             */
12366         'specialfilter' : true,
12367         /**
12368          * @event tick
12369          * Fires when tick the element
12370             * @param {Roo.bootstrap.ComboBox} combo This combo box
12371             */
12372         'tick' : true,
12373         /**
12374          * @event touchviewdisplay
12375          * Fires when touch view require special display (default is using displayField)
12376             * @param {Roo.bootstrap.ComboBox} combo This combo box
12377             * @param {Object} cfg set html .
12378             */
12379         'touchviewdisplay' : true
12380         
12381     });
12382     
12383     this.item = [];
12384     this.tickItems = [];
12385     
12386     this.selectedIndex = -1;
12387     if(this.mode == 'local'){
12388         if(config.queryDelay === undefined){
12389             this.queryDelay = 10;
12390         }
12391         if(config.minChars === undefined){
12392             this.minChars = 0;
12393         }
12394     }
12395 };
12396
12397 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12398      
12399     /**
12400      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12401      * rendering into an Roo.Editor, defaults to false)
12402      */
12403     /**
12404      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12405      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12406      */
12407     /**
12408      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12409      */
12410     /**
12411      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12412      * the dropdown list (defaults to undefined, with no header element)
12413      */
12414
12415      /**
12416      * @cfg {String/Roo.Template} tpl The template to use to render the output
12417      */
12418      
12419      /**
12420      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12421      */
12422     listWidth: undefined,
12423     /**
12424      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12425      * mode = 'remote' or 'text' if mode = 'local')
12426      */
12427     displayField: undefined,
12428     
12429     /**
12430      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12431      * mode = 'remote' or 'value' if mode = 'local'). 
12432      * Note: use of a valueField requires the user make a selection
12433      * in order for a value to be mapped.
12434      */
12435     valueField: undefined,
12436     /**
12437      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12438      */
12439     modalTitle : '',
12440     
12441     /**
12442      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12443      * field's data value (defaults to the underlying DOM element's name)
12444      */
12445     hiddenName: undefined,
12446     /**
12447      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12448      */
12449     listClass: '',
12450     /**
12451      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12452      */
12453     selectedClass: 'active',
12454     
12455     /**
12456      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12457      */
12458     shadow:'sides',
12459     /**
12460      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12461      * anchor positions (defaults to 'tl-bl')
12462      */
12463     listAlign: 'tl-bl?',
12464     /**
12465      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12466      */
12467     maxHeight: 300,
12468     /**
12469      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12470      * query specified by the allQuery config option (defaults to 'query')
12471      */
12472     triggerAction: 'query',
12473     /**
12474      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12475      * (defaults to 4, does not apply if editable = false)
12476      */
12477     minChars : 4,
12478     /**
12479      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12480      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12481      */
12482     typeAhead: false,
12483     /**
12484      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12485      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12486      */
12487     queryDelay: 500,
12488     /**
12489      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12490      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12491      */
12492     pageSize: 0,
12493     /**
12494      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12495      * when editable = true (defaults to false)
12496      */
12497     selectOnFocus:false,
12498     /**
12499      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12500      */
12501     queryParam: 'query',
12502     /**
12503      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12504      * when mode = 'remote' (defaults to 'Loading...')
12505      */
12506     loadingText: 'Loading...',
12507     /**
12508      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12509      */
12510     resizable: false,
12511     /**
12512      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12513      */
12514     handleHeight : 8,
12515     /**
12516      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12517      * traditional select (defaults to true)
12518      */
12519     editable: true,
12520     /**
12521      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12522      */
12523     allQuery: '',
12524     /**
12525      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12526      */
12527     mode: 'remote',
12528     /**
12529      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12530      * listWidth has a higher value)
12531      */
12532     minListWidth : 70,
12533     /**
12534      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12535      * allow the user to set arbitrary text into the field (defaults to false)
12536      */
12537     forceSelection:false,
12538     /**
12539      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12540      * if typeAhead = true (defaults to 250)
12541      */
12542     typeAheadDelay : 250,
12543     /**
12544      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12545      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12546      */
12547     valueNotFoundText : undefined,
12548     /**
12549      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12550      */
12551     blockFocus : false,
12552     
12553     /**
12554      * @cfg {Boolean} disableClear Disable showing of clear button.
12555      */
12556     disableClear : false,
12557     /**
12558      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12559      */
12560     alwaysQuery : false,
12561     
12562     /**
12563      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12564      */
12565     multiple : false,
12566     
12567     /**
12568      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12569      */
12570     invalidClass : "has-warning",
12571     
12572     /**
12573      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12574      */
12575     validClass : "has-success",
12576     
12577     /**
12578      * @cfg {Boolean} specialFilter (true|false) special filter default false
12579      */
12580     specialFilter : false,
12581     
12582     /**
12583      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12584      */
12585     mobileTouchView : true,
12586     
12587     /**
12588      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12589      */
12590     useNativeIOS : false,
12591     
12592     ios_options : false,
12593     
12594     //private
12595     addicon : false,
12596     editicon: false,
12597     
12598     page: 0,
12599     hasQuery: false,
12600     append: false,
12601     loadNext: false,
12602     autoFocus : true,
12603     tickable : false,
12604     btnPosition : 'right',
12605     triggerList : true,
12606     showToggleBtn : true,
12607     animate : true,
12608     emptyResultText: 'Empty',
12609     triggerText : 'Select',
12610     
12611     // element that contains real text value.. (when hidden is used..)
12612     
12613     getAutoCreate : function()
12614     {   
12615         var cfg = false;
12616         //render
12617         /*
12618          * Render classic select for iso
12619          */
12620         
12621         if(Roo.isIOS && this.useNativeIOS){
12622             cfg = this.getAutoCreateNativeIOS();
12623             return cfg;
12624         }
12625         
12626         /*
12627          * Touch Devices
12628          */
12629         
12630         if(Roo.isTouch && this.mobileTouchView){
12631             cfg = this.getAutoCreateTouchView();
12632             return cfg;;
12633         }
12634         
12635         /*
12636          *  Normal ComboBox
12637          */
12638         if(!this.tickable){
12639             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12640             if(this.name == 'info_year_invest_id_display_name'){
12641                 Roo.log('cfg.................................................');
12642                 Roo.log(cfg);
12643             }
12644             return cfg;
12645         }
12646         
12647         /*
12648          *  ComboBox with tickable selections
12649          */
12650              
12651         var align = this.labelAlign || this.parentLabelAlign();
12652         
12653         cfg = {
12654             cls : 'form-group roo-combobox-tickable' //input-group
12655         };
12656         
12657         var btn_text_select = '';
12658         var btn_text_done = '';
12659         var btn_text_cancel = '';
12660         
12661         if (this.btn_text_show) {
12662             btn_text_select = 'Select';
12663             btn_text_done = 'Done';
12664             btn_text_cancel = 'Cancel'; 
12665         }
12666         
12667         var buttons = {
12668             tag : 'div',
12669             cls : 'tickable-buttons',
12670             cn : [
12671                 {
12672                     tag : 'button',
12673                     type : 'button',
12674                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12675                     //html : this.triggerText
12676                     html: btn_text_select
12677                 },
12678                 {
12679                     tag : 'button',
12680                     type : 'button',
12681                     name : 'ok',
12682                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12683                     //html : 'Done'
12684                     html: btn_text_done
12685                 },
12686                 {
12687                     tag : 'button',
12688                     type : 'button',
12689                     name : 'cancel',
12690                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12691                     //html : 'Cancel'
12692                     html: btn_text_cancel
12693                 }
12694             ]
12695         };
12696         
12697         if(this.editable){
12698             buttons.cn.unshift({
12699                 tag: 'input',
12700                 cls: 'roo-select2-search-field-input'
12701             });
12702         }
12703         
12704         var _this = this;
12705         
12706         Roo.each(buttons.cn, function(c){
12707             if (_this.size) {
12708                 c.cls += ' btn-' + _this.size;
12709             }
12710
12711             if (_this.disabled) {
12712                 c.disabled = true;
12713             }
12714         });
12715         
12716         var box = {
12717             tag: 'div',
12718             cn: [
12719                 {
12720                     tag: 'input',
12721                     type : 'hidden',
12722                     cls: 'form-hidden-field'
12723                 },
12724                 {
12725                     tag: 'ul',
12726                     cls: 'roo-select2-choices',
12727                     cn:[
12728                         {
12729                             tag: 'li',
12730                             cls: 'roo-select2-search-field',
12731                             cn: [
12732                                 buttons
12733                             ]
12734                         }
12735                     ]
12736                 }
12737             ]
12738         };
12739         
12740         var combobox = {
12741             cls: 'roo-select2-container input-group roo-select2-container-multi',
12742             cn: [
12743                 box
12744 //                {
12745 //                    tag: 'ul',
12746 //                    cls: 'typeahead typeahead-long dropdown-menu',
12747 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12748 //                }
12749             ]
12750         };
12751         
12752         if(this.hasFeedback && !this.allowBlank){
12753             
12754             var feedback = {
12755                 tag: 'span',
12756                 cls: 'glyphicon form-control-feedback'
12757             };
12758
12759             combobox.cn.push(feedback);
12760         }
12761         
12762         
12763         if (align ==='left' && this.fieldLabel.length) {
12764             
12765             cfg.cls += ' roo-form-group-label-left';
12766             
12767             cfg.cn = [
12768                 {
12769                     tag : 'i',
12770                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12771                     tooltip : 'This field is required'
12772                 },
12773                 {
12774                     tag: 'label',
12775                     'for' :  id,
12776                     cls : 'control-label',
12777                     html : this.fieldLabel
12778
12779                 },
12780                 {
12781                     cls : "", 
12782                     cn: [
12783                         combobox
12784                     ]
12785                 }
12786
12787             ];
12788             
12789             var labelCfg = cfg.cn[1];
12790             var contentCfg = cfg.cn[2];
12791             
12792
12793             if(this.indicatorpos == 'right'){
12794                 
12795                 cfg.cn = [
12796                     {
12797                         tag: 'label',
12798                         'for' :  id,
12799                         cls : 'control-label',
12800                         cn : [
12801                             {
12802                                 tag : 'span',
12803                                 html : this.fieldLabel
12804                             },
12805                             {
12806                                 tag : 'i',
12807                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12808                                 tooltip : 'This field is required'
12809                             }
12810                         ]
12811                     },
12812                     {
12813                         cls : "",
12814                         cn: [
12815                             combobox
12816                         ]
12817                     }
12818
12819                 ];
12820                 
12821                 
12822                 
12823                 labelCfg = cfg.cn[0];
12824                 contentCfg = cfg.cn[1];
12825             
12826             }
12827             
12828             if(this.labelWidth > 12){
12829                 labelCfg.style = "width: " + this.labelWidth + 'px';
12830             }
12831             
12832             if(this.labelWidth < 13 && this.labelmd == 0){
12833                 this.labelmd = this.labelWidth;
12834             }
12835             
12836             if(this.labellg > 0){
12837                 labelCfg.cls += ' col-lg-' + this.labellg;
12838                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12839             }
12840             
12841             if(this.labelmd > 0){
12842                 labelCfg.cls += ' col-md-' + this.labelmd;
12843                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12844             }
12845             
12846             if(this.labelsm > 0){
12847                 labelCfg.cls += ' col-sm-' + this.labelsm;
12848                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12849             }
12850             
12851             if(this.labelxs > 0){
12852                 labelCfg.cls += ' col-xs-' + this.labelxs;
12853                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12854             }
12855                 
12856                 
12857         } else if ( this.fieldLabel.length) {
12858 //                Roo.log(" label");
12859                  cfg.cn = [
12860                     {
12861                         tag : 'i',
12862                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12863                         tooltip : 'This field is required'
12864                     },
12865                     {
12866                         tag: 'label',
12867                         //cls : 'input-group-addon',
12868                         html : this.fieldLabel
12869                         
12870                     },
12871                     
12872                     combobox
12873                     
12874                 ];
12875                 
12876                 if(this.indicatorpos == 'right'){
12877                     
12878                     cfg.cn = [
12879                         {
12880                             tag: 'label',
12881                             //cls : 'input-group-addon',
12882                             cn : [
12883                                 {
12884                                     tag : 'span',
12885                                     html : this.fieldLabel
12886                                 },
12887                                 {
12888                                     tag : 'i',
12889                                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12890                                     tooltip : 'This field is required'
12891                                 }
12892                             ]
12893                         },
12894                         
12895                         
12896                         
12897                         combobox
12898
12899                     ];
12900                 
12901                 }
12902
12903         } else {
12904             
12905 //                Roo.log(" no label && no align");
12906                 cfg = combobox
12907                      
12908                 
12909         }
12910          
12911         var settings=this;
12912         ['xs','sm','md','lg'].map(function(size){
12913             if (settings[size]) {
12914                 cfg.cls += ' col-' + size + '-' + settings[size];
12915             }
12916         });
12917         
12918         return cfg;
12919         
12920     },
12921     
12922     _initEventsCalled : false,
12923     
12924     // private
12925     initEvents: function()
12926     {   
12927         if (this._initEventsCalled) { // as we call render... prevent looping...
12928             return;
12929         }
12930         this._initEventsCalled = true;
12931         
12932         if (!this.store) {
12933             throw "can not find store for combo";
12934         }
12935         
12936         this.store = Roo.factory(this.store, Roo.data);
12937         
12938         // if we are building from html. then this element is so complex, that we can not really
12939         // use the rendered HTML.
12940         // so we have to trash and replace the previous code.
12941         if (Roo.XComponent.build_from_html) {
12942             
12943             // remove this element....
12944             var e = this.el.dom, k=0;
12945             while (e ) { e = e.previousSibling;  ++k;}
12946
12947             this.el.remove();
12948             
12949             this.el=false;
12950             this.rendered = false;
12951             
12952             this.render(this.parent().getChildContainer(true), k);
12953             
12954             
12955             
12956         }
12957         
12958         if(Roo.isIOS && this.useNativeIOS){
12959             this.initIOSView();
12960             return;
12961         }
12962         
12963         /*
12964          * Touch Devices
12965          */
12966         
12967         if(Roo.isTouch && this.mobileTouchView){
12968             this.initTouchView();
12969             return;
12970         }
12971         
12972         if(this.tickable){
12973             this.initTickableEvents();
12974             return;
12975         }
12976         
12977         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12978         
12979         if(this.hiddenName){
12980             
12981             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12982             
12983             this.hiddenField.dom.value =
12984                 this.hiddenValue !== undefined ? this.hiddenValue :
12985                 this.value !== undefined ? this.value : '';
12986
12987             // prevent input submission
12988             this.el.dom.removeAttribute('name');
12989             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12990              
12991              
12992         }
12993         //if(Roo.isGecko){
12994         //    this.el.dom.setAttribute('autocomplete', 'off');
12995         //}
12996         
12997         var cls = 'x-combo-list';
12998         
12999         //this.list = new Roo.Layer({
13000         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13001         //});
13002         
13003         var _this = this;
13004         
13005         (function(){
13006             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13007             _this.list.setWidth(lw);
13008         }).defer(100);
13009         
13010         this.list.on('mouseover', this.onViewOver, this);
13011         this.list.on('mousemove', this.onViewMove, this);
13012         
13013         this.list.on('scroll', this.onViewScroll, this);
13014         
13015         /*
13016         this.list.swallowEvent('mousewheel');
13017         this.assetHeight = 0;
13018
13019         if(this.title){
13020             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13021             this.assetHeight += this.header.getHeight();
13022         }
13023
13024         this.innerList = this.list.createChild({cls:cls+'-inner'});
13025         this.innerList.on('mouseover', this.onViewOver, this);
13026         this.innerList.on('mousemove', this.onViewMove, this);
13027         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13028         
13029         if(this.allowBlank && !this.pageSize && !this.disableClear){
13030             this.footer = this.list.createChild({cls:cls+'-ft'});
13031             this.pageTb = new Roo.Toolbar(this.footer);
13032            
13033         }
13034         if(this.pageSize){
13035             this.footer = this.list.createChild({cls:cls+'-ft'});
13036             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13037                     {pageSize: this.pageSize});
13038             
13039         }
13040         
13041         if (this.pageTb && this.allowBlank && !this.disableClear) {
13042             var _this = this;
13043             this.pageTb.add(new Roo.Toolbar.Fill(), {
13044                 cls: 'x-btn-icon x-btn-clear',
13045                 text: '&#160;',
13046                 handler: function()
13047                 {
13048                     _this.collapse();
13049                     _this.clearValue();
13050                     _this.onSelect(false, -1);
13051                 }
13052             });
13053         }
13054         if (this.footer) {
13055             this.assetHeight += this.footer.getHeight();
13056         }
13057         */
13058             
13059         if(!this.tpl){
13060             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13061         }
13062
13063         this.view = new Roo.View(this.list, this.tpl, {
13064             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13065         });
13066         //this.view.wrapEl.setDisplayed(false);
13067         this.view.on('click', this.onViewClick, this);
13068         
13069         
13070         
13071         this.store.on('beforeload', this.onBeforeLoad, this);
13072         this.store.on('load', this.onLoad, this);
13073         this.store.on('loadexception', this.onLoadException, this);
13074         /*
13075         if(this.resizable){
13076             this.resizer = new Roo.Resizable(this.list,  {
13077                pinned:true, handles:'se'
13078             });
13079             this.resizer.on('resize', function(r, w, h){
13080                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13081                 this.listWidth = w;
13082                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13083                 this.restrictHeight();
13084             }, this);
13085             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13086         }
13087         */
13088         if(!this.editable){
13089             this.editable = true;
13090             this.setEditable(false);
13091         }
13092         
13093         /*
13094         
13095         if (typeof(this.events.add.listeners) != 'undefined') {
13096             
13097             this.addicon = this.wrap.createChild(
13098                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13099        
13100             this.addicon.on('click', function(e) {
13101                 this.fireEvent('add', this);
13102             }, this);
13103         }
13104         if (typeof(this.events.edit.listeners) != 'undefined') {
13105             
13106             this.editicon = this.wrap.createChild(
13107                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13108             if (this.addicon) {
13109                 this.editicon.setStyle('margin-left', '40px');
13110             }
13111             this.editicon.on('click', function(e) {
13112                 
13113                 // we fire even  if inothing is selected..
13114                 this.fireEvent('edit', this, this.lastData );
13115                 
13116             }, this);
13117         }
13118         */
13119         
13120         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13121             "up" : function(e){
13122                 this.inKeyMode = true;
13123                 this.selectPrev();
13124             },
13125
13126             "down" : function(e){
13127                 if(!this.isExpanded()){
13128                     this.onTriggerClick();
13129                 }else{
13130                     this.inKeyMode = true;
13131                     this.selectNext();
13132                 }
13133             },
13134
13135             "enter" : function(e){
13136 //                this.onViewClick();
13137                 //return true;
13138                 this.collapse();
13139                 
13140                 if(this.fireEvent("specialkey", this, e)){
13141                     this.onViewClick(false);
13142                 }
13143                 
13144                 return true;
13145             },
13146
13147             "esc" : function(e){
13148                 this.collapse();
13149             },
13150
13151             "tab" : function(e){
13152                 this.collapse();
13153                 
13154                 if(this.fireEvent("specialkey", this, e)){
13155                     this.onViewClick(false);
13156                 }
13157                 
13158                 return true;
13159             },
13160
13161             scope : this,
13162
13163             doRelay : function(foo, bar, hname){
13164                 if(hname == 'down' || this.scope.isExpanded()){
13165                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13166                 }
13167                 return true;
13168             },
13169
13170             forceKeyDown: true
13171         });
13172         
13173         
13174         this.queryDelay = Math.max(this.queryDelay || 10,
13175                 this.mode == 'local' ? 10 : 250);
13176         
13177         
13178         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13179         
13180         if(this.typeAhead){
13181             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13182         }
13183         if(this.editable !== false){
13184             this.inputEl().on("keyup", this.onKeyUp, this);
13185         }
13186         if(this.forceSelection){
13187             this.inputEl().on('blur', this.doForce, this);
13188         }
13189         
13190         if(this.multiple){
13191             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13192             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13193         }
13194     },
13195     
13196     initTickableEvents: function()
13197     {   
13198         this.createList();
13199         
13200         if(this.hiddenName){
13201             
13202             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13203             
13204             this.hiddenField.dom.value =
13205                 this.hiddenValue !== undefined ? this.hiddenValue :
13206                 this.value !== undefined ? this.value : '';
13207
13208             // prevent input submission
13209             this.el.dom.removeAttribute('name');
13210             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13211              
13212              
13213         }
13214         
13215 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13216         
13217         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13218         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13219         if(this.triggerList){
13220             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13221         }
13222          
13223         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13224         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13225         
13226         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13227         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13228         
13229         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13230         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13231         
13232         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13233         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13234         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13235         
13236         this.okBtn.hide();
13237         this.cancelBtn.hide();
13238         
13239         var _this = this;
13240         
13241         (function(){
13242             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13243             _this.list.setWidth(lw);
13244         }).defer(100);
13245         
13246         this.list.on('mouseover', this.onViewOver, this);
13247         this.list.on('mousemove', this.onViewMove, this);
13248         
13249         this.list.on('scroll', this.onViewScroll, this);
13250         
13251         if(!this.tpl){
13252             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>';
13253         }
13254
13255         this.view = new Roo.View(this.list, this.tpl, {
13256             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13257         });
13258         
13259         //this.view.wrapEl.setDisplayed(false);
13260         this.view.on('click', this.onViewClick, this);
13261         
13262         
13263         
13264         this.store.on('beforeload', this.onBeforeLoad, this);
13265         this.store.on('load', this.onLoad, this);
13266         this.store.on('loadexception', this.onLoadException, this);
13267         
13268         if(this.editable){
13269             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13270                 "up" : function(e){
13271                     this.inKeyMode = true;
13272                     this.selectPrev();
13273                 },
13274
13275                 "down" : function(e){
13276                     this.inKeyMode = true;
13277                     this.selectNext();
13278                 },
13279
13280                 "enter" : function(e){
13281                     if(this.fireEvent("specialkey", this, e)){
13282                         this.onViewClick(false);
13283                     }
13284                     
13285                     return true;
13286                 },
13287
13288                 "esc" : function(e){
13289                     this.onTickableFooterButtonClick(e, false, false);
13290                 },
13291
13292                 "tab" : function(e){
13293                     this.fireEvent("specialkey", this, e);
13294                     
13295                     this.onTickableFooterButtonClick(e, false, false);
13296                     
13297                     return true;
13298                 },
13299
13300                 scope : this,
13301
13302                 doRelay : function(e, fn, key){
13303                     if(this.scope.isExpanded()){
13304                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13305                     }
13306                     return true;
13307                 },
13308
13309                 forceKeyDown: true
13310             });
13311         }
13312         
13313         this.queryDelay = Math.max(this.queryDelay || 10,
13314                 this.mode == 'local' ? 10 : 250);
13315         
13316         
13317         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13318         
13319         if(this.typeAhead){
13320             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13321         }
13322         
13323         if(this.editable !== false){
13324             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13325         }
13326         
13327         this.indicator = this.indicatorEl();
13328         
13329         if(this.indicator){
13330             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13331             this.indicator.hide();
13332         }
13333         
13334     },
13335
13336     onDestroy : function(){
13337         if(this.view){
13338             this.view.setStore(null);
13339             this.view.el.removeAllListeners();
13340             this.view.el.remove();
13341             this.view.purgeListeners();
13342         }
13343         if(this.list){
13344             this.list.dom.innerHTML  = '';
13345         }
13346         
13347         if(this.store){
13348             this.store.un('beforeload', this.onBeforeLoad, this);
13349             this.store.un('load', this.onLoad, this);
13350             this.store.un('loadexception', this.onLoadException, this);
13351         }
13352         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13353     },
13354
13355     // private
13356     fireKey : function(e){
13357         if(e.isNavKeyPress() && !this.list.isVisible()){
13358             this.fireEvent("specialkey", this, e);
13359         }
13360     },
13361
13362     // private
13363     onResize: function(w, h){
13364 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13365 //        
13366 //        if(typeof w != 'number'){
13367 //            // we do not handle it!?!?
13368 //            return;
13369 //        }
13370 //        var tw = this.trigger.getWidth();
13371 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13372 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13373 //        var x = w - tw;
13374 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13375 //            
13376 //        //this.trigger.setStyle('left', x+'px');
13377 //        
13378 //        if(this.list && this.listWidth === undefined){
13379 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13380 //            this.list.setWidth(lw);
13381 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13382 //        }
13383         
13384     
13385         
13386     },
13387
13388     /**
13389      * Allow or prevent the user from directly editing the field text.  If false is passed,
13390      * the user will only be able to select from the items defined in the dropdown list.  This method
13391      * is the runtime equivalent of setting the 'editable' config option at config time.
13392      * @param {Boolean} value True to allow the user to directly edit the field text
13393      */
13394     setEditable : function(value){
13395         if(value == this.editable){
13396             return;
13397         }
13398         this.editable = value;
13399         if(!value){
13400             this.inputEl().dom.setAttribute('readOnly', true);
13401             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13402             this.inputEl().addClass('x-combo-noedit');
13403         }else{
13404             this.inputEl().dom.setAttribute('readOnly', false);
13405             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13406             this.inputEl().removeClass('x-combo-noedit');
13407         }
13408     },
13409
13410     // private
13411     
13412     onBeforeLoad : function(combo,opts){
13413         if(!this.hasFocus){
13414             return;
13415         }
13416          if (!opts.add) {
13417             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13418          }
13419         this.restrictHeight();
13420         this.selectedIndex = -1;
13421     },
13422
13423     // private
13424     onLoad : function(){
13425         
13426         this.hasQuery = false;
13427         
13428         if(!this.hasFocus){
13429             return;
13430         }
13431         
13432         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13433             this.loading.hide();
13434         }
13435              
13436         if(this.store.getCount() > 0){
13437             this.expand();
13438             this.restrictHeight();
13439             if(this.lastQuery == this.allQuery){
13440                 if(this.editable && !this.tickable){
13441                     this.inputEl().dom.select();
13442                 }
13443                 
13444                 if(
13445                     !this.selectByValue(this.value, true) &&
13446                     this.autoFocus && 
13447                     (
13448                         !this.store.lastOptions ||
13449                         typeof(this.store.lastOptions.add) == 'undefined' || 
13450                         this.store.lastOptions.add != true
13451                     )
13452                 ){
13453                     this.select(0, true);
13454                 }
13455             }else{
13456                 if(this.autoFocus){
13457                     this.selectNext();
13458                 }
13459                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13460                     this.taTask.delay(this.typeAheadDelay);
13461                 }
13462             }
13463         }else{
13464             this.onEmptyResults();
13465         }
13466         
13467         //this.el.focus();
13468     },
13469     // private
13470     onLoadException : function()
13471     {
13472         this.hasQuery = false;
13473         
13474         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13475             this.loading.hide();
13476         }
13477         
13478         if(this.tickable && this.editable){
13479             return;
13480         }
13481         
13482         this.collapse();
13483         // only causes errors at present
13484         //Roo.log(this.store.reader.jsonData);
13485         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13486             // fixme
13487             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13488         //}
13489         
13490         
13491     },
13492     // private
13493     onTypeAhead : function(){
13494         if(this.store.getCount() > 0){
13495             var r = this.store.getAt(0);
13496             var newValue = r.data[this.displayField];
13497             var len = newValue.length;
13498             var selStart = this.getRawValue().length;
13499             
13500             if(selStart != len){
13501                 this.setRawValue(newValue);
13502                 this.selectText(selStart, newValue.length);
13503             }
13504         }
13505     },
13506
13507     // private
13508     onSelect : function(record, index){
13509         
13510         if(this.fireEvent('beforeselect', this, record, index) !== false){
13511         
13512             this.setFromData(index > -1 ? record.data : false);
13513             
13514             this.collapse();
13515             this.fireEvent('select', this, record, index);
13516         }
13517     },
13518
13519     /**
13520      * Returns the currently selected field value or empty string if no value is set.
13521      * @return {String} value The selected value
13522      */
13523     getValue : function()
13524     {
13525         if(Roo.isIOS && this.useNativeIOS){
13526             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13527         }
13528         
13529         if(this.multiple){
13530             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13531         }
13532         
13533         if(this.valueField){
13534             return typeof this.value != 'undefined' ? this.value : '';
13535         }else{
13536             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13537         }
13538     },
13539     
13540     getRawValue : function()
13541     {
13542         if(Roo.isIOS && this.useNativeIOS){
13543             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13544         }
13545         
13546         var v = this.inputEl().getValue();
13547         
13548         return v;
13549     },
13550
13551     /**
13552      * Clears any text/value currently set in the field
13553      */
13554     clearValue : function(){
13555         
13556         if(this.hiddenField){
13557             this.hiddenField.dom.value = '';
13558         }
13559         this.value = '';
13560         this.setRawValue('');
13561         this.lastSelectionText = '';
13562         this.lastData = false;
13563         
13564         var close = this.closeTriggerEl();
13565         
13566         if(close){
13567             close.hide();
13568         }
13569         
13570         this.validate();
13571         
13572     },
13573
13574     /**
13575      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13576      * will be displayed in the field.  If the value does not match the data value of an existing item,
13577      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13578      * Otherwise the field will be blank (although the value will still be set).
13579      * @param {String} value The value to match
13580      */
13581     setValue : function(v)
13582     {
13583         if(Roo.isIOS && this.useNativeIOS){
13584             this.setIOSValue(v);
13585             return;
13586         }
13587         
13588         if(this.multiple){
13589             this.syncValue();
13590             return;
13591         }
13592         
13593         var text = v;
13594         if(this.valueField){
13595             var r = this.findRecord(this.valueField, v);
13596             if(r){
13597                 text = r.data[this.displayField];
13598             }else if(this.valueNotFoundText !== undefined){
13599                 text = this.valueNotFoundText;
13600             }
13601         }
13602         this.lastSelectionText = text;
13603         if(this.hiddenField){
13604             this.hiddenField.dom.value = v;
13605         }
13606         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13607         this.value = v;
13608         
13609         var close = this.closeTriggerEl();
13610         
13611         if(close){
13612             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13613         }
13614         
13615         this.validate();
13616     },
13617     /**
13618      * @property {Object} the last set data for the element
13619      */
13620     
13621     lastData : false,
13622     /**
13623      * Sets the value of the field based on a object which is related to the record format for the store.
13624      * @param {Object} value the value to set as. or false on reset?
13625      */
13626     setFromData : function(o){
13627         
13628         if(this.multiple){
13629             this.addItem(o);
13630             return;
13631         }
13632             
13633         var dv = ''; // display value
13634         var vv = ''; // value value..
13635         this.lastData = o;
13636         if (this.displayField) {
13637             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13638         } else {
13639             // this is an error condition!!!
13640             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13641         }
13642         
13643         if(this.valueField){
13644             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13645         }
13646         
13647         var close = this.closeTriggerEl();
13648         
13649         if(close){
13650             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13651         }
13652         
13653         if(this.hiddenField){
13654             this.hiddenField.dom.value = vv;
13655             
13656             this.lastSelectionText = dv;
13657             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13658             this.value = vv;
13659             return;
13660         }
13661         // no hidden field.. - we store the value in 'value', but still display
13662         // display field!!!!
13663         this.lastSelectionText = dv;
13664         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13665         this.value = vv;
13666         
13667         
13668         
13669     },
13670     // private
13671     reset : function(){
13672         // overridden so that last data is reset..
13673         
13674         if(this.multiple){
13675             this.clearItem();
13676             return;
13677         }
13678         
13679         this.setValue(this.originalValue);
13680         //this.clearInvalid();
13681         this.lastData = false;
13682         if (this.view) {
13683             this.view.clearSelections();
13684         }
13685         
13686         this.validate();
13687     },
13688     // private
13689     findRecord : function(prop, value){
13690         var record;
13691         if(this.store.getCount() > 0){
13692             this.store.each(function(r){
13693                 if(r.data[prop] == value){
13694                     record = r;
13695                     return false;
13696                 }
13697                 return true;
13698             });
13699         }
13700         return record;
13701     },
13702     
13703     getName: function()
13704     {
13705         // returns hidden if it's set..
13706         if (!this.rendered) {return ''};
13707         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13708         
13709     },
13710     // private
13711     onViewMove : function(e, t){
13712         this.inKeyMode = false;
13713     },
13714
13715     // private
13716     onViewOver : function(e, t){
13717         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13718             return;
13719         }
13720         var item = this.view.findItemFromChild(t);
13721         
13722         if(item){
13723             var index = this.view.indexOf(item);
13724             this.select(index, false);
13725         }
13726     },
13727
13728     // private
13729     onViewClick : function(view, doFocus, el, e)
13730     {
13731         var index = this.view.getSelectedIndexes()[0];
13732         
13733         var r = this.store.getAt(index);
13734         
13735         if(this.tickable){
13736             
13737             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13738                 return;
13739             }
13740             
13741             var rm = false;
13742             var _this = this;
13743             
13744             Roo.each(this.tickItems, function(v,k){
13745                 
13746                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13747                     Roo.log(v);
13748                     _this.tickItems.splice(k, 1);
13749                     
13750                     if(typeof(e) == 'undefined' && view == false){
13751                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13752                     }
13753                     
13754                     rm = true;
13755                     return;
13756                 }
13757             });
13758             
13759             if(rm){
13760                 return;
13761             }
13762             
13763             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13764                 this.tickItems.push(r.data);
13765             }
13766             
13767             if(typeof(e) == 'undefined' && view == false){
13768                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13769             }
13770                     
13771             return;
13772         }
13773         
13774         if(r){
13775             this.onSelect(r, index);
13776         }
13777         if(doFocus !== false && !this.blockFocus){
13778             this.inputEl().focus();
13779         }
13780     },
13781
13782     // private
13783     restrictHeight : function(){
13784         //this.innerList.dom.style.height = '';
13785         //var inner = this.innerList.dom;
13786         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13787         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13788         //this.list.beginUpdate();
13789         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13790         this.list.alignTo(this.inputEl(), this.listAlign);
13791         this.list.alignTo(this.inputEl(), this.listAlign);
13792         //this.list.endUpdate();
13793     },
13794
13795     // private
13796     onEmptyResults : function(){
13797         
13798         if(this.tickable && this.editable){
13799             this.restrictHeight();
13800             return;
13801         }
13802         
13803         this.collapse();
13804     },
13805
13806     /**
13807      * Returns true if the dropdown list is expanded, else false.
13808      */
13809     isExpanded : function(){
13810         return this.list.isVisible();
13811     },
13812
13813     /**
13814      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13815      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13816      * @param {String} value The data value of the item to select
13817      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13818      * selected item if it is not currently in view (defaults to true)
13819      * @return {Boolean} True if the value matched an item in the list, else false
13820      */
13821     selectByValue : function(v, scrollIntoView){
13822         if(v !== undefined && v !== null){
13823             var r = this.findRecord(this.valueField || this.displayField, v);
13824             if(r){
13825                 this.select(this.store.indexOf(r), scrollIntoView);
13826                 return true;
13827             }
13828         }
13829         return false;
13830     },
13831
13832     /**
13833      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13834      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13835      * @param {Number} index The zero-based index of the list item to select
13836      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13837      * selected item if it is not currently in view (defaults to true)
13838      */
13839     select : function(index, scrollIntoView){
13840         this.selectedIndex = index;
13841         this.view.select(index);
13842         if(scrollIntoView !== false){
13843             var el = this.view.getNode(index);
13844             /*
13845              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13846              */
13847             if(el){
13848                 this.list.scrollChildIntoView(el, false);
13849             }
13850         }
13851     },
13852
13853     // private
13854     selectNext : function(){
13855         var ct = this.store.getCount();
13856         if(ct > 0){
13857             if(this.selectedIndex == -1){
13858                 this.select(0);
13859             }else if(this.selectedIndex < ct-1){
13860                 this.select(this.selectedIndex+1);
13861             }
13862         }
13863     },
13864
13865     // private
13866     selectPrev : function(){
13867         var ct = this.store.getCount();
13868         if(ct > 0){
13869             if(this.selectedIndex == -1){
13870                 this.select(0);
13871             }else if(this.selectedIndex != 0){
13872                 this.select(this.selectedIndex-1);
13873             }
13874         }
13875     },
13876
13877     // private
13878     onKeyUp : function(e){
13879         if(this.editable !== false && !e.isSpecialKey()){
13880             this.lastKey = e.getKey();
13881             this.dqTask.delay(this.queryDelay);
13882         }
13883     },
13884
13885     // private
13886     validateBlur : function(){
13887         return !this.list || !this.list.isVisible();   
13888     },
13889
13890     // private
13891     initQuery : function(){
13892         
13893         var v = this.getRawValue();
13894         
13895         if(this.tickable && this.editable){
13896             v = this.tickableInputEl().getValue();
13897         }
13898         
13899         this.doQuery(v);
13900     },
13901
13902     // private
13903     doForce : function(){
13904         if(this.inputEl().dom.value.length > 0){
13905             this.inputEl().dom.value =
13906                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13907              
13908         }
13909     },
13910
13911     /**
13912      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13913      * query allowing the query action to be canceled if needed.
13914      * @param {String} query The SQL query to execute
13915      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13916      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13917      * saved in the current store (defaults to false)
13918      */
13919     doQuery : function(q, forceAll){
13920         
13921         if(q === undefined || q === null){
13922             q = '';
13923         }
13924         var qe = {
13925             query: q,
13926             forceAll: forceAll,
13927             combo: this,
13928             cancel:false
13929         };
13930         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13931             return false;
13932         }
13933         q = qe.query;
13934         
13935         forceAll = qe.forceAll;
13936         if(forceAll === true || (q.length >= this.minChars)){
13937             
13938             this.hasQuery = true;
13939             
13940             if(this.lastQuery != q || this.alwaysQuery){
13941                 this.lastQuery = q;
13942                 if(this.mode == 'local'){
13943                     this.selectedIndex = -1;
13944                     if(forceAll){
13945                         this.store.clearFilter();
13946                     }else{
13947                         
13948                         if(this.specialFilter){
13949                             this.fireEvent('specialfilter', this);
13950                             this.onLoad();
13951                             return;
13952                         }
13953                         
13954                         this.store.filter(this.displayField, q);
13955                     }
13956                     
13957                     this.store.fireEvent("datachanged", this.store);
13958                     
13959                     this.onLoad();
13960                     
13961                     
13962                 }else{
13963                     
13964                     this.store.baseParams[this.queryParam] = q;
13965                     
13966                     var options = {params : this.getParams(q)};
13967                     
13968                     if(this.loadNext){
13969                         options.add = true;
13970                         options.params.start = this.page * this.pageSize;
13971                     }
13972                     
13973                     this.store.load(options);
13974                     
13975                     /*
13976                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13977                      *  we should expand the list on onLoad
13978                      *  so command out it
13979                      */
13980 //                    this.expand();
13981                 }
13982             }else{
13983                 this.selectedIndex = -1;
13984                 this.onLoad();   
13985             }
13986         }
13987         
13988         this.loadNext = false;
13989     },
13990     
13991     // private
13992     getParams : function(q){
13993         var p = {};
13994         //p[this.queryParam] = q;
13995         
13996         if(this.pageSize){
13997             p.start = 0;
13998             p.limit = this.pageSize;
13999         }
14000         return p;
14001     },
14002
14003     /**
14004      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14005      */
14006     collapse : function(){
14007         if(!this.isExpanded()){
14008             return;
14009         }
14010         
14011         this.list.hide();
14012         
14013         this.hasFocus = false;
14014         
14015         if(this.tickable){
14016             this.okBtn.hide();
14017             this.cancelBtn.hide();
14018             this.trigger.show();
14019             
14020             if(this.editable){
14021                 this.tickableInputEl().dom.value = '';
14022                 this.tickableInputEl().blur();
14023             }
14024             
14025         }
14026         
14027         Roo.get(document).un('mousedown', this.collapseIf, this);
14028         Roo.get(document).un('mousewheel', this.collapseIf, this);
14029         if (!this.editable) {
14030             Roo.get(document).un('keydown', this.listKeyPress, this);
14031         }
14032         this.fireEvent('collapse', this);
14033         
14034         this.validate();
14035     },
14036
14037     // private
14038     collapseIf : function(e){
14039         var in_combo  = e.within(this.el);
14040         var in_list =  e.within(this.list);
14041         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14042         
14043         if (in_combo || in_list || is_list) {
14044             //e.stopPropagation();
14045             return;
14046         }
14047         
14048         if(this.tickable){
14049             this.onTickableFooterButtonClick(e, false, false);
14050         }
14051
14052         this.collapse();
14053         
14054     },
14055
14056     /**
14057      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14058      */
14059     expand : function(){
14060        
14061         if(this.isExpanded() || !this.hasFocus){
14062             return;
14063         }
14064         
14065         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14066         this.list.setWidth(lw);
14067         
14068         Roo.log('expand');
14069         
14070         this.list.show();
14071         
14072         this.restrictHeight();
14073         
14074         if(this.tickable){
14075             
14076             this.tickItems = Roo.apply([], this.item);
14077             
14078             this.okBtn.show();
14079             this.cancelBtn.show();
14080             this.trigger.hide();
14081             
14082             if(this.editable){
14083                 this.tickableInputEl().focus();
14084             }
14085             
14086         }
14087         
14088         Roo.get(document).on('mousedown', this.collapseIf, this);
14089         Roo.get(document).on('mousewheel', this.collapseIf, this);
14090         if (!this.editable) {
14091             Roo.get(document).on('keydown', this.listKeyPress, this);
14092         }
14093         
14094         this.fireEvent('expand', this);
14095     },
14096
14097     // private
14098     // Implements the default empty TriggerField.onTriggerClick function
14099     onTriggerClick : function(e)
14100     {
14101         Roo.log('trigger click');
14102         
14103         if(this.disabled || !this.triggerList){
14104             return;
14105         }
14106         
14107         this.page = 0;
14108         this.loadNext = false;
14109         
14110         if(this.isExpanded()){
14111             this.collapse();
14112             if (!this.blockFocus) {
14113                 this.inputEl().focus();
14114             }
14115             
14116         }else {
14117             this.hasFocus = true;
14118             if(this.triggerAction == 'all') {
14119                 this.doQuery(this.allQuery, true);
14120             } else {
14121                 this.doQuery(this.getRawValue());
14122             }
14123             if (!this.blockFocus) {
14124                 this.inputEl().focus();
14125             }
14126         }
14127     },
14128     
14129     onTickableTriggerClick : function(e)
14130     {
14131         if(this.disabled){
14132             return;
14133         }
14134         
14135         this.page = 0;
14136         this.loadNext = false;
14137         this.hasFocus = true;
14138         
14139         if(this.triggerAction == 'all') {
14140             this.doQuery(this.allQuery, true);
14141         } else {
14142             this.doQuery(this.getRawValue());
14143         }
14144     },
14145     
14146     onSearchFieldClick : function(e)
14147     {
14148         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14149             this.onTickableFooterButtonClick(e, false, false);
14150             return;
14151         }
14152         
14153         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14154             return;
14155         }
14156         
14157         this.page = 0;
14158         this.loadNext = false;
14159         this.hasFocus = true;
14160         
14161         if(this.triggerAction == 'all') {
14162             this.doQuery(this.allQuery, true);
14163         } else {
14164             this.doQuery(this.getRawValue());
14165         }
14166     },
14167     
14168     listKeyPress : function(e)
14169     {
14170         //Roo.log('listkeypress');
14171         // scroll to first matching element based on key pres..
14172         if (e.isSpecialKey()) {
14173             return false;
14174         }
14175         var k = String.fromCharCode(e.getKey()).toUpperCase();
14176         //Roo.log(k);
14177         var match  = false;
14178         var csel = this.view.getSelectedNodes();
14179         var cselitem = false;
14180         if (csel.length) {
14181             var ix = this.view.indexOf(csel[0]);
14182             cselitem  = this.store.getAt(ix);
14183             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14184                 cselitem = false;
14185             }
14186             
14187         }
14188         
14189         this.store.each(function(v) { 
14190             if (cselitem) {
14191                 // start at existing selection.
14192                 if (cselitem.id == v.id) {
14193                     cselitem = false;
14194                 }
14195                 return true;
14196             }
14197                 
14198             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14199                 match = this.store.indexOf(v);
14200                 return false;
14201             }
14202             return true;
14203         }, this);
14204         
14205         if (match === false) {
14206             return true; // no more action?
14207         }
14208         // scroll to?
14209         this.view.select(match);
14210         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14211         sn.scrollIntoView(sn.dom.parentNode, false);
14212     },
14213     
14214     onViewScroll : function(e, t){
14215         
14216         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){
14217             return;
14218         }
14219         
14220         this.hasQuery = true;
14221         
14222         this.loading = this.list.select('.loading', true).first();
14223         
14224         if(this.loading === null){
14225             this.list.createChild({
14226                 tag: 'div',
14227                 cls: 'loading roo-select2-more-results roo-select2-active',
14228                 html: 'Loading more results...'
14229             });
14230             
14231             this.loading = this.list.select('.loading', true).first();
14232             
14233             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14234             
14235             this.loading.hide();
14236         }
14237         
14238         this.loading.show();
14239         
14240         var _combo = this;
14241         
14242         this.page++;
14243         this.loadNext = true;
14244         
14245         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14246         
14247         return;
14248     },
14249     
14250     addItem : function(o)
14251     {   
14252         var dv = ''; // display value
14253         
14254         if (this.displayField) {
14255             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14256         } else {
14257             // this is an error condition!!!
14258             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14259         }
14260         
14261         if(!dv.length){
14262             return;
14263         }
14264         
14265         var choice = this.choices.createChild({
14266             tag: 'li',
14267             cls: 'roo-select2-search-choice',
14268             cn: [
14269                 {
14270                     tag: 'div',
14271                     html: dv
14272                 },
14273                 {
14274                     tag: 'a',
14275                     href: '#',
14276                     cls: 'roo-select2-search-choice-close',
14277                     tabindex: '-1'
14278                 }
14279             ]
14280             
14281         }, this.searchField);
14282         
14283         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14284         
14285         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14286         
14287         this.item.push(o);
14288         
14289         this.lastData = o;
14290         
14291         this.syncValue();
14292         
14293         this.inputEl().dom.value = '';
14294         
14295         this.validate();
14296     },
14297     
14298     onRemoveItem : function(e, _self, o)
14299     {
14300         e.preventDefault();
14301         
14302         this.lastItem = Roo.apply([], this.item);
14303         
14304         var index = this.item.indexOf(o.data) * 1;
14305         
14306         if( index < 0){
14307             Roo.log('not this item?!');
14308             return;
14309         }
14310         
14311         this.item.splice(index, 1);
14312         o.item.remove();
14313         
14314         this.syncValue();
14315         
14316         this.fireEvent('remove', this, e);
14317         
14318         this.validate();
14319         
14320     },
14321     
14322     syncValue : function()
14323     {
14324         if(!this.item.length){
14325             this.clearValue();
14326             return;
14327         }
14328             
14329         var value = [];
14330         var _this = this;
14331         Roo.each(this.item, function(i){
14332             if(_this.valueField){
14333                 value.push(i[_this.valueField]);
14334                 return;
14335             }
14336
14337             value.push(i);
14338         });
14339
14340         this.value = value.join(',');
14341
14342         if(this.hiddenField){
14343             this.hiddenField.dom.value = this.value;
14344         }
14345         
14346         this.store.fireEvent("datachanged", this.store);
14347         
14348         this.validate();
14349     },
14350     
14351     clearItem : function()
14352     {
14353         if(!this.multiple){
14354             return;
14355         }
14356         
14357         this.item = [];
14358         
14359         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14360            c.remove();
14361         });
14362         
14363         this.syncValue();
14364         
14365         this.validate();
14366         
14367         if(this.tickable && !Roo.isTouch){
14368             this.view.refresh();
14369         }
14370     },
14371     
14372     inputEl: function ()
14373     {
14374         if(Roo.isIOS && this.useNativeIOS){
14375             return this.el.select('select.roo-ios-select', true).first();
14376         }
14377         
14378         if(Roo.isTouch && this.mobileTouchView){
14379             return this.el.select('input.form-control',true).first();
14380         }
14381         
14382         if(this.tickable){
14383             return this.searchField;
14384         }
14385         
14386         return this.el.select('input.form-control',true).first();
14387     },
14388     
14389     onTickableFooterButtonClick : function(e, btn, el)
14390     {
14391         e.preventDefault();
14392         
14393         this.lastItem = Roo.apply([], this.item);
14394         
14395         if(btn && btn.name == 'cancel'){
14396             this.tickItems = Roo.apply([], this.item);
14397             this.collapse();
14398             return;
14399         }
14400         
14401         this.clearItem();
14402         
14403         var _this = this;
14404         
14405         Roo.each(this.tickItems, function(o){
14406             _this.addItem(o);
14407         });
14408         
14409         this.collapse();
14410         
14411     },
14412     
14413     validate : function()
14414     {
14415         var v = this.getRawValue();
14416         
14417         if(this.multiple){
14418             v = this.getValue();
14419         }
14420         
14421         if(this.disabled || this.allowBlank || v.length){
14422             this.markValid();
14423             return true;
14424         }
14425         
14426         this.markInvalid();
14427         return false;
14428     },
14429     
14430     tickableInputEl : function()
14431     {
14432         if(!this.tickable || !this.editable){
14433             return this.inputEl();
14434         }
14435         
14436         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14437     },
14438     
14439     
14440     getAutoCreateTouchView : function()
14441     {
14442         var id = Roo.id();
14443         
14444         var cfg = {
14445             cls: 'form-group' //input-group
14446         };
14447         
14448         var input =  {
14449             tag: 'input',
14450             id : id,
14451             type : this.inputType,
14452             cls : 'form-control x-combo-noedit',
14453             autocomplete: 'new-password',
14454             placeholder : this.placeholder || '',
14455             readonly : true
14456         };
14457         
14458         if (this.name) {
14459             input.name = this.name;
14460         }
14461         
14462         if (this.size) {
14463             input.cls += ' input-' + this.size;
14464         }
14465         
14466         if (this.disabled) {
14467             input.disabled = true;
14468         }
14469         
14470         var inputblock = {
14471             cls : '',
14472             cn : [
14473                 input
14474             ]
14475         };
14476         
14477         if(this.before){
14478             inputblock.cls += ' input-group';
14479             
14480             inputblock.cn.unshift({
14481                 tag :'span',
14482                 cls : 'input-group-addon',
14483                 html : this.before
14484             });
14485         }
14486         
14487         if(this.removable && !this.multiple){
14488             inputblock.cls += ' roo-removable';
14489             
14490             inputblock.cn.push({
14491                 tag: 'button',
14492                 html : 'x',
14493                 cls : 'roo-combo-removable-btn close'
14494             });
14495         }
14496
14497         if(this.hasFeedback && !this.allowBlank){
14498             
14499             inputblock.cls += ' has-feedback';
14500             
14501             inputblock.cn.push({
14502                 tag: 'span',
14503                 cls: 'glyphicon form-control-feedback'
14504             });
14505             
14506         }
14507         
14508         if (this.after) {
14509             
14510             inputblock.cls += (this.before) ? '' : ' input-group';
14511             
14512             inputblock.cn.push({
14513                 tag :'span',
14514                 cls : 'input-group-addon',
14515                 html : this.after
14516             });
14517         }
14518
14519         var box = {
14520             tag: 'div',
14521             cn: [
14522                 {
14523                     tag: 'input',
14524                     type : 'hidden',
14525                     cls: 'form-hidden-field'
14526                 },
14527                 inputblock
14528             ]
14529             
14530         };
14531         
14532         if(this.multiple){
14533             box = {
14534                 tag: 'div',
14535                 cn: [
14536                     {
14537                         tag: 'input',
14538                         type : 'hidden',
14539                         cls: 'form-hidden-field'
14540                     },
14541                     {
14542                         tag: 'ul',
14543                         cls: 'roo-select2-choices',
14544                         cn:[
14545                             {
14546                                 tag: 'li',
14547                                 cls: 'roo-select2-search-field',
14548                                 cn: [
14549
14550                                     inputblock
14551                                 ]
14552                             }
14553                         ]
14554                     }
14555                 ]
14556             }
14557         };
14558         
14559         var combobox = {
14560             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14561             cn: [
14562                 box
14563             ]
14564         };
14565         
14566         if(!this.multiple && this.showToggleBtn){
14567             
14568             var caret = {
14569                         tag: 'span',
14570                         cls: 'caret'
14571             };
14572             
14573             if (this.caret != false) {
14574                 caret = {
14575                      tag: 'i',
14576                      cls: 'fa fa-' + this.caret
14577                 };
14578                 
14579             }
14580             
14581             combobox.cn.push({
14582                 tag :'span',
14583                 cls : 'input-group-addon btn dropdown-toggle',
14584                 cn : [
14585                     caret,
14586                     {
14587                         tag: 'span',
14588                         cls: 'combobox-clear',
14589                         cn  : [
14590                             {
14591                                 tag : 'i',
14592                                 cls: 'icon-remove'
14593                             }
14594                         ]
14595                     }
14596                 ]
14597
14598             })
14599         }
14600         
14601         if(this.multiple){
14602             combobox.cls += ' roo-select2-container-multi';
14603         }
14604         
14605         var align = this.labelAlign || this.parentLabelAlign();
14606         
14607         if (align ==='left' && this.fieldLabel.length) {
14608
14609             cfg.cn = [
14610                 {
14611                    tag : 'i',
14612                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14613                    tooltip : 'This field is required'
14614                 },
14615                 {
14616                     tag: 'label',
14617                     cls : 'control-label',
14618                     html : this.fieldLabel
14619
14620                 },
14621                 {
14622                     cls : '', 
14623                     cn: [
14624                         combobox
14625                     ]
14626                 }
14627             ];
14628             
14629             var labelCfg = cfg.cn[1];
14630             var contentCfg = cfg.cn[2];
14631             
14632
14633             if(this.indicatorpos == 'right'){
14634                 cfg.cn = [
14635                     {
14636                         tag: 'label',
14637                         cls : 'control-label',
14638                         html : this.fieldLabel,
14639                         cn : [
14640                             {
14641                                tag : 'i',
14642                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14643                                tooltip : 'This field is required'
14644                             }
14645                         ]
14646                     },
14647                     {
14648                         cls : '', 
14649                         cn: [
14650                             combobox
14651                         ]
14652                     }
14653                 ];
14654             }
14655             
14656             labelCfg = cfg.cn[0];
14657             contentCfg = cfg.cn[2];
14658             
14659             if(this.labelWidth > 12){
14660                 labelCfg.style = "width: " + this.labelWidth + 'px';
14661             }
14662             
14663             if(this.labelWidth < 13 && this.labelmd == 0){
14664                 this.labelmd = this.labelWidth;
14665             }
14666             
14667             if(this.labellg > 0){
14668                 labelCfg.cls += ' col-lg-' + this.labellg;
14669                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14670             }
14671             
14672             if(this.labelmd > 0){
14673                 labelCfg.cls += ' col-md-' + this.labelmd;
14674                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14675             }
14676             
14677             if(this.labelsm > 0){
14678                 labelCfg.cls += ' col-sm-' + this.labelsm;
14679                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14680             }
14681             
14682             if(this.labelxs > 0){
14683                 labelCfg.cls += ' col-xs-' + this.labelxs;
14684                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14685             }
14686                 
14687                 
14688         } else if ( this.fieldLabel.length) {
14689             cfg.cn = [
14690                 {
14691                    tag : 'i',
14692                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14693                    tooltip : 'This field is required'
14694                 },
14695                 {
14696                     tag: 'label',
14697                     cls : 'control-label',
14698                     html : this.fieldLabel
14699
14700                 },
14701                 {
14702                     cls : '', 
14703                     cn: [
14704                         combobox
14705                     ]
14706                 }
14707             ];
14708             
14709             if(this.indicatorpos == 'right'){
14710                 cfg.cn = [
14711                     {
14712                         tag: 'label',
14713                         cls : 'control-label',
14714                         html : this.fieldLabel,
14715                         cn : [
14716                             {
14717                                tag : 'i',
14718                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14719                                tooltip : 'This field is required'
14720                             }
14721                         ]
14722                     },
14723                     {
14724                         cls : '', 
14725                         cn: [
14726                             combobox
14727                         ]
14728                     }
14729                 ];
14730             }
14731         } else {
14732             cfg.cn = combobox;    
14733         }
14734         
14735         
14736         var settings = this;
14737         
14738         ['xs','sm','md','lg'].map(function(size){
14739             if (settings[size]) {
14740                 cfg.cls += ' col-' + size + '-' + settings[size];
14741             }
14742         });
14743         
14744         return cfg;
14745     },
14746     
14747     initTouchView : function()
14748     {
14749         this.renderTouchView();
14750         
14751         this.touchViewEl.on('scroll', function(){
14752             this.el.dom.scrollTop = 0;
14753         }, this);
14754         
14755         this.originalValue = this.getValue();
14756         
14757         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14758         
14759         this.inputEl().on("click", this.showTouchView, this);
14760         if (this.triggerEl) {
14761             this.triggerEl.on("click", this.showTouchView, this);
14762         }
14763         
14764         
14765         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14766         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14767         
14768         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14769         
14770         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14771         this.store.on('load', this.onTouchViewLoad, this);
14772         this.store.on('loadexception', this.onTouchViewLoadException, this);
14773         
14774         if(this.hiddenName){
14775             
14776             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14777             
14778             this.hiddenField.dom.value =
14779                 this.hiddenValue !== undefined ? this.hiddenValue :
14780                 this.value !== undefined ? this.value : '';
14781         
14782             this.el.dom.removeAttribute('name');
14783             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14784         }
14785         
14786         if(this.multiple){
14787             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14788             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14789         }
14790         
14791         if(this.removable && !this.multiple){
14792             var close = this.closeTriggerEl();
14793             if(close){
14794                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14795                 close.on('click', this.removeBtnClick, this, close);
14796             }
14797         }
14798         /*
14799          * fix the bug in Safari iOS8
14800          */
14801         this.inputEl().on("focus", function(e){
14802             document.activeElement.blur();
14803         }, this);
14804         
14805         return;
14806         
14807         
14808     },
14809     
14810     renderTouchView : function()
14811     {
14812         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14813         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14814         
14815         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14816         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14817         
14818         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14819         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14820         this.touchViewBodyEl.setStyle('overflow', 'auto');
14821         
14822         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14823         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14824         
14825         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14826         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14827         
14828     },
14829     
14830     showTouchView : function()
14831     {
14832         if(this.disabled){
14833             return;
14834         }
14835         
14836         this.touchViewHeaderEl.hide();
14837
14838         if(this.modalTitle.length){
14839             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14840             this.touchViewHeaderEl.show();
14841         }
14842
14843         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14844         this.touchViewEl.show();
14845
14846         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14847         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14848                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14849
14850         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14851
14852         if(this.modalTitle.length){
14853             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14854         }
14855         
14856         this.touchViewBodyEl.setHeight(bodyHeight);
14857
14858         if(this.animate){
14859             var _this = this;
14860             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14861         }else{
14862             this.touchViewEl.addClass('in');
14863         }
14864
14865         this.doTouchViewQuery();
14866         
14867     },
14868     
14869     hideTouchView : function()
14870     {
14871         this.touchViewEl.removeClass('in');
14872
14873         if(this.animate){
14874             var _this = this;
14875             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14876         }else{
14877             this.touchViewEl.setStyle('display', 'none');
14878         }
14879         
14880     },
14881     
14882     setTouchViewValue : function()
14883     {
14884         if(this.multiple){
14885             this.clearItem();
14886         
14887             var _this = this;
14888
14889             Roo.each(this.tickItems, function(o){
14890                 this.addItem(o);
14891             }, this);
14892         }
14893         
14894         this.hideTouchView();
14895     },
14896     
14897     doTouchViewQuery : function()
14898     {
14899         var qe = {
14900             query: '',
14901             forceAll: true,
14902             combo: this,
14903             cancel:false
14904         };
14905         
14906         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14907             return false;
14908         }
14909         
14910         if(!this.alwaysQuery || this.mode == 'local'){
14911             this.onTouchViewLoad();
14912             return;
14913         }
14914         
14915         this.store.load();
14916     },
14917     
14918     onTouchViewBeforeLoad : function(combo,opts)
14919     {
14920         return;
14921     },
14922
14923     // private
14924     onTouchViewLoad : function()
14925     {
14926         if(this.store.getCount() < 1){
14927             this.onTouchViewEmptyResults();
14928             return;
14929         }
14930         
14931         this.clearTouchView();
14932         
14933         var rawValue = this.getRawValue();
14934         
14935         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14936         
14937         this.tickItems = [];
14938         
14939         this.store.data.each(function(d, rowIndex){
14940             var row = this.touchViewListGroup.createChild(template);
14941             
14942             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14943                 row.addClass(d.data.cls);
14944             }
14945             
14946             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14947                 var cfg = {
14948                     data : d.data,
14949                     html : d.data[this.displayField]
14950                 };
14951                 
14952                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14953                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14954                 }
14955             }
14956             row.removeClass('selected');
14957             if(!this.multiple && this.valueField &&
14958                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14959             {
14960                 // radio buttons..
14961                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14962                 row.addClass('selected');
14963             }
14964             
14965             if(this.multiple && this.valueField &&
14966                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14967             {
14968                 
14969                 // checkboxes...
14970                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14971                 this.tickItems.push(d.data);
14972             }
14973             
14974             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14975             
14976         }, this);
14977         
14978         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14979         
14980         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14981
14982         if(this.modalTitle.length){
14983             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14984         }
14985
14986         var listHeight = this.touchViewListGroup.getHeight();
14987         
14988         var _this = this;
14989         
14990         if(firstChecked && listHeight > bodyHeight){
14991             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14992         }
14993         
14994     },
14995     
14996     onTouchViewLoadException : function()
14997     {
14998         this.hideTouchView();
14999     },
15000     
15001     onTouchViewEmptyResults : function()
15002     {
15003         this.clearTouchView();
15004         
15005         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15006         
15007         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15008         
15009     },
15010     
15011     clearTouchView : function()
15012     {
15013         this.touchViewListGroup.dom.innerHTML = '';
15014     },
15015     
15016     onTouchViewClick : function(e, el, o)
15017     {
15018         e.preventDefault();
15019         
15020         var row = o.row;
15021         var rowIndex = o.rowIndex;
15022         
15023         var r = this.store.getAt(rowIndex);
15024         
15025         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15026             
15027             if(!this.multiple){
15028                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15029                     c.dom.removeAttribute('checked');
15030                 }, this);
15031
15032                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15033
15034                 this.setFromData(r.data);
15035
15036                 var close = this.closeTriggerEl();
15037
15038                 if(close){
15039                     close.show();
15040                 }
15041
15042                 this.hideTouchView();
15043
15044                 this.fireEvent('select', this, r, rowIndex);
15045
15046                 return;
15047             }
15048
15049             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15050                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15051                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15052                 return;
15053             }
15054
15055             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15056             this.addItem(r.data);
15057             this.tickItems.push(r.data);
15058         }
15059     },
15060     
15061     getAutoCreateNativeIOS : function()
15062     {
15063         var cfg = {
15064             cls: 'form-group' //input-group,
15065         };
15066         
15067         var combobox =  {
15068             tag: 'select',
15069             cls : 'roo-ios-select'
15070         };
15071         
15072         if (this.name) {
15073             combobox.name = this.name;
15074         }
15075         
15076         if (this.disabled) {
15077             combobox.disabled = true;
15078         }
15079         
15080         var settings = this;
15081         
15082         ['xs','sm','md','lg'].map(function(size){
15083             if (settings[size]) {
15084                 cfg.cls += ' col-' + size + '-' + settings[size];
15085             }
15086         });
15087         
15088         cfg.cn = combobox;
15089         
15090         return cfg;
15091         
15092     },
15093     
15094     initIOSView : function()
15095     {
15096         this.store.on('load', this.onIOSViewLoad, this);
15097         
15098         return;
15099     },
15100     
15101     onIOSViewLoad : function()
15102     {
15103         if(this.store.getCount() < 1){
15104             return;
15105         }
15106         
15107         this.clearIOSView();
15108         
15109         if(this.allowBlank) {
15110             
15111             var default_text = '-- SELECT --';
15112             
15113             var opt = this.inputEl().createChild({
15114                 tag: 'option',
15115                 value : 0,
15116                 html : default_text
15117             });
15118             
15119             var o = {};
15120             o[this.valueField] = 0;
15121             o[this.displayField] = default_text;
15122             
15123             this.ios_options.push({
15124                 data : o,
15125                 el : opt
15126             });
15127             
15128         }
15129         
15130         this.store.data.each(function(d, rowIndex){
15131             
15132             var html = '';
15133             
15134             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15135                 html = d.data[this.displayField];
15136             }
15137             
15138             var value = '';
15139             
15140             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15141                 value = d.data[this.valueField];
15142             }
15143             
15144             var option = {
15145                 tag: 'option',
15146                 value : value,
15147                 html : html
15148             };
15149             
15150             if(this.value == d.data[this.valueField]){
15151                 option['selected'] = true;
15152             }
15153             
15154             var opt = this.inputEl().createChild(option);
15155             
15156             this.ios_options.push({
15157                 data : d.data,
15158                 el : opt
15159             });
15160             
15161         }, this);
15162         
15163         this.inputEl().on('change', function(){
15164            this.fireEvent('select', this);
15165         }, this);
15166         
15167     },
15168     
15169     clearIOSView: function()
15170     {
15171         this.inputEl().dom.innerHTML = '';
15172         
15173         this.ios_options = [];
15174     },
15175     
15176     setIOSValue: function(v)
15177     {
15178         this.value = v;
15179         
15180         if(!this.ios_options){
15181             return;
15182         }
15183         
15184         Roo.each(this.ios_options, function(opts){
15185            
15186            opts.el.dom.removeAttribute('selected');
15187            
15188            if(opts.data[this.valueField] != v){
15189                return;
15190            }
15191            
15192            opts.el.dom.setAttribute('selected', true);
15193            
15194         }, this);
15195     }
15196
15197     /** 
15198     * @cfg {Boolean} grow 
15199     * @hide 
15200     */
15201     /** 
15202     * @cfg {Number} growMin 
15203     * @hide 
15204     */
15205     /** 
15206     * @cfg {Number} growMax 
15207     * @hide 
15208     */
15209     /**
15210      * @hide
15211      * @method autoSize
15212      */
15213 });
15214
15215 Roo.apply(Roo.bootstrap.ComboBox,  {
15216     
15217     header : {
15218         tag: 'div',
15219         cls: 'modal-header',
15220         cn: [
15221             {
15222                 tag: 'h4',
15223                 cls: 'modal-title'
15224             }
15225         ]
15226     },
15227     
15228     body : {
15229         tag: 'div',
15230         cls: 'modal-body',
15231         cn: [
15232             {
15233                 tag: 'ul',
15234                 cls: 'list-group'
15235             }
15236         ]
15237     },
15238     
15239     listItemRadio : {
15240         tag: 'li',
15241         cls: 'list-group-item',
15242         cn: [
15243             {
15244                 tag: 'span',
15245                 cls: 'roo-combobox-list-group-item-value'
15246             },
15247             {
15248                 tag: 'div',
15249                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15250                 cn: [
15251                     {
15252                         tag: 'input',
15253                         type: 'radio'
15254                     },
15255                     {
15256                         tag: 'label'
15257                     }
15258                 ]
15259             }
15260         ]
15261     },
15262     
15263     listItemCheckbox : {
15264         tag: 'li',
15265         cls: 'list-group-item',
15266         cn: [
15267             {
15268                 tag: 'span',
15269                 cls: 'roo-combobox-list-group-item-value'
15270             },
15271             {
15272                 tag: 'div',
15273                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15274                 cn: [
15275                     {
15276                         tag: 'input',
15277                         type: 'checkbox'
15278                     },
15279                     {
15280                         tag: 'label'
15281                     }
15282                 ]
15283             }
15284         ]
15285     },
15286     
15287     emptyResult : {
15288         tag: 'div',
15289         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15290     },
15291     
15292     footer : {
15293         tag: 'div',
15294         cls: 'modal-footer',
15295         cn: [
15296             {
15297                 tag: 'div',
15298                 cls: 'row',
15299                 cn: [
15300                     {
15301                         tag: 'div',
15302                         cls: 'col-xs-6 text-left',
15303                         cn: {
15304                             tag: 'button',
15305                             cls: 'btn btn-danger roo-touch-view-cancel',
15306                             html: 'Cancel'
15307                         }
15308                     },
15309                     {
15310                         tag: 'div',
15311                         cls: 'col-xs-6 text-right',
15312                         cn: {
15313                             tag: 'button',
15314                             cls: 'btn btn-success roo-touch-view-ok',
15315                             html: 'OK'
15316                         }
15317                     }
15318                 ]
15319             }
15320         ]
15321         
15322     }
15323 });
15324
15325 Roo.apply(Roo.bootstrap.ComboBox,  {
15326     
15327     touchViewTemplate : {
15328         tag: 'div',
15329         cls: 'modal fade roo-combobox-touch-view',
15330         cn: [
15331             {
15332                 tag: 'div',
15333                 cls: 'modal-dialog',
15334                 style : 'position:fixed', // we have to fix position....
15335                 cn: [
15336                     {
15337                         tag: 'div',
15338                         cls: 'modal-content',
15339                         cn: [
15340                             Roo.bootstrap.ComboBox.header,
15341                             Roo.bootstrap.ComboBox.body,
15342                             Roo.bootstrap.ComboBox.footer
15343                         ]
15344                     }
15345                 ]
15346             }
15347         ]
15348     }
15349 });/*
15350  * Based on:
15351  * Ext JS Library 1.1.1
15352  * Copyright(c) 2006-2007, Ext JS, LLC.
15353  *
15354  * Originally Released Under LGPL - original licence link has changed is not relivant.
15355  *
15356  * Fork - LGPL
15357  * <script type="text/javascript">
15358  */
15359
15360 /**
15361  * @class Roo.View
15362  * @extends Roo.util.Observable
15363  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15364  * This class also supports single and multi selection modes. <br>
15365  * Create a data model bound view:
15366  <pre><code>
15367  var store = new Roo.data.Store(...);
15368
15369  var view = new Roo.View({
15370     el : "my-element",
15371     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15372  
15373     singleSelect: true,
15374     selectedClass: "ydataview-selected",
15375     store: store
15376  });
15377
15378  // listen for node click?
15379  view.on("click", function(vw, index, node, e){
15380  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15381  });
15382
15383  // load XML data
15384  dataModel.load("foobar.xml");
15385  </code></pre>
15386  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15387  * <br><br>
15388  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15389  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15390  * 
15391  * Note: old style constructor is still suported (container, template, config)
15392  * 
15393  * @constructor
15394  * Create a new View
15395  * @param {Object} config The config object
15396  * 
15397  */
15398 Roo.View = function(config, depreciated_tpl, depreciated_config){
15399     
15400     this.parent = false;
15401     
15402     if (typeof(depreciated_tpl) == 'undefined') {
15403         // new way.. - universal constructor.
15404         Roo.apply(this, config);
15405         this.el  = Roo.get(this.el);
15406     } else {
15407         // old format..
15408         this.el  = Roo.get(config);
15409         this.tpl = depreciated_tpl;
15410         Roo.apply(this, depreciated_config);
15411     }
15412     this.wrapEl  = this.el.wrap().wrap();
15413     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15414     
15415     
15416     if(typeof(this.tpl) == "string"){
15417         this.tpl = new Roo.Template(this.tpl);
15418     } else {
15419         // support xtype ctors..
15420         this.tpl = new Roo.factory(this.tpl, Roo);
15421     }
15422     
15423     
15424     this.tpl.compile();
15425     
15426     /** @private */
15427     this.addEvents({
15428         /**
15429          * @event beforeclick
15430          * Fires before a click is processed. Returns false to cancel the default action.
15431          * @param {Roo.View} this
15432          * @param {Number} index The index of the target node
15433          * @param {HTMLElement} node The target node
15434          * @param {Roo.EventObject} e The raw event object
15435          */
15436             "beforeclick" : true,
15437         /**
15438          * @event click
15439          * Fires when a template node is clicked.
15440          * @param {Roo.View} this
15441          * @param {Number} index The index of the target node
15442          * @param {HTMLElement} node The target node
15443          * @param {Roo.EventObject} e The raw event object
15444          */
15445             "click" : true,
15446         /**
15447          * @event dblclick
15448          * Fires when a template node is double clicked.
15449          * @param {Roo.View} this
15450          * @param {Number} index The index of the target node
15451          * @param {HTMLElement} node The target node
15452          * @param {Roo.EventObject} e The raw event object
15453          */
15454             "dblclick" : true,
15455         /**
15456          * @event contextmenu
15457          * Fires when a template node is right clicked.
15458          * @param {Roo.View} this
15459          * @param {Number} index The index of the target node
15460          * @param {HTMLElement} node The target node
15461          * @param {Roo.EventObject} e The raw event object
15462          */
15463             "contextmenu" : true,
15464         /**
15465          * @event selectionchange
15466          * Fires when the selected nodes change.
15467          * @param {Roo.View} this
15468          * @param {Array} selections Array of the selected nodes
15469          */
15470             "selectionchange" : true,
15471     
15472         /**
15473          * @event beforeselect
15474          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15475          * @param {Roo.View} this
15476          * @param {HTMLElement} node The node to be selected
15477          * @param {Array} selections Array of currently selected nodes
15478          */
15479             "beforeselect" : true,
15480         /**
15481          * @event preparedata
15482          * Fires on every row to render, to allow you to change the data.
15483          * @param {Roo.View} this
15484          * @param {Object} data to be rendered (change this)
15485          */
15486           "preparedata" : true
15487           
15488           
15489         });
15490
15491
15492
15493     this.el.on({
15494         "click": this.onClick,
15495         "dblclick": this.onDblClick,
15496         "contextmenu": this.onContextMenu,
15497         scope:this
15498     });
15499
15500     this.selections = [];
15501     this.nodes = [];
15502     this.cmp = new Roo.CompositeElementLite([]);
15503     if(this.store){
15504         this.store = Roo.factory(this.store, Roo.data);
15505         this.setStore(this.store, true);
15506     }
15507     
15508     if ( this.footer && this.footer.xtype) {
15509            
15510          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15511         
15512         this.footer.dataSource = this.store;
15513         this.footer.container = fctr;
15514         this.footer = Roo.factory(this.footer, Roo);
15515         fctr.insertFirst(this.el);
15516         
15517         // this is a bit insane - as the paging toolbar seems to detach the el..
15518 //        dom.parentNode.parentNode.parentNode
15519          // they get detached?
15520     }
15521     
15522     
15523     Roo.View.superclass.constructor.call(this);
15524     
15525     
15526 };
15527
15528 Roo.extend(Roo.View, Roo.util.Observable, {
15529     
15530      /**
15531      * @cfg {Roo.data.Store} store Data store to load data from.
15532      */
15533     store : false,
15534     
15535     /**
15536      * @cfg {String|Roo.Element} el The container element.
15537      */
15538     el : '',
15539     
15540     /**
15541      * @cfg {String|Roo.Template} tpl The template used by this View 
15542      */
15543     tpl : false,
15544     /**
15545      * @cfg {String} dataName the named area of the template to use as the data area
15546      *                          Works with domtemplates roo-name="name"
15547      */
15548     dataName: false,
15549     /**
15550      * @cfg {String} selectedClass The css class to add to selected nodes
15551      */
15552     selectedClass : "x-view-selected",
15553      /**
15554      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15555      */
15556     emptyText : "",
15557     
15558     /**
15559      * @cfg {String} text to display on mask (default Loading)
15560      */
15561     mask : false,
15562     /**
15563      * @cfg {Boolean} multiSelect Allow multiple selection
15564      */
15565     multiSelect : false,
15566     /**
15567      * @cfg {Boolean} singleSelect Allow single selection
15568      */
15569     singleSelect:  false,
15570     
15571     /**
15572      * @cfg {Boolean} toggleSelect - selecting 
15573      */
15574     toggleSelect : false,
15575     
15576     /**
15577      * @cfg {Boolean} tickable - selecting 
15578      */
15579     tickable : false,
15580     
15581     /**
15582      * Returns the element this view is bound to.
15583      * @return {Roo.Element}
15584      */
15585     getEl : function(){
15586         return this.wrapEl;
15587     },
15588     
15589     
15590
15591     /**
15592      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15593      */
15594     refresh : function(){
15595         //Roo.log('refresh');
15596         var t = this.tpl;
15597         
15598         // if we are using something like 'domtemplate', then
15599         // the what gets used is:
15600         // t.applySubtemplate(NAME, data, wrapping data..)
15601         // the outer template then get' applied with
15602         //     the store 'extra data'
15603         // and the body get's added to the
15604         //      roo-name="data" node?
15605         //      <span class='roo-tpl-{name}'></span> ?????
15606         
15607         
15608         
15609         this.clearSelections();
15610         this.el.update("");
15611         var html = [];
15612         var records = this.store.getRange();
15613         if(records.length < 1) {
15614             
15615             // is this valid??  = should it render a template??
15616             
15617             this.el.update(this.emptyText);
15618             return;
15619         }
15620         var el = this.el;
15621         if (this.dataName) {
15622             this.el.update(t.apply(this.store.meta)); //????
15623             el = this.el.child('.roo-tpl-' + this.dataName);
15624         }
15625         
15626         for(var i = 0, len = records.length; i < len; i++){
15627             var data = this.prepareData(records[i].data, i, records[i]);
15628             this.fireEvent("preparedata", this, data, i, records[i]);
15629             
15630             var d = Roo.apply({}, data);
15631             
15632             if(this.tickable){
15633                 Roo.apply(d, {'roo-id' : Roo.id()});
15634                 
15635                 var _this = this;
15636             
15637                 Roo.each(this.parent.item, function(item){
15638                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15639                         return;
15640                     }
15641                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15642                 });
15643             }
15644             
15645             html[html.length] = Roo.util.Format.trim(
15646                 this.dataName ?
15647                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15648                     t.apply(d)
15649             );
15650         }
15651         
15652         
15653         
15654         el.update(html.join(""));
15655         this.nodes = el.dom.childNodes;
15656         this.updateIndexes(0);
15657     },
15658     
15659
15660     /**
15661      * Function to override to reformat the data that is sent to
15662      * the template for each node.
15663      * DEPRICATED - use the preparedata event handler.
15664      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15665      * a JSON object for an UpdateManager bound view).
15666      */
15667     prepareData : function(data, index, record)
15668     {
15669         this.fireEvent("preparedata", this, data, index, record);
15670         return data;
15671     },
15672
15673     onUpdate : function(ds, record){
15674         // Roo.log('on update');   
15675         this.clearSelections();
15676         var index = this.store.indexOf(record);
15677         var n = this.nodes[index];
15678         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15679         n.parentNode.removeChild(n);
15680         this.updateIndexes(index, index);
15681     },
15682
15683     
15684     
15685 // --------- FIXME     
15686     onAdd : function(ds, records, index)
15687     {
15688         //Roo.log(['on Add', ds, records, index] );        
15689         this.clearSelections();
15690         if(this.nodes.length == 0){
15691             this.refresh();
15692             return;
15693         }
15694         var n = this.nodes[index];
15695         for(var i = 0, len = records.length; i < len; i++){
15696             var d = this.prepareData(records[i].data, i, records[i]);
15697             if(n){
15698                 this.tpl.insertBefore(n, d);
15699             }else{
15700                 
15701                 this.tpl.append(this.el, d);
15702             }
15703         }
15704         this.updateIndexes(index);
15705     },
15706
15707     onRemove : function(ds, record, index){
15708        // Roo.log('onRemove');
15709         this.clearSelections();
15710         var el = this.dataName  ?
15711             this.el.child('.roo-tpl-' + this.dataName) :
15712             this.el; 
15713         
15714         el.dom.removeChild(this.nodes[index]);
15715         this.updateIndexes(index);
15716     },
15717
15718     /**
15719      * Refresh an individual node.
15720      * @param {Number} index
15721      */
15722     refreshNode : function(index){
15723         this.onUpdate(this.store, this.store.getAt(index));
15724     },
15725
15726     updateIndexes : function(startIndex, endIndex){
15727         var ns = this.nodes;
15728         startIndex = startIndex || 0;
15729         endIndex = endIndex || ns.length - 1;
15730         for(var i = startIndex; i <= endIndex; i++){
15731             ns[i].nodeIndex = i;
15732         }
15733     },
15734
15735     /**
15736      * Changes the data store this view uses and refresh the view.
15737      * @param {Store} store
15738      */
15739     setStore : function(store, initial){
15740         if(!initial && this.store){
15741             this.store.un("datachanged", this.refresh);
15742             this.store.un("add", this.onAdd);
15743             this.store.un("remove", this.onRemove);
15744             this.store.un("update", this.onUpdate);
15745             this.store.un("clear", this.refresh);
15746             this.store.un("beforeload", this.onBeforeLoad);
15747             this.store.un("load", this.onLoad);
15748             this.store.un("loadexception", this.onLoad);
15749         }
15750         if(store){
15751           
15752             store.on("datachanged", this.refresh, this);
15753             store.on("add", this.onAdd, this);
15754             store.on("remove", this.onRemove, this);
15755             store.on("update", this.onUpdate, this);
15756             store.on("clear", this.refresh, this);
15757             store.on("beforeload", this.onBeforeLoad, this);
15758             store.on("load", this.onLoad, this);
15759             store.on("loadexception", this.onLoad, this);
15760         }
15761         
15762         if(store){
15763             this.refresh();
15764         }
15765     },
15766     /**
15767      * onbeforeLoad - masks the loading area.
15768      *
15769      */
15770     onBeforeLoad : function(store,opts)
15771     {
15772          //Roo.log('onBeforeLoad');   
15773         if (!opts.add) {
15774             this.el.update("");
15775         }
15776         this.el.mask(this.mask ? this.mask : "Loading" ); 
15777     },
15778     onLoad : function ()
15779     {
15780         this.el.unmask();
15781     },
15782     
15783
15784     /**
15785      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15786      * @param {HTMLElement} node
15787      * @return {HTMLElement} The template node
15788      */
15789     findItemFromChild : function(node){
15790         var el = this.dataName  ?
15791             this.el.child('.roo-tpl-' + this.dataName,true) :
15792             this.el.dom; 
15793         
15794         if(!node || node.parentNode == el){
15795                     return node;
15796             }
15797             var p = node.parentNode;
15798             while(p && p != el){
15799             if(p.parentNode == el){
15800                 return p;
15801             }
15802             p = p.parentNode;
15803         }
15804             return null;
15805     },
15806
15807     /** @ignore */
15808     onClick : function(e){
15809         var item = this.findItemFromChild(e.getTarget());
15810         if(item){
15811             var index = this.indexOf(item);
15812             if(this.onItemClick(item, index, e) !== false){
15813                 this.fireEvent("click", this, index, item, e);
15814             }
15815         }else{
15816             this.clearSelections();
15817         }
15818     },
15819
15820     /** @ignore */
15821     onContextMenu : function(e){
15822         var item = this.findItemFromChild(e.getTarget());
15823         if(item){
15824             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15825         }
15826     },
15827
15828     /** @ignore */
15829     onDblClick : function(e){
15830         var item = this.findItemFromChild(e.getTarget());
15831         if(item){
15832             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15833         }
15834     },
15835
15836     onItemClick : function(item, index, e)
15837     {
15838         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15839             return false;
15840         }
15841         if (this.toggleSelect) {
15842             var m = this.isSelected(item) ? 'unselect' : 'select';
15843             //Roo.log(m);
15844             var _t = this;
15845             _t[m](item, true, false);
15846             return true;
15847         }
15848         if(this.multiSelect || this.singleSelect){
15849             if(this.multiSelect && e.shiftKey && this.lastSelection){
15850                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15851             }else{
15852                 this.select(item, this.multiSelect && e.ctrlKey);
15853                 this.lastSelection = item;
15854             }
15855             
15856             if(!this.tickable){
15857                 e.preventDefault();
15858             }
15859             
15860         }
15861         return true;
15862     },
15863
15864     /**
15865      * Get the number of selected nodes.
15866      * @return {Number}
15867      */
15868     getSelectionCount : function(){
15869         return this.selections.length;
15870     },
15871
15872     /**
15873      * Get the currently selected nodes.
15874      * @return {Array} An array of HTMLElements
15875      */
15876     getSelectedNodes : function(){
15877         return this.selections;
15878     },
15879
15880     /**
15881      * Get the indexes of the selected nodes.
15882      * @return {Array}
15883      */
15884     getSelectedIndexes : function(){
15885         var indexes = [], s = this.selections;
15886         for(var i = 0, len = s.length; i < len; i++){
15887             indexes.push(s[i].nodeIndex);
15888         }
15889         return indexes;
15890     },
15891
15892     /**
15893      * Clear all selections
15894      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15895      */
15896     clearSelections : function(suppressEvent){
15897         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15898             this.cmp.elements = this.selections;
15899             this.cmp.removeClass(this.selectedClass);
15900             this.selections = [];
15901             if(!suppressEvent){
15902                 this.fireEvent("selectionchange", this, this.selections);
15903             }
15904         }
15905     },
15906
15907     /**
15908      * Returns true if the passed node is selected
15909      * @param {HTMLElement/Number} node The node or node index
15910      * @return {Boolean}
15911      */
15912     isSelected : function(node){
15913         var s = this.selections;
15914         if(s.length < 1){
15915             return false;
15916         }
15917         node = this.getNode(node);
15918         return s.indexOf(node) !== -1;
15919     },
15920
15921     /**
15922      * Selects nodes.
15923      * @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
15924      * @param {Boolean} keepExisting (optional) true to keep existing selections
15925      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15926      */
15927     select : function(nodeInfo, keepExisting, suppressEvent){
15928         if(nodeInfo instanceof Array){
15929             if(!keepExisting){
15930                 this.clearSelections(true);
15931             }
15932             for(var i = 0, len = nodeInfo.length; i < len; i++){
15933                 this.select(nodeInfo[i], true, true);
15934             }
15935             return;
15936         } 
15937         var node = this.getNode(nodeInfo);
15938         if(!node || this.isSelected(node)){
15939             return; // already selected.
15940         }
15941         if(!keepExisting){
15942             this.clearSelections(true);
15943         }
15944         
15945         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15946             Roo.fly(node).addClass(this.selectedClass);
15947             this.selections.push(node);
15948             if(!suppressEvent){
15949                 this.fireEvent("selectionchange", this, this.selections);
15950             }
15951         }
15952         
15953         
15954     },
15955       /**
15956      * Unselects nodes.
15957      * @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
15958      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15959      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15960      */
15961     unselect : function(nodeInfo, keepExisting, suppressEvent)
15962     {
15963         if(nodeInfo instanceof Array){
15964             Roo.each(this.selections, function(s) {
15965                 this.unselect(s, nodeInfo);
15966             }, this);
15967             return;
15968         }
15969         var node = this.getNode(nodeInfo);
15970         if(!node || !this.isSelected(node)){
15971             //Roo.log("not selected");
15972             return; // not selected.
15973         }
15974         // fireevent???
15975         var ns = [];
15976         Roo.each(this.selections, function(s) {
15977             if (s == node ) {
15978                 Roo.fly(node).removeClass(this.selectedClass);
15979
15980                 return;
15981             }
15982             ns.push(s);
15983         },this);
15984         
15985         this.selections= ns;
15986         this.fireEvent("selectionchange", this, this.selections);
15987     },
15988
15989     /**
15990      * Gets a template node.
15991      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15992      * @return {HTMLElement} The node or null if it wasn't found
15993      */
15994     getNode : function(nodeInfo){
15995         if(typeof nodeInfo == "string"){
15996             return document.getElementById(nodeInfo);
15997         }else if(typeof nodeInfo == "number"){
15998             return this.nodes[nodeInfo];
15999         }
16000         return nodeInfo;
16001     },
16002
16003     /**
16004      * Gets a range template nodes.
16005      * @param {Number} startIndex
16006      * @param {Number} endIndex
16007      * @return {Array} An array of nodes
16008      */
16009     getNodes : function(start, end){
16010         var ns = this.nodes;
16011         start = start || 0;
16012         end = typeof end == "undefined" ? ns.length - 1 : end;
16013         var nodes = [];
16014         if(start <= end){
16015             for(var i = start; i <= end; i++){
16016                 nodes.push(ns[i]);
16017             }
16018         } else{
16019             for(var i = start; i >= end; i--){
16020                 nodes.push(ns[i]);
16021             }
16022         }
16023         return nodes;
16024     },
16025
16026     /**
16027      * Finds the index of the passed node
16028      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16029      * @return {Number} The index of the node or -1
16030      */
16031     indexOf : function(node){
16032         node = this.getNode(node);
16033         if(typeof node.nodeIndex == "number"){
16034             return node.nodeIndex;
16035         }
16036         var ns = this.nodes;
16037         for(var i = 0, len = ns.length; i < len; i++){
16038             if(ns[i] == node){
16039                 return i;
16040             }
16041         }
16042         return -1;
16043     }
16044 });
16045 /*
16046  * - LGPL
16047  *
16048  * based on jquery fullcalendar
16049  * 
16050  */
16051
16052 Roo.bootstrap = Roo.bootstrap || {};
16053 /**
16054  * @class Roo.bootstrap.Calendar
16055  * @extends Roo.bootstrap.Component
16056  * Bootstrap Calendar class
16057  * @cfg {Boolean} loadMask (true|false) default false
16058  * @cfg {Object} header generate the user specific header of the calendar, default false
16059
16060  * @constructor
16061  * Create a new Container
16062  * @param {Object} config The config object
16063  */
16064
16065
16066
16067 Roo.bootstrap.Calendar = function(config){
16068     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16069      this.addEvents({
16070         /**
16071              * @event select
16072              * Fires when a date is selected
16073              * @param {DatePicker} this
16074              * @param {Date} date The selected date
16075              */
16076         'select': true,
16077         /**
16078              * @event monthchange
16079              * Fires when the displayed month changes 
16080              * @param {DatePicker} this
16081              * @param {Date} date The selected month
16082              */
16083         'monthchange': true,
16084         /**
16085              * @event evententer
16086              * Fires when mouse over an event
16087              * @param {Calendar} this
16088              * @param {event} Event
16089              */
16090         'evententer': true,
16091         /**
16092              * @event eventleave
16093              * Fires when the mouse leaves an
16094              * @param {Calendar} this
16095              * @param {event}
16096              */
16097         'eventleave': true,
16098         /**
16099              * @event eventclick
16100              * Fires when the mouse click an
16101              * @param {Calendar} this
16102              * @param {event}
16103              */
16104         'eventclick': true
16105         
16106     });
16107
16108 };
16109
16110 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16111     
16112      /**
16113      * @cfg {Number} startDay
16114      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16115      */
16116     startDay : 0,
16117     
16118     loadMask : false,
16119     
16120     header : false,
16121       
16122     getAutoCreate : function(){
16123         
16124         
16125         var fc_button = function(name, corner, style, content ) {
16126             return Roo.apply({},{
16127                 tag : 'span',
16128                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16129                          (corner.length ?
16130                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16131                             ''
16132                         ),
16133                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16134                 unselectable: 'on'
16135             });
16136         };
16137         
16138         var header = {};
16139         
16140         if(!this.header){
16141             header = {
16142                 tag : 'table',
16143                 cls : 'fc-header',
16144                 style : 'width:100%',
16145                 cn : [
16146                     {
16147                         tag: 'tr',
16148                         cn : [
16149                             {
16150                                 tag : 'td',
16151                                 cls : 'fc-header-left',
16152                                 cn : [
16153                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16154                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16155                                     { tag: 'span', cls: 'fc-header-space' },
16156                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16157
16158
16159                                 ]
16160                             },
16161
16162                             {
16163                                 tag : 'td',
16164                                 cls : 'fc-header-center',
16165                                 cn : [
16166                                     {
16167                                         tag: 'span',
16168                                         cls: 'fc-header-title',
16169                                         cn : {
16170                                             tag: 'H2',
16171                                             html : 'month / year'
16172                                         }
16173                                     }
16174
16175                                 ]
16176                             },
16177                             {
16178                                 tag : 'td',
16179                                 cls : 'fc-header-right',
16180                                 cn : [
16181                               /*      fc_button('month', 'left', '', 'month' ),
16182                                     fc_button('week', '', '', 'week' ),
16183                                     fc_button('day', 'right', '', 'day' )
16184                                 */    
16185
16186                                 ]
16187                             }
16188
16189                         ]
16190                     }
16191                 ]
16192             };
16193         }
16194         
16195         header = this.header;
16196         
16197        
16198         var cal_heads = function() {
16199             var ret = [];
16200             // fixme - handle this.
16201             
16202             for (var i =0; i < Date.dayNames.length; i++) {
16203                 var d = Date.dayNames[i];
16204                 ret.push({
16205                     tag: 'th',
16206                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16207                     html : d.substring(0,3)
16208                 });
16209                 
16210             }
16211             ret[0].cls += ' fc-first';
16212             ret[6].cls += ' fc-last';
16213             return ret;
16214         };
16215         var cal_cell = function(n) {
16216             return  {
16217                 tag: 'td',
16218                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16219                 cn : [
16220                     {
16221                         cn : [
16222                             {
16223                                 cls: 'fc-day-number',
16224                                 html: 'D'
16225                             },
16226                             {
16227                                 cls: 'fc-day-content',
16228                              
16229                                 cn : [
16230                                      {
16231                                         style: 'position: relative;' // height: 17px;
16232                                     }
16233                                 ]
16234                             }
16235                             
16236                             
16237                         ]
16238                     }
16239                 ]
16240                 
16241             }
16242         };
16243         var cal_rows = function() {
16244             
16245             var ret = [];
16246             for (var r = 0; r < 6; r++) {
16247                 var row= {
16248                     tag : 'tr',
16249                     cls : 'fc-week',
16250                     cn : []
16251                 };
16252                 
16253                 for (var i =0; i < Date.dayNames.length; i++) {
16254                     var d = Date.dayNames[i];
16255                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16256
16257                 }
16258                 row.cn[0].cls+=' fc-first';
16259                 row.cn[0].cn[0].style = 'min-height:90px';
16260                 row.cn[6].cls+=' fc-last';
16261                 ret.push(row);
16262                 
16263             }
16264             ret[0].cls += ' fc-first';
16265             ret[4].cls += ' fc-prev-last';
16266             ret[5].cls += ' fc-last';
16267             return ret;
16268             
16269         };
16270         
16271         var cal_table = {
16272             tag: 'table',
16273             cls: 'fc-border-separate',
16274             style : 'width:100%',
16275             cellspacing  : 0,
16276             cn : [
16277                 { 
16278                     tag: 'thead',
16279                     cn : [
16280                         { 
16281                             tag: 'tr',
16282                             cls : 'fc-first fc-last',
16283                             cn : cal_heads()
16284                         }
16285                     ]
16286                 },
16287                 { 
16288                     tag: 'tbody',
16289                     cn : cal_rows()
16290                 }
16291                   
16292             ]
16293         };
16294          
16295          var cfg = {
16296             cls : 'fc fc-ltr',
16297             cn : [
16298                 header,
16299                 {
16300                     cls : 'fc-content',
16301                     style : "position: relative;",
16302                     cn : [
16303                         {
16304                             cls : 'fc-view fc-view-month fc-grid',
16305                             style : 'position: relative',
16306                             unselectable : 'on',
16307                             cn : [
16308                                 {
16309                                     cls : 'fc-event-container',
16310                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16311                                 },
16312                                 cal_table
16313                             ]
16314                         }
16315                     ]
16316     
16317                 }
16318            ] 
16319             
16320         };
16321         
16322          
16323         
16324         return cfg;
16325     },
16326     
16327     
16328     initEvents : function()
16329     {
16330         if(!this.store){
16331             throw "can not find store for calendar";
16332         }
16333         
16334         var mark = {
16335             tag: "div",
16336             cls:"x-dlg-mask",
16337             style: "text-align:center",
16338             cn: [
16339                 {
16340                     tag: "div",
16341                     style: "background-color:white;width:50%;margin:250 auto",
16342                     cn: [
16343                         {
16344                             tag: "img",
16345                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16346                         },
16347                         {
16348                             tag: "span",
16349                             html: "Loading"
16350                         }
16351                         
16352                     ]
16353                 }
16354             ]
16355         };
16356         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16357         
16358         var size = this.el.select('.fc-content', true).first().getSize();
16359         this.maskEl.setSize(size.width, size.height);
16360         this.maskEl.enableDisplayMode("block");
16361         if(!this.loadMask){
16362             this.maskEl.hide();
16363         }
16364         
16365         this.store = Roo.factory(this.store, Roo.data);
16366         this.store.on('load', this.onLoad, this);
16367         this.store.on('beforeload', this.onBeforeLoad, this);
16368         
16369         this.resize();
16370         
16371         this.cells = this.el.select('.fc-day',true);
16372         //Roo.log(this.cells);
16373         this.textNodes = this.el.query('.fc-day-number');
16374         this.cells.addClassOnOver('fc-state-hover');
16375         
16376         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16377         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16378         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16379         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16380         
16381         this.on('monthchange', this.onMonthChange, this);
16382         
16383         this.update(new Date().clearTime());
16384     },
16385     
16386     resize : function() {
16387         var sz  = this.el.getSize();
16388         
16389         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16390         this.el.select('.fc-day-content div',true).setHeight(34);
16391     },
16392     
16393     
16394     // private
16395     showPrevMonth : function(e){
16396         this.update(this.activeDate.add("mo", -1));
16397     },
16398     showToday : function(e){
16399         this.update(new Date().clearTime());
16400     },
16401     // private
16402     showNextMonth : function(e){
16403         this.update(this.activeDate.add("mo", 1));
16404     },
16405
16406     // private
16407     showPrevYear : function(){
16408         this.update(this.activeDate.add("y", -1));
16409     },
16410
16411     // private
16412     showNextYear : function(){
16413         this.update(this.activeDate.add("y", 1));
16414     },
16415
16416     
16417    // private
16418     update : function(date)
16419     {
16420         var vd = this.activeDate;
16421         this.activeDate = date;
16422 //        if(vd && this.el){
16423 //            var t = date.getTime();
16424 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16425 //                Roo.log('using add remove');
16426 //                
16427 //                this.fireEvent('monthchange', this, date);
16428 //                
16429 //                this.cells.removeClass("fc-state-highlight");
16430 //                this.cells.each(function(c){
16431 //                   if(c.dateValue == t){
16432 //                       c.addClass("fc-state-highlight");
16433 //                       setTimeout(function(){
16434 //                            try{c.dom.firstChild.focus();}catch(e){}
16435 //                       }, 50);
16436 //                       return false;
16437 //                   }
16438 //                   return true;
16439 //                });
16440 //                return;
16441 //            }
16442 //        }
16443         
16444         var days = date.getDaysInMonth();
16445         
16446         var firstOfMonth = date.getFirstDateOfMonth();
16447         var startingPos = firstOfMonth.getDay()-this.startDay;
16448         
16449         if(startingPos < this.startDay){
16450             startingPos += 7;
16451         }
16452         
16453         var pm = date.add(Date.MONTH, -1);
16454         var prevStart = pm.getDaysInMonth()-startingPos;
16455 //        
16456         this.cells = this.el.select('.fc-day',true);
16457         this.textNodes = this.el.query('.fc-day-number');
16458         this.cells.addClassOnOver('fc-state-hover');
16459         
16460         var cells = this.cells.elements;
16461         var textEls = this.textNodes;
16462         
16463         Roo.each(cells, function(cell){
16464             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16465         });
16466         
16467         days += startingPos;
16468
16469         // convert everything to numbers so it's fast
16470         var day = 86400000;
16471         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16472         //Roo.log(d);
16473         //Roo.log(pm);
16474         //Roo.log(prevStart);
16475         
16476         var today = new Date().clearTime().getTime();
16477         var sel = date.clearTime().getTime();
16478         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16479         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16480         var ddMatch = this.disabledDatesRE;
16481         var ddText = this.disabledDatesText;
16482         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16483         var ddaysText = this.disabledDaysText;
16484         var format = this.format;
16485         
16486         var setCellClass = function(cal, cell){
16487             cell.row = 0;
16488             cell.events = [];
16489             cell.more = [];
16490             //Roo.log('set Cell Class');
16491             cell.title = "";
16492             var t = d.getTime();
16493             
16494             //Roo.log(d);
16495             
16496             cell.dateValue = t;
16497             if(t == today){
16498                 cell.className += " fc-today";
16499                 cell.className += " fc-state-highlight";
16500                 cell.title = cal.todayText;
16501             }
16502             if(t == sel){
16503                 // disable highlight in other month..
16504                 //cell.className += " fc-state-highlight";
16505                 
16506             }
16507             // disabling
16508             if(t < min) {
16509                 cell.className = " fc-state-disabled";
16510                 cell.title = cal.minText;
16511                 return;
16512             }
16513             if(t > max) {
16514                 cell.className = " fc-state-disabled";
16515                 cell.title = cal.maxText;
16516                 return;
16517             }
16518             if(ddays){
16519                 if(ddays.indexOf(d.getDay()) != -1){
16520                     cell.title = ddaysText;
16521                     cell.className = " fc-state-disabled";
16522                 }
16523             }
16524             if(ddMatch && format){
16525                 var fvalue = d.dateFormat(format);
16526                 if(ddMatch.test(fvalue)){
16527                     cell.title = ddText.replace("%0", fvalue);
16528                     cell.className = " fc-state-disabled";
16529                 }
16530             }
16531             
16532             if (!cell.initialClassName) {
16533                 cell.initialClassName = cell.dom.className;
16534             }
16535             
16536             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16537         };
16538
16539         var i = 0;
16540         
16541         for(; i < startingPos; i++) {
16542             textEls[i].innerHTML = (++prevStart);
16543             d.setDate(d.getDate()+1);
16544             
16545             cells[i].className = "fc-past fc-other-month";
16546             setCellClass(this, cells[i]);
16547         }
16548         
16549         var intDay = 0;
16550         
16551         for(; i < days; i++){
16552             intDay = i - startingPos + 1;
16553             textEls[i].innerHTML = (intDay);
16554             d.setDate(d.getDate()+1);
16555             
16556             cells[i].className = ''; // "x-date-active";
16557             setCellClass(this, cells[i]);
16558         }
16559         var extraDays = 0;
16560         
16561         for(; i < 42; i++) {
16562             textEls[i].innerHTML = (++extraDays);
16563             d.setDate(d.getDate()+1);
16564             
16565             cells[i].className = "fc-future fc-other-month";
16566             setCellClass(this, cells[i]);
16567         }
16568         
16569         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16570         
16571         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16572         
16573         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16574         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16575         
16576         if(totalRows != 6){
16577             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16578             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16579         }
16580         
16581         this.fireEvent('monthchange', this, date);
16582         
16583         
16584         /*
16585         if(!this.internalRender){
16586             var main = this.el.dom.firstChild;
16587             var w = main.offsetWidth;
16588             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16589             Roo.fly(main).setWidth(w);
16590             this.internalRender = true;
16591             // opera does not respect the auto grow header center column
16592             // then, after it gets a width opera refuses to recalculate
16593             // without a second pass
16594             if(Roo.isOpera && !this.secondPass){
16595                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16596                 this.secondPass = true;
16597                 this.update.defer(10, this, [date]);
16598             }
16599         }
16600         */
16601         
16602     },
16603     
16604     findCell : function(dt) {
16605         dt = dt.clearTime().getTime();
16606         var ret = false;
16607         this.cells.each(function(c){
16608             //Roo.log("check " +c.dateValue + '?=' + dt);
16609             if(c.dateValue == dt){
16610                 ret = c;
16611                 return false;
16612             }
16613             return true;
16614         });
16615         
16616         return ret;
16617     },
16618     
16619     findCells : function(ev) {
16620         var s = ev.start.clone().clearTime().getTime();
16621        // Roo.log(s);
16622         var e= ev.end.clone().clearTime().getTime();
16623        // Roo.log(e);
16624         var ret = [];
16625         this.cells.each(function(c){
16626              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16627             
16628             if(c.dateValue > e){
16629                 return ;
16630             }
16631             if(c.dateValue < s){
16632                 return ;
16633             }
16634             ret.push(c);
16635         });
16636         
16637         return ret;    
16638     },
16639     
16640 //    findBestRow: function(cells)
16641 //    {
16642 //        var ret = 0;
16643 //        
16644 //        for (var i =0 ; i < cells.length;i++) {
16645 //            ret  = Math.max(cells[i].rows || 0,ret);
16646 //        }
16647 //        return ret;
16648 //        
16649 //    },
16650     
16651     
16652     addItem : function(ev)
16653     {
16654         // look for vertical location slot in
16655         var cells = this.findCells(ev);
16656         
16657 //        ev.row = this.findBestRow(cells);
16658         
16659         // work out the location.
16660         
16661         var crow = false;
16662         var rows = [];
16663         for(var i =0; i < cells.length; i++) {
16664             
16665             cells[i].row = cells[0].row;
16666             
16667             if(i == 0){
16668                 cells[i].row = cells[i].row + 1;
16669             }
16670             
16671             if (!crow) {
16672                 crow = {
16673                     start : cells[i],
16674                     end :  cells[i]
16675                 };
16676                 continue;
16677             }
16678             if (crow.start.getY() == cells[i].getY()) {
16679                 // on same row.
16680                 crow.end = cells[i];
16681                 continue;
16682             }
16683             // different row.
16684             rows.push(crow);
16685             crow = {
16686                 start: cells[i],
16687                 end : cells[i]
16688             };
16689             
16690         }
16691         
16692         rows.push(crow);
16693         ev.els = [];
16694         ev.rows = rows;
16695         ev.cells = cells;
16696         
16697         cells[0].events.push(ev);
16698         
16699         this.calevents.push(ev);
16700     },
16701     
16702     clearEvents: function() {
16703         
16704         if(!this.calevents){
16705             return;
16706         }
16707         
16708         Roo.each(this.cells.elements, function(c){
16709             c.row = 0;
16710             c.events = [];
16711             c.more = [];
16712         });
16713         
16714         Roo.each(this.calevents, function(e) {
16715             Roo.each(e.els, function(el) {
16716                 el.un('mouseenter' ,this.onEventEnter, this);
16717                 el.un('mouseleave' ,this.onEventLeave, this);
16718                 el.remove();
16719             },this);
16720         },this);
16721         
16722         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16723             e.remove();
16724         });
16725         
16726     },
16727     
16728     renderEvents: function()
16729     {   
16730         var _this = this;
16731         
16732         this.cells.each(function(c) {
16733             
16734             if(c.row < 5){
16735                 return;
16736             }
16737             
16738             var ev = c.events;
16739             
16740             var r = 4;
16741             if(c.row != c.events.length){
16742                 r = 4 - (4 - (c.row - c.events.length));
16743             }
16744             
16745             c.events = ev.slice(0, r);
16746             c.more = ev.slice(r);
16747             
16748             if(c.more.length && c.more.length == 1){
16749                 c.events.push(c.more.pop());
16750             }
16751             
16752             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16753             
16754         });
16755             
16756         this.cells.each(function(c) {
16757             
16758             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16759             
16760             
16761             for (var e = 0; e < c.events.length; e++){
16762                 var ev = c.events[e];
16763                 var rows = ev.rows;
16764                 
16765                 for(var i = 0; i < rows.length; i++) {
16766                 
16767                     // how many rows should it span..
16768
16769                     var  cfg = {
16770                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16771                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16772
16773                         unselectable : "on",
16774                         cn : [
16775                             {
16776                                 cls: 'fc-event-inner',
16777                                 cn : [
16778     //                                {
16779     //                                  tag:'span',
16780     //                                  cls: 'fc-event-time',
16781     //                                  html : cells.length > 1 ? '' : ev.time
16782     //                                },
16783                                     {
16784                                       tag:'span',
16785                                       cls: 'fc-event-title',
16786                                       html : String.format('{0}', ev.title)
16787                                     }
16788
16789
16790                                 ]
16791                             },
16792                             {
16793                                 cls: 'ui-resizable-handle ui-resizable-e',
16794                                 html : '&nbsp;&nbsp;&nbsp'
16795                             }
16796
16797                         ]
16798                     };
16799
16800                     if (i == 0) {
16801                         cfg.cls += ' fc-event-start';
16802                     }
16803                     if ((i+1) == rows.length) {
16804                         cfg.cls += ' fc-event-end';
16805                     }
16806
16807                     var ctr = _this.el.select('.fc-event-container',true).first();
16808                     var cg = ctr.createChild(cfg);
16809
16810                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16811                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16812
16813                     var r = (c.more.length) ? 1 : 0;
16814                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16815                     cg.setWidth(ebox.right - sbox.x -2);
16816
16817                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16818                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16819                     cg.on('click', _this.onEventClick, _this, ev);
16820
16821                     ev.els.push(cg);
16822                     
16823                 }
16824                 
16825             }
16826             
16827             
16828             if(c.more.length){
16829                 var  cfg = {
16830                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16831                     style : 'position: absolute',
16832                     unselectable : "on",
16833                     cn : [
16834                         {
16835                             cls: 'fc-event-inner',
16836                             cn : [
16837                                 {
16838                                   tag:'span',
16839                                   cls: 'fc-event-title',
16840                                   html : 'More'
16841                                 }
16842
16843
16844                             ]
16845                         },
16846                         {
16847                             cls: 'ui-resizable-handle ui-resizable-e',
16848                             html : '&nbsp;&nbsp;&nbsp'
16849                         }
16850
16851                     ]
16852                 };
16853
16854                 var ctr = _this.el.select('.fc-event-container',true).first();
16855                 var cg = ctr.createChild(cfg);
16856
16857                 var sbox = c.select('.fc-day-content',true).first().getBox();
16858                 var ebox = c.select('.fc-day-content',true).first().getBox();
16859                 //Roo.log(cg);
16860                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16861                 cg.setWidth(ebox.right - sbox.x -2);
16862
16863                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16864                 
16865             }
16866             
16867         });
16868         
16869         
16870         
16871     },
16872     
16873     onEventEnter: function (e, el,event,d) {
16874         this.fireEvent('evententer', this, el, event);
16875     },
16876     
16877     onEventLeave: function (e, el,event,d) {
16878         this.fireEvent('eventleave', this, el, event);
16879     },
16880     
16881     onEventClick: function (e, el,event,d) {
16882         this.fireEvent('eventclick', this, el, event);
16883     },
16884     
16885     onMonthChange: function () {
16886         this.store.load();
16887     },
16888     
16889     onMoreEventClick: function(e, el, more)
16890     {
16891         var _this = this;
16892         
16893         this.calpopover.placement = 'right';
16894         this.calpopover.setTitle('More');
16895         
16896         this.calpopover.setContent('');
16897         
16898         var ctr = this.calpopover.el.select('.popover-content', true).first();
16899         
16900         Roo.each(more, function(m){
16901             var cfg = {
16902                 cls : 'fc-event-hori fc-event-draggable',
16903                 html : m.title
16904             };
16905             var cg = ctr.createChild(cfg);
16906             
16907             cg.on('click', _this.onEventClick, _this, m);
16908         });
16909         
16910         this.calpopover.show(el);
16911         
16912         
16913     },
16914     
16915     onLoad: function () 
16916     {   
16917         this.calevents = [];
16918         var cal = this;
16919         
16920         if(this.store.getCount() > 0){
16921             this.store.data.each(function(d){
16922                cal.addItem({
16923                     id : d.data.id,
16924                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16925                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16926                     time : d.data.start_time,
16927                     title : d.data.title,
16928                     description : d.data.description,
16929                     venue : d.data.venue
16930                 });
16931             });
16932         }
16933         
16934         this.renderEvents();
16935         
16936         if(this.calevents.length && this.loadMask){
16937             this.maskEl.hide();
16938         }
16939     },
16940     
16941     onBeforeLoad: function()
16942     {
16943         this.clearEvents();
16944         if(this.loadMask){
16945             this.maskEl.show();
16946         }
16947     }
16948 });
16949
16950  
16951  /*
16952  * - LGPL
16953  *
16954  * element
16955  * 
16956  */
16957
16958 /**
16959  * @class Roo.bootstrap.Popover
16960  * @extends Roo.bootstrap.Component
16961  * Bootstrap Popover class
16962  * @cfg {String} html contents of the popover   (or false to use children..)
16963  * @cfg {String} title of popover (or false to hide)
16964  * @cfg {String} placement how it is placed
16965  * @cfg {String} trigger click || hover (or false to trigger manually)
16966  * @cfg {String} over what (parent or false to trigger manually.)
16967  * @cfg {Number} delay - delay before showing
16968  
16969  * @constructor
16970  * Create a new Popover
16971  * @param {Object} config The config object
16972  */
16973
16974 Roo.bootstrap.Popover = function(config){
16975     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16976     
16977     this.addEvents({
16978         // raw events
16979          /**
16980          * @event show
16981          * After the popover show
16982          * 
16983          * @param {Roo.bootstrap.Popover} this
16984          */
16985         "show" : true,
16986         /**
16987          * @event hide
16988          * After the popover hide
16989          * 
16990          * @param {Roo.bootstrap.Popover} this
16991          */
16992         "hide" : true
16993     });
16994 };
16995
16996 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16997     
16998     title: 'Fill in a title',
16999     html: false,
17000     
17001     placement : 'right',
17002     trigger : 'hover', // hover
17003     
17004     delay : 0,
17005     
17006     over: 'parent',
17007     
17008     can_build_overlaid : false,
17009     
17010     getChildContainer : function()
17011     {
17012         return this.el.select('.popover-content',true).first();
17013     },
17014     
17015     getAutoCreate : function(){
17016          
17017         var cfg = {
17018            cls : 'popover roo-dynamic',
17019            style: 'display:block',
17020            cn : [
17021                 {
17022                     cls : 'arrow'
17023                 },
17024                 {
17025                     cls : 'popover-inner',
17026                     cn : [
17027                         {
17028                             tag: 'h3',
17029                             cls: 'popover-title',
17030                             html : this.title
17031                         },
17032                         {
17033                             cls : 'popover-content',
17034                             html : this.html
17035                         }
17036                     ]
17037                     
17038                 }
17039            ]
17040         };
17041         
17042         return cfg;
17043     },
17044     setTitle: function(str)
17045     {
17046         this.title = str;
17047         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17048     },
17049     setContent: function(str)
17050     {
17051         this.html = str;
17052         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17053     },
17054     // as it get's added to the bottom of the page.
17055     onRender : function(ct, position)
17056     {
17057         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17058         if(!this.el){
17059             var cfg = Roo.apply({},  this.getAutoCreate());
17060             cfg.id = Roo.id();
17061             
17062             if (this.cls) {
17063                 cfg.cls += ' ' + this.cls;
17064             }
17065             if (this.style) {
17066                 cfg.style = this.style;
17067             }
17068             //Roo.log("adding to ");
17069             this.el = Roo.get(document.body).createChild(cfg, position);
17070 //            Roo.log(this.el);
17071         }
17072         this.initEvents();
17073     },
17074     
17075     initEvents : function()
17076     {
17077         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17078         this.el.enableDisplayMode('block');
17079         this.el.hide();
17080         if (this.over === false) {
17081             return; 
17082         }
17083         if (this.triggers === false) {
17084             return;
17085         }
17086         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17087         var triggers = this.trigger ? this.trigger.split(' ') : [];
17088         Roo.each(triggers, function(trigger) {
17089         
17090             if (trigger == 'click') {
17091                 on_el.on('click', this.toggle, this);
17092             } else if (trigger != 'manual') {
17093                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17094                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17095       
17096                 on_el.on(eventIn  ,this.enter, this);
17097                 on_el.on(eventOut, this.leave, this);
17098             }
17099         }, this);
17100         
17101     },
17102     
17103     
17104     // private
17105     timeout : null,
17106     hoverState : null,
17107     
17108     toggle : function () {
17109         this.hoverState == 'in' ? this.leave() : this.enter();
17110     },
17111     
17112     enter : function () {
17113         
17114         clearTimeout(this.timeout);
17115     
17116         this.hoverState = 'in';
17117     
17118         if (!this.delay || !this.delay.show) {
17119             this.show();
17120             return;
17121         }
17122         var _t = this;
17123         this.timeout = setTimeout(function () {
17124             if (_t.hoverState == 'in') {
17125                 _t.show();
17126             }
17127         }, this.delay.show)
17128     },
17129     
17130     leave : function() {
17131         clearTimeout(this.timeout);
17132     
17133         this.hoverState = 'out';
17134     
17135         if (!this.delay || !this.delay.hide) {
17136             this.hide();
17137             return;
17138         }
17139         var _t = this;
17140         this.timeout = setTimeout(function () {
17141             if (_t.hoverState == 'out') {
17142                 _t.hide();
17143             }
17144         }, this.delay.hide)
17145     },
17146     
17147     show : function (on_el)
17148     {
17149         if (!on_el) {
17150             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17151         }
17152         
17153         // set content.
17154         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17155         if (this.html !== false) {
17156             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17157         }
17158         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17159         if (!this.title.length) {
17160             this.el.select('.popover-title',true).hide();
17161         }
17162         
17163         var placement = typeof this.placement == 'function' ?
17164             this.placement.call(this, this.el, on_el) :
17165             this.placement;
17166             
17167         var autoToken = /\s?auto?\s?/i;
17168         var autoPlace = autoToken.test(placement);
17169         if (autoPlace) {
17170             placement = placement.replace(autoToken, '') || 'top';
17171         }
17172         
17173         //this.el.detach()
17174         //this.el.setXY([0,0]);
17175         this.el.show();
17176         this.el.dom.style.display='block';
17177         this.el.addClass(placement);
17178         
17179         //this.el.appendTo(on_el);
17180         
17181         var p = this.getPosition();
17182         var box = this.el.getBox();
17183         
17184         if (autoPlace) {
17185             // fixme..
17186         }
17187         var align = Roo.bootstrap.Popover.alignment[placement];
17188         this.el.alignTo(on_el, align[0],align[1]);
17189         //var arrow = this.el.select('.arrow',true).first();
17190         //arrow.set(align[2], 
17191         
17192         this.el.addClass('in');
17193         
17194         
17195         if (this.el.hasClass('fade')) {
17196             // fade it?
17197         }
17198         
17199         this.hoverState = 'in';
17200         
17201         this.fireEvent('show', this);
17202         
17203     },
17204     hide : function()
17205     {
17206         this.el.setXY([0,0]);
17207         this.el.removeClass('in');
17208         this.el.hide();
17209         this.hoverState = null;
17210         
17211         this.fireEvent('hide', this);
17212     }
17213     
17214 });
17215
17216 Roo.bootstrap.Popover.alignment = {
17217     'left' : ['r-l', [-10,0], 'right'],
17218     'right' : ['l-r', [10,0], 'left'],
17219     'bottom' : ['t-b', [0,10], 'top'],
17220     'top' : [ 'b-t', [0,-10], 'bottom']
17221 };
17222
17223  /*
17224  * - LGPL
17225  *
17226  * Progress
17227  * 
17228  */
17229
17230 /**
17231  * @class Roo.bootstrap.Progress
17232  * @extends Roo.bootstrap.Component
17233  * Bootstrap Progress class
17234  * @cfg {Boolean} striped striped of the progress bar
17235  * @cfg {Boolean} active animated of the progress bar
17236  * 
17237  * 
17238  * @constructor
17239  * Create a new Progress
17240  * @param {Object} config The config object
17241  */
17242
17243 Roo.bootstrap.Progress = function(config){
17244     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17245 };
17246
17247 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17248     
17249     striped : false,
17250     active: false,
17251     
17252     getAutoCreate : function(){
17253         var cfg = {
17254             tag: 'div',
17255             cls: 'progress'
17256         };
17257         
17258         
17259         if(this.striped){
17260             cfg.cls += ' progress-striped';
17261         }
17262       
17263         if(this.active){
17264             cfg.cls += ' active';
17265         }
17266         
17267         
17268         return cfg;
17269     }
17270    
17271 });
17272
17273  
17274
17275  /*
17276  * - LGPL
17277  *
17278  * ProgressBar
17279  * 
17280  */
17281
17282 /**
17283  * @class Roo.bootstrap.ProgressBar
17284  * @extends Roo.bootstrap.Component
17285  * Bootstrap ProgressBar class
17286  * @cfg {Number} aria_valuenow aria-value now
17287  * @cfg {Number} aria_valuemin aria-value min
17288  * @cfg {Number} aria_valuemax aria-value max
17289  * @cfg {String} label label for the progress bar
17290  * @cfg {String} panel (success | info | warning | danger )
17291  * @cfg {String} role role of the progress bar
17292  * @cfg {String} sr_only text
17293  * 
17294  * 
17295  * @constructor
17296  * Create a new ProgressBar
17297  * @param {Object} config The config object
17298  */
17299
17300 Roo.bootstrap.ProgressBar = function(config){
17301     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17302 };
17303
17304 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17305     
17306     aria_valuenow : 0,
17307     aria_valuemin : 0,
17308     aria_valuemax : 100,
17309     label : false,
17310     panel : false,
17311     role : false,
17312     sr_only: false,
17313     
17314     getAutoCreate : function()
17315     {
17316         
17317         var cfg = {
17318             tag: 'div',
17319             cls: 'progress-bar',
17320             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17321         };
17322         
17323         if(this.sr_only){
17324             cfg.cn = {
17325                 tag: 'span',
17326                 cls: 'sr-only',
17327                 html: this.sr_only
17328             }
17329         }
17330         
17331         if(this.role){
17332             cfg.role = this.role;
17333         }
17334         
17335         if(this.aria_valuenow){
17336             cfg['aria-valuenow'] = this.aria_valuenow;
17337         }
17338         
17339         if(this.aria_valuemin){
17340             cfg['aria-valuemin'] = this.aria_valuemin;
17341         }
17342         
17343         if(this.aria_valuemax){
17344             cfg['aria-valuemax'] = this.aria_valuemax;
17345         }
17346         
17347         if(this.label && !this.sr_only){
17348             cfg.html = this.label;
17349         }
17350         
17351         if(this.panel){
17352             cfg.cls += ' progress-bar-' + this.panel;
17353         }
17354         
17355         return cfg;
17356     },
17357     
17358     update : function(aria_valuenow)
17359     {
17360         this.aria_valuenow = aria_valuenow;
17361         
17362         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17363     }
17364    
17365 });
17366
17367  
17368
17369  /*
17370  * - LGPL
17371  *
17372  * column
17373  * 
17374  */
17375
17376 /**
17377  * @class Roo.bootstrap.TabGroup
17378  * @extends Roo.bootstrap.Column
17379  * Bootstrap Column class
17380  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17381  * @cfg {Boolean} carousel true to make the group behave like a carousel
17382  * @cfg {Boolean} bullets show bullets for the panels
17383  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17384  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17385  * @cfg {Boolean} showarrow (true|false) show arrow default true
17386  * 
17387  * @constructor
17388  * Create a new TabGroup
17389  * @param {Object} config The config object
17390  */
17391
17392 Roo.bootstrap.TabGroup = function(config){
17393     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17394     if (!this.navId) {
17395         this.navId = Roo.id();
17396     }
17397     this.tabs = [];
17398     Roo.bootstrap.TabGroup.register(this);
17399     
17400 };
17401
17402 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17403     
17404     carousel : false,
17405     transition : false,
17406     bullets : 0,
17407     timer : 0,
17408     autoslide : false,
17409     slideFn : false,
17410     slideOnTouch : false,
17411     showarrow : true,
17412     
17413     getAutoCreate : function()
17414     {
17415         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17416         
17417         cfg.cls += ' tab-content';
17418         
17419         if (this.carousel) {
17420             cfg.cls += ' carousel slide';
17421             
17422             cfg.cn = [{
17423                cls : 'carousel-inner',
17424                cn : []
17425             }];
17426         
17427             if(this.bullets  && !Roo.isTouch){
17428                 
17429                 var bullets = {
17430                     cls : 'carousel-bullets',
17431                     cn : []
17432                 };
17433                
17434                 if(this.bullets_cls){
17435                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17436                 }
17437                 
17438                 bullets.cn.push({
17439                     cls : 'clear'
17440                 });
17441                 
17442                 cfg.cn[0].cn.push(bullets);
17443             }
17444             
17445             if(this.showarrow){
17446                 cfg.cn[0].cn.push({
17447                     tag : 'div',
17448                     class : 'carousel-arrow',
17449                     cn : [
17450                         {
17451                             tag : 'div',
17452                             class : 'carousel-prev',
17453                             cn : [
17454                                 {
17455                                     tag : 'i',
17456                                     class : 'fa fa-chevron-left'
17457                                 }
17458                             ]
17459                         },
17460                         {
17461                             tag : 'div',
17462                             class : 'carousel-next',
17463                             cn : [
17464                                 {
17465                                     tag : 'i',
17466                                     class : 'fa fa-chevron-right'
17467                                 }
17468                             ]
17469                         }
17470                     ]
17471                 });
17472             }
17473             
17474         }
17475         
17476         return cfg;
17477     },
17478     
17479     initEvents:  function()
17480     {
17481 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17482 //            this.el.on("touchstart", this.onTouchStart, this);
17483 //        }
17484         
17485         if(this.autoslide){
17486             var _this = this;
17487             
17488             this.slideFn = window.setInterval(function() {
17489                 _this.showPanelNext();
17490             }, this.timer);
17491         }
17492         
17493         if(this.showarrow){
17494             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17495             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17496         }
17497         
17498         
17499     },
17500     
17501 //    onTouchStart : function(e, el, o)
17502 //    {
17503 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17504 //            return;
17505 //        }
17506 //        
17507 //        this.showPanelNext();
17508 //    },
17509     
17510     
17511     getChildContainer : function()
17512     {
17513         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17514     },
17515     
17516     /**
17517     * register a Navigation item
17518     * @param {Roo.bootstrap.NavItem} the navitem to add
17519     */
17520     register : function(item)
17521     {
17522         this.tabs.push( item);
17523         item.navId = this.navId; // not really needed..
17524         this.addBullet();
17525     
17526     },
17527     
17528     getActivePanel : function()
17529     {
17530         var r = false;
17531         Roo.each(this.tabs, function(t) {
17532             if (t.active) {
17533                 r = t;
17534                 return false;
17535             }
17536             return null;
17537         });
17538         return r;
17539         
17540     },
17541     getPanelByName : function(n)
17542     {
17543         var r = false;
17544         Roo.each(this.tabs, function(t) {
17545             if (t.tabId == n) {
17546                 r = t;
17547                 return false;
17548             }
17549             return null;
17550         });
17551         return r;
17552     },
17553     indexOfPanel : function(p)
17554     {
17555         var r = false;
17556         Roo.each(this.tabs, function(t,i) {
17557             if (t.tabId == p.tabId) {
17558                 r = i;
17559                 return false;
17560             }
17561             return null;
17562         });
17563         return r;
17564     },
17565     /**
17566      * show a specific panel
17567      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17568      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17569      */
17570     showPanel : function (pan)
17571     {
17572         if(this.transition || typeof(pan) == 'undefined'){
17573             Roo.log("waiting for the transitionend");
17574             return;
17575         }
17576         
17577         if (typeof(pan) == 'number') {
17578             pan = this.tabs[pan];
17579         }
17580         
17581         if (typeof(pan) == 'string') {
17582             pan = this.getPanelByName(pan);
17583         }
17584         
17585         var cur = this.getActivePanel();
17586         
17587         if(!pan || !cur){
17588             Roo.log('pan or acitve pan is undefined');
17589             return false;
17590         }
17591         
17592         if (pan.tabId == this.getActivePanel().tabId) {
17593             return true;
17594         }
17595         
17596         if (false === cur.fireEvent('beforedeactivate')) {
17597             return false;
17598         }
17599         
17600         if(this.bullets > 0 && !Roo.isTouch){
17601             this.setActiveBullet(this.indexOfPanel(pan));
17602         }
17603         
17604         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17605             
17606             this.transition = true;
17607             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17608             var lr = dir == 'next' ? 'left' : 'right';
17609             pan.el.addClass(dir); // or prev
17610             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17611             cur.el.addClass(lr); // or right
17612             pan.el.addClass(lr);
17613             
17614             var _this = this;
17615             cur.el.on('transitionend', function() {
17616                 Roo.log("trans end?");
17617                 
17618                 pan.el.removeClass([lr,dir]);
17619                 pan.setActive(true);
17620                 
17621                 cur.el.removeClass([lr]);
17622                 cur.setActive(false);
17623                 
17624                 _this.transition = false;
17625                 
17626             }, this, { single:  true } );
17627             
17628             return true;
17629         }
17630         
17631         cur.setActive(false);
17632         pan.setActive(true);
17633         
17634         return true;
17635         
17636     },
17637     showPanelNext : function()
17638     {
17639         var i = this.indexOfPanel(this.getActivePanel());
17640         
17641         if (i >= this.tabs.length - 1 && !this.autoslide) {
17642             return;
17643         }
17644         
17645         if (i >= this.tabs.length - 1 && this.autoslide) {
17646             i = -1;
17647         }
17648         
17649         this.showPanel(this.tabs[i+1]);
17650     },
17651     
17652     showPanelPrev : function()
17653     {
17654         var i = this.indexOfPanel(this.getActivePanel());
17655         
17656         if (i  < 1 && !this.autoslide) {
17657             return;
17658         }
17659         
17660         if (i < 1 && this.autoslide) {
17661             i = this.tabs.length;
17662         }
17663         
17664         this.showPanel(this.tabs[i-1]);
17665     },
17666     
17667     
17668     addBullet: function()
17669     {
17670         if(!this.bullets || Roo.isTouch){
17671             return;
17672         }
17673         var ctr = this.el.select('.carousel-bullets',true).first();
17674         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17675         var bullet = ctr.createChild({
17676             cls : 'bullet bullet-' + i
17677         },ctr.dom.lastChild);
17678         
17679         
17680         var _this = this;
17681         
17682         bullet.on('click', (function(e, el, o, ii, t){
17683
17684             e.preventDefault();
17685
17686             this.showPanel(ii);
17687
17688             if(this.autoslide && this.slideFn){
17689                 clearInterval(this.slideFn);
17690                 this.slideFn = window.setInterval(function() {
17691                     _this.showPanelNext();
17692                 }, this.timer);
17693             }
17694
17695         }).createDelegate(this, [i, bullet], true));
17696                 
17697         
17698     },
17699      
17700     setActiveBullet : function(i)
17701     {
17702         if(Roo.isTouch){
17703             return;
17704         }
17705         
17706         Roo.each(this.el.select('.bullet', true).elements, function(el){
17707             el.removeClass('selected');
17708         });
17709
17710         var bullet = this.el.select('.bullet-' + i, true).first();
17711         
17712         if(!bullet){
17713             return;
17714         }
17715         
17716         bullet.addClass('selected');
17717     }
17718     
17719     
17720   
17721 });
17722
17723  
17724
17725  
17726  
17727 Roo.apply(Roo.bootstrap.TabGroup, {
17728     
17729     groups: {},
17730      /**
17731     * register a Navigation Group
17732     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17733     */
17734     register : function(navgrp)
17735     {
17736         this.groups[navgrp.navId] = navgrp;
17737         
17738     },
17739     /**
17740     * fetch a Navigation Group based on the navigation ID
17741     * if one does not exist , it will get created.
17742     * @param {string} the navgroup to add
17743     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17744     */
17745     get: function(navId) {
17746         if (typeof(this.groups[navId]) == 'undefined') {
17747             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17748         }
17749         return this.groups[navId] ;
17750     }
17751     
17752     
17753     
17754 });
17755
17756  /*
17757  * - LGPL
17758  *
17759  * TabPanel
17760  * 
17761  */
17762
17763 /**
17764  * @class Roo.bootstrap.TabPanel
17765  * @extends Roo.bootstrap.Component
17766  * Bootstrap TabPanel class
17767  * @cfg {Boolean} active panel active
17768  * @cfg {String} html panel content
17769  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17770  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17771  * @cfg {String} href click to link..
17772  * 
17773  * 
17774  * @constructor
17775  * Create a new TabPanel
17776  * @param {Object} config The config object
17777  */
17778
17779 Roo.bootstrap.TabPanel = function(config){
17780     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17781     this.addEvents({
17782         /**
17783              * @event changed
17784              * Fires when the active status changes
17785              * @param {Roo.bootstrap.TabPanel} this
17786              * @param {Boolean} state the new state
17787             
17788          */
17789         'changed': true,
17790         /**
17791              * @event beforedeactivate
17792              * Fires before a tab is de-activated - can be used to do validation on a form.
17793              * @param {Roo.bootstrap.TabPanel} this
17794              * @return {Boolean} false if there is an error
17795             
17796          */
17797         'beforedeactivate': true
17798      });
17799     
17800     this.tabId = this.tabId || Roo.id();
17801   
17802 };
17803
17804 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17805     
17806     active: false,
17807     html: false,
17808     tabId: false,
17809     navId : false,
17810     href : '',
17811     
17812     getAutoCreate : function(){
17813         var cfg = {
17814             tag: 'div',
17815             // item is needed for carousel - not sure if it has any effect otherwise
17816             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17817             html: this.html || ''
17818         };
17819         
17820         if(this.active){
17821             cfg.cls += ' active';
17822         }
17823         
17824         if(this.tabId){
17825             cfg.tabId = this.tabId;
17826         }
17827         
17828         
17829         return cfg;
17830     },
17831     
17832     initEvents:  function()
17833     {
17834         var p = this.parent();
17835         
17836         this.navId = this.navId || p.navId;
17837         
17838         if (typeof(this.navId) != 'undefined') {
17839             // not really needed.. but just in case.. parent should be a NavGroup.
17840             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17841             
17842             tg.register(this);
17843             
17844             var i = tg.tabs.length - 1;
17845             
17846             if(this.active && tg.bullets > 0 && i < tg.bullets){
17847                 tg.setActiveBullet(i);
17848             }
17849         }
17850         
17851         this.el.on('click', this.onClick, this);
17852         
17853         if(Roo.isTouch){
17854             this.el.on("touchstart", this.onTouchStart, this);
17855             this.el.on("touchmove", this.onTouchMove, this);
17856             this.el.on("touchend", this.onTouchEnd, this);
17857         }
17858         
17859     },
17860     
17861     onRender : function(ct, position)
17862     {
17863         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17864     },
17865     
17866     setActive : function(state)
17867     {
17868         Roo.log("panel - set active " + this.tabId + "=" + state);
17869         
17870         this.active = state;
17871         if (!state) {
17872             this.el.removeClass('active');
17873             
17874         } else  if (!this.el.hasClass('active')) {
17875             this.el.addClass('active');
17876         }
17877         
17878         this.fireEvent('changed', this, state);
17879     },
17880     
17881     onClick : function(e)
17882     {
17883         e.preventDefault();
17884         
17885         if(!this.href.length){
17886             return;
17887         }
17888         
17889         window.location.href = this.href;
17890     },
17891     
17892     startX : 0,
17893     startY : 0,
17894     endX : 0,
17895     endY : 0,
17896     swiping : false,
17897     
17898     onTouchStart : function(e)
17899     {
17900         this.swiping = false;
17901         
17902         this.startX = e.browserEvent.touches[0].clientX;
17903         this.startY = e.browserEvent.touches[0].clientY;
17904     },
17905     
17906     onTouchMove : function(e)
17907     {
17908         this.swiping = true;
17909         
17910         this.endX = e.browserEvent.touches[0].clientX;
17911         this.endY = e.browserEvent.touches[0].clientY;
17912     },
17913     
17914     onTouchEnd : function(e)
17915     {
17916         if(!this.swiping){
17917             this.onClick(e);
17918             return;
17919         }
17920         
17921         var tabGroup = this.parent();
17922         
17923         if(this.endX > this.startX){ // swiping right
17924             tabGroup.showPanelPrev();
17925             return;
17926         }
17927         
17928         if(this.startX > this.endX){ // swiping left
17929             tabGroup.showPanelNext();
17930             return;
17931         }
17932     }
17933     
17934     
17935 });
17936  
17937
17938  
17939
17940  /*
17941  * - LGPL
17942  *
17943  * DateField
17944  * 
17945  */
17946
17947 /**
17948  * @class Roo.bootstrap.DateField
17949  * @extends Roo.bootstrap.Input
17950  * Bootstrap DateField class
17951  * @cfg {Number} weekStart default 0
17952  * @cfg {String} viewMode default empty, (months|years)
17953  * @cfg {String} minViewMode default empty, (months|years)
17954  * @cfg {Number} startDate default -Infinity
17955  * @cfg {Number} endDate default Infinity
17956  * @cfg {Boolean} todayHighlight default false
17957  * @cfg {Boolean} todayBtn default false
17958  * @cfg {Boolean} calendarWeeks default false
17959  * @cfg {Object} daysOfWeekDisabled default empty
17960  * @cfg {Boolean} singleMode default false (true | false)
17961  * 
17962  * @cfg {Boolean} keyboardNavigation default true
17963  * @cfg {String} language default en
17964  * 
17965  * @constructor
17966  * Create a new DateField
17967  * @param {Object} config The config object
17968  */
17969
17970 Roo.bootstrap.DateField = function(config){
17971     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17972      this.addEvents({
17973             /**
17974              * @event show
17975              * Fires when this field show.
17976              * @param {Roo.bootstrap.DateField} this
17977              * @param {Mixed} date The date value
17978              */
17979             show : true,
17980             /**
17981              * @event show
17982              * Fires when this field hide.
17983              * @param {Roo.bootstrap.DateField} this
17984              * @param {Mixed} date The date value
17985              */
17986             hide : true,
17987             /**
17988              * @event select
17989              * Fires when select a date.
17990              * @param {Roo.bootstrap.DateField} this
17991              * @param {Mixed} date The date value
17992              */
17993             select : true,
17994             /**
17995              * @event beforeselect
17996              * Fires when before select a date.
17997              * @param {Roo.bootstrap.DateField} this
17998              * @param {Mixed} date The date value
17999              */
18000             beforeselect : true
18001         });
18002 };
18003
18004 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18005     
18006     /**
18007      * @cfg {String} format
18008      * The default date format string which can be overriden for localization support.  The format must be
18009      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18010      */
18011     format : "m/d/y",
18012     /**
18013      * @cfg {String} altFormats
18014      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18015      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18016      */
18017     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18018     
18019     weekStart : 0,
18020     
18021     viewMode : '',
18022     
18023     minViewMode : '',
18024     
18025     todayHighlight : false,
18026     
18027     todayBtn: false,
18028     
18029     language: 'en',
18030     
18031     keyboardNavigation: true,
18032     
18033     calendarWeeks: false,
18034     
18035     startDate: -Infinity,
18036     
18037     endDate: Infinity,
18038     
18039     daysOfWeekDisabled: [],
18040     
18041     _events: [],
18042     
18043     singleMode : false,
18044     
18045     UTCDate: function()
18046     {
18047         return new Date(Date.UTC.apply(Date, arguments));
18048     },
18049     
18050     UTCToday: function()
18051     {
18052         var today = new Date();
18053         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18054     },
18055     
18056     getDate: function() {
18057             var d = this.getUTCDate();
18058             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18059     },
18060     
18061     getUTCDate: function() {
18062             return this.date;
18063     },
18064     
18065     setDate: function(d) {
18066             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18067     },
18068     
18069     setUTCDate: function(d) {
18070             this.date = d;
18071             this.setValue(this.formatDate(this.date));
18072     },
18073         
18074     onRender: function(ct, position)
18075     {
18076         
18077         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18078         
18079         this.language = this.language || 'en';
18080         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18081         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18082         
18083         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18084         this.format = this.format || 'm/d/y';
18085         this.isInline = false;
18086         this.isInput = true;
18087         this.component = this.el.select('.add-on', true).first() || false;
18088         this.component = (this.component && this.component.length === 0) ? false : this.component;
18089         this.hasInput = this.component && this.inputEl().length;
18090         
18091         if (typeof(this.minViewMode === 'string')) {
18092             switch (this.minViewMode) {
18093                 case 'months':
18094                     this.minViewMode = 1;
18095                     break;
18096                 case 'years':
18097                     this.minViewMode = 2;
18098                     break;
18099                 default:
18100                     this.minViewMode = 0;
18101                     break;
18102             }
18103         }
18104         
18105         if (typeof(this.viewMode === 'string')) {
18106             switch (this.viewMode) {
18107                 case 'months':
18108                     this.viewMode = 1;
18109                     break;
18110                 case 'years':
18111                     this.viewMode = 2;
18112                     break;
18113                 default:
18114                     this.viewMode = 0;
18115                     break;
18116             }
18117         }
18118                 
18119         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18120         
18121 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18122         
18123         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18124         
18125         this.picker().on('mousedown', this.onMousedown, this);
18126         this.picker().on('click', this.onClick, this);
18127         
18128         this.picker().addClass('datepicker-dropdown');
18129         
18130         this.startViewMode = this.viewMode;
18131         
18132         if(this.singleMode){
18133             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18134                 v.setVisibilityMode(Roo.Element.DISPLAY);
18135                 v.hide();
18136             });
18137             
18138             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18139                 v.setStyle('width', '189px');
18140             });
18141         }
18142         
18143         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18144             if(!this.calendarWeeks){
18145                 v.remove();
18146                 return;
18147             }
18148             
18149             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18150             v.attr('colspan', function(i, val){
18151                 return parseInt(val) + 1;
18152             });
18153         });
18154                         
18155         
18156         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18157         
18158         this.setStartDate(this.startDate);
18159         this.setEndDate(this.endDate);
18160         
18161         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18162         
18163         this.fillDow();
18164         this.fillMonths();
18165         this.update();
18166         this.showMode();
18167         
18168         if(this.isInline) {
18169             this.show();
18170         }
18171     },
18172     
18173     picker : function()
18174     {
18175         return this.pickerEl;
18176 //        return this.el.select('.datepicker', true).first();
18177     },
18178     
18179     fillDow: function()
18180     {
18181         var dowCnt = this.weekStart;
18182         
18183         var dow = {
18184             tag: 'tr',
18185             cn: [
18186                 
18187             ]
18188         };
18189         
18190         if(this.calendarWeeks){
18191             dow.cn.push({
18192                 tag: 'th',
18193                 cls: 'cw',
18194                 html: '&nbsp;'
18195             })
18196         }
18197         
18198         while (dowCnt < this.weekStart + 7) {
18199             dow.cn.push({
18200                 tag: 'th',
18201                 cls: 'dow',
18202                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18203             });
18204         }
18205         
18206         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18207     },
18208     
18209     fillMonths: function()
18210     {    
18211         var i = 0;
18212         var months = this.picker().select('>.datepicker-months td', true).first();
18213         
18214         months.dom.innerHTML = '';
18215         
18216         while (i < 12) {
18217             var month = {
18218                 tag: 'span',
18219                 cls: 'month',
18220                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18221             };
18222             
18223             months.createChild(month);
18224         }
18225         
18226     },
18227     
18228     update: function()
18229     {
18230         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;
18231         
18232         if (this.date < this.startDate) {
18233             this.viewDate = new Date(this.startDate);
18234         } else if (this.date > this.endDate) {
18235             this.viewDate = new Date(this.endDate);
18236         } else {
18237             this.viewDate = new Date(this.date);
18238         }
18239         
18240         this.fill();
18241     },
18242     
18243     fill: function() 
18244     {
18245         var d = new Date(this.viewDate),
18246                 year = d.getUTCFullYear(),
18247                 month = d.getUTCMonth(),
18248                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18249                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18250                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18251                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18252                 currentDate = this.date && this.date.valueOf(),
18253                 today = this.UTCToday();
18254         
18255         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18256         
18257 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18258         
18259 //        this.picker.select('>tfoot th.today').
18260 //                                              .text(dates[this.language].today)
18261 //                                              .toggle(this.todayBtn !== false);
18262     
18263         this.updateNavArrows();
18264         this.fillMonths();
18265                                                 
18266         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18267         
18268         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18269          
18270         prevMonth.setUTCDate(day);
18271         
18272         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18273         
18274         var nextMonth = new Date(prevMonth);
18275         
18276         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18277         
18278         nextMonth = nextMonth.valueOf();
18279         
18280         var fillMonths = false;
18281         
18282         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18283         
18284         while(prevMonth.valueOf() < nextMonth) {
18285             var clsName = '';
18286             
18287             if (prevMonth.getUTCDay() === this.weekStart) {
18288                 if(fillMonths){
18289                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18290                 }
18291                     
18292                 fillMonths = {
18293                     tag: 'tr',
18294                     cn: []
18295                 };
18296                 
18297                 if(this.calendarWeeks){
18298                     // ISO 8601: First week contains first thursday.
18299                     // ISO also states week starts on Monday, but we can be more abstract here.
18300                     var
18301                     // Start of current week: based on weekstart/current date
18302                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18303                     // Thursday of this week
18304                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18305                     // First Thursday of year, year from thursday
18306                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18307                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18308                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18309                     
18310                     fillMonths.cn.push({
18311                         tag: 'td',
18312                         cls: 'cw',
18313                         html: calWeek
18314                     });
18315                 }
18316             }
18317             
18318             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18319                 clsName += ' old';
18320             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18321                 clsName += ' new';
18322             }
18323             if (this.todayHighlight &&
18324                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18325                 prevMonth.getUTCMonth() == today.getMonth() &&
18326                 prevMonth.getUTCDate() == today.getDate()) {
18327                 clsName += ' today';
18328             }
18329             
18330             if (currentDate && prevMonth.valueOf() === currentDate) {
18331                 clsName += ' active';
18332             }
18333             
18334             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18335                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18336                     clsName += ' disabled';
18337             }
18338             
18339             fillMonths.cn.push({
18340                 tag: 'td',
18341                 cls: 'day ' + clsName,
18342                 html: prevMonth.getDate()
18343             });
18344             
18345             prevMonth.setDate(prevMonth.getDate()+1);
18346         }
18347           
18348         var currentYear = this.date && this.date.getUTCFullYear();
18349         var currentMonth = this.date && this.date.getUTCMonth();
18350         
18351         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18352         
18353         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18354             v.removeClass('active');
18355             
18356             if(currentYear === year && k === currentMonth){
18357                 v.addClass('active');
18358             }
18359             
18360             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18361                 v.addClass('disabled');
18362             }
18363             
18364         });
18365         
18366         
18367         year = parseInt(year/10, 10) * 10;
18368         
18369         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18370         
18371         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18372         
18373         year -= 1;
18374         for (var i = -1; i < 11; i++) {
18375             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18376                 tag: 'span',
18377                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18378                 html: year
18379             });
18380             
18381             year += 1;
18382         }
18383     },
18384     
18385     showMode: function(dir) 
18386     {
18387         if (dir) {
18388             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18389         }
18390         
18391         Roo.each(this.picker().select('>div',true).elements, function(v){
18392             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18393             v.hide();
18394         });
18395         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18396     },
18397     
18398     place: function()
18399     {
18400         if(this.isInline) {
18401             return;
18402         }
18403         
18404         this.picker().removeClass(['bottom', 'top']);
18405         
18406         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18407             /*
18408              * place to the top of element!
18409              *
18410              */
18411             
18412             this.picker().addClass('top');
18413             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18414             
18415             return;
18416         }
18417         
18418         this.picker().addClass('bottom');
18419         
18420         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18421     },
18422     
18423     parseDate : function(value)
18424     {
18425         if(!value || value instanceof Date){
18426             return value;
18427         }
18428         var v = Date.parseDate(value, this.format);
18429         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18430             v = Date.parseDate(value, 'Y-m-d');
18431         }
18432         if(!v && this.altFormats){
18433             if(!this.altFormatsArray){
18434                 this.altFormatsArray = this.altFormats.split("|");
18435             }
18436             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18437                 v = Date.parseDate(value, this.altFormatsArray[i]);
18438             }
18439         }
18440         return v;
18441     },
18442     
18443     formatDate : function(date, fmt)
18444     {   
18445         return (!date || !(date instanceof Date)) ?
18446         date : date.dateFormat(fmt || this.format);
18447     },
18448     
18449     onFocus : function()
18450     {
18451         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18452         this.show();
18453     },
18454     
18455     onBlur : function()
18456     {
18457         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18458         
18459         var d = this.inputEl().getValue();
18460         
18461         this.setValue(d);
18462                 
18463         this.hide();
18464     },
18465     
18466     show : function()
18467     {
18468         this.picker().show();
18469         this.update();
18470         this.place();
18471         
18472         this.fireEvent('show', this, this.date);
18473     },
18474     
18475     hide : function()
18476     {
18477         if(this.isInline) {
18478             return;
18479         }
18480         this.picker().hide();
18481         this.viewMode = this.startViewMode;
18482         this.showMode();
18483         
18484         this.fireEvent('hide', this, this.date);
18485         
18486     },
18487     
18488     onMousedown: function(e)
18489     {
18490         e.stopPropagation();
18491         e.preventDefault();
18492     },
18493     
18494     keyup: function(e)
18495     {
18496         Roo.bootstrap.DateField.superclass.keyup.call(this);
18497         this.update();
18498     },
18499
18500     setValue: function(v)
18501     {
18502         if(this.fireEvent('beforeselect', this, v) !== false){
18503             var d = new Date(this.parseDate(v) ).clearTime();
18504         
18505             if(isNaN(d.getTime())){
18506                 this.date = this.viewDate = '';
18507                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18508                 return;
18509             }
18510
18511             v = this.formatDate(d);
18512
18513             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18514
18515             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18516
18517             this.update();
18518
18519             this.fireEvent('select', this, this.date);
18520         }
18521     },
18522     
18523     getValue: function()
18524     {
18525         return this.formatDate(this.date);
18526     },
18527     
18528     fireKey: function(e)
18529     {
18530         if (!this.picker().isVisible()){
18531             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18532                 this.show();
18533             }
18534             return;
18535         }
18536         
18537         var dateChanged = false,
18538         dir, day, month,
18539         newDate, newViewDate;
18540         
18541         switch(e.keyCode){
18542             case 27: // escape
18543                 this.hide();
18544                 e.preventDefault();
18545                 break;
18546             case 37: // left
18547             case 39: // right
18548                 if (!this.keyboardNavigation) {
18549                     break;
18550                 }
18551                 dir = e.keyCode == 37 ? -1 : 1;
18552                 
18553                 if (e.ctrlKey){
18554                     newDate = this.moveYear(this.date, dir);
18555                     newViewDate = this.moveYear(this.viewDate, dir);
18556                 } else if (e.shiftKey){
18557                     newDate = this.moveMonth(this.date, dir);
18558                     newViewDate = this.moveMonth(this.viewDate, dir);
18559                 } else {
18560                     newDate = new Date(this.date);
18561                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18562                     newViewDate = new Date(this.viewDate);
18563                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18564                 }
18565                 if (this.dateWithinRange(newDate)){
18566                     this.date = newDate;
18567                     this.viewDate = newViewDate;
18568                     this.setValue(this.formatDate(this.date));
18569 //                    this.update();
18570                     e.preventDefault();
18571                     dateChanged = true;
18572                 }
18573                 break;
18574             case 38: // up
18575             case 40: // down
18576                 if (!this.keyboardNavigation) {
18577                     break;
18578                 }
18579                 dir = e.keyCode == 38 ? -1 : 1;
18580                 if (e.ctrlKey){
18581                     newDate = this.moveYear(this.date, dir);
18582                     newViewDate = this.moveYear(this.viewDate, dir);
18583                 } else if (e.shiftKey){
18584                     newDate = this.moveMonth(this.date, dir);
18585                     newViewDate = this.moveMonth(this.viewDate, dir);
18586                 } else {
18587                     newDate = new Date(this.date);
18588                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18589                     newViewDate = new Date(this.viewDate);
18590                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18591                 }
18592                 if (this.dateWithinRange(newDate)){
18593                     this.date = newDate;
18594                     this.viewDate = newViewDate;
18595                     this.setValue(this.formatDate(this.date));
18596 //                    this.update();
18597                     e.preventDefault();
18598                     dateChanged = true;
18599                 }
18600                 break;
18601             case 13: // enter
18602                 this.setValue(this.formatDate(this.date));
18603                 this.hide();
18604                 e.preventDefault();
18605                 break;
18606             case 9: // tab
18607                 this.setValue(this.formatDate(this.date));
18608                 this.hide();
18609                 break;
18610             case 16: // shift
18611             case 17: // ctrl
18612             case 18: // alt
18613                 break;
18614             default :
18615                 this.hide();
18616                 
18617         }
18618     },
18619     
18620     
18621     onClick: function(e) 
18622     {
18623         e.stopPropagation();
18624         e.preventDefault();
18625         
18626         var target = e.getTarget();
18627         
18628         if(target.nodeName.toLowerCase() === 'i'){
18629             target = Roo.get(target).dom.parentNode;
18630         }
18631         
18632         var nodeName = target.nodeName;
18633         var className = target.className;
18634         var html = target.innerHTML;
18635         //Roo.log(nodeName);
18636         
18637         switch(nodeName.toLowerCase()) {
18638             case 'th':
18639                 switch(className) {
18640                     case 'switch':
18641                         this.showMode(1);
18642                         break;
18643                     case 'prev':
18644                     case 'next':
18645                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18646                         switch(this.viewMode){
18647                                 case 0:
18648                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18649                                         break;
18650                                 case 1:
18651                                 case 2:
18652                                         this.viewDate = this.moveYear(this.viewDate, dir);
18653                                         break;
18654                         }
18655                         this.fill();
18656                         break;
18657                     case 'today':
18658                         var date = new Date();
18659                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18660 //                        this.fill()
18661                         this.setValue(this.formatDate(this.date));
18662                         
18663                         this.hide();
18664                         break;
18665                 }
18666                 break;
18667             case 'span':
18668                 if (className.indexOf('disabled') < 0) {
18669                     this.viewDate.setUTCDate(1);
18670                     if (className.indexOf('month') > -1) {
18671                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18672                     } else {
18673                         var year = parseInt(html, 10) || 0;
18674                         this.viewDate.setUTCFullYear(year);
18675                         
18676                     }
18677                     
18678                     if(this.singleMode){
18679                         this.setValue(this.formatDate(this.viewDate));
18680                         this.hide();
18681                         return;
18682                     }
18683                     
18684                     this.showMode(-1);
18685                     this.fill();
18686                 }
18687                 break;
18688                 
18689             case 'td':
18690                 //Roo.log(className);
18691                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18692                     var day = parseInt(html, 10) || 1;
18693                     var year = this.viewDate.getUTCFullYear(),
18694                         month = this.viewDate.getUTCMonth();
18695
18696                     if (className.indexOf('old') > -1) {
18697                         if(month === 0 ){
18698                             month = 11;
18699                             year -= 1;
18700                         }else{
18701                             month -= 1;
18702                         }
18703                     } else if (className.indexOf('new') > -1) {
18704                         if (month == 11) {
18705                             month = 0;
18706                             year += 1;
18707                         } else {
18708                             month += 1;
18709                         }
18710                     }
18711                     //Roo.log([year,month,day]);
18712                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18713                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18714 //                    this.fill();
18715                     //Roo.log(this.formatDate(this.date));
18716                     this.setValue(this.formatDate(this.date));
18717                     this.hide();
18718                 }
18719                 break;
18720         }
18721     },
18722     
18723     setStartDate: function(startDate)
18724     {
18725         this.startDate = startDate || -Infinity;
18726         if (this.startDate !== -Infinity) {
18727             this.startDate = this.parseDate(this.startDate);
18728         }
18729         this.update();
18730         this.updateNavArrows();
18731     },
18732
18733     setEndDate: function(endDate)
18734     {
18735         this.endDate = endDate || Infinity;
18736         if (this.endDate !== Infinity) {
18737             this.endDate = this.parseDate(this.endDate);
18738         }
18739         this.update();
18740         this.updateNavArrows();
18741     },
18742     
18743     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18744     {
18745         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18746         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18747             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18748         }
18749         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18750             return parseInt(d, 10);
18751         });
18752         this.update();
18753         this.updateNavArrows();
18754     },
18755     
18756     updateNavArrows: function() 
18757     {
18758         if(this.singleMode){
18759             return;
18760         }
18761         
18762         var d = new Date(this.viewDate),
18763         year = d.getUTCFullYear(),
18764         month = d.getUTCMonth();
18765         
18766         Roo.each(this.picker().select('.prev', true).elements, function(v){
18767             v.show();
18768             switch (this.viewMode) {
18769                 case 0:
18770
18771                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18772                         v.hide();
18773                     }
18774                     break;
18775                 case 1:
18776                 case 2:
18777                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18778                         v.hide();
18779                     }
18780                     break;
18781             }
18782         });
18783         
18784         Roo.each(this.picker().select('.next', true).elements, function(v){
18785             v.show();
18786             switch (this.viewMode) {
18787                 case 0:
18788
18789                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18790                         v.hide();
18791                     }
18792                     break;
18793                 case 1:
18794                 case 2:
18795                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18796                         v.hide();
18797                     }
18798                     break;
18799             }
18800         })
18801     },
18802     
18803     moveMonth: function(date, dir)
18804     {
18805         if (!dir) {
18806             return date;
18807         }
18808         var new_date = new Date(date.valueOf()),
18809         day = new_date.getUTCDate(),
18810         month = new_date.getUTCMonth(),
18811         mag = Math.abs(dir),
18812         new_month, test;
18813         dir = dir > 0 ? 1 : -1;
18814         if (mag == 1){
18815             test = dir == -1
18816             // If going back one month, make sure month is not current month
18817             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18818             ? function(){
18819                 return new_date.getUTCMonth() == month;
18820             }
18821             // If going forward one month, make sure month is as expected
18822             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18823             : function(){
18824                 return new_date.getUTCMonth() != new_month;
18825             };
18826             new_month = month + dir;
18827             new_date.setUTCMonth(new_month);
18828             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18829             if (new_month < 0 || new_month > 11) {
18830                 new_month = (new_month + 12) % 12;
18831             }
18832         } else {
18833             // For magnitudes >1, move one month at a time...
18834             for (var i=0; i<mag; i++) {
18835                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18836                 new_date = this.moveMonth(new_date, dir);
18837             }
18838             // ...then reset the day, keeping it in the new month
18839             new_month = new_date.getUTCMonth();
18840             new_date.setUTCDate(day);
18841             test = function(){
18842                 return new_month != new_date.getUTCMonth();
18843             };
18844         }
18845         // Common date-resetting loop -- if date is beyond end of month, make it
18846         // end of month
18847         while (test()){
18848             new_date.setUTCDate(--day);
18849             new_date.setUTCMonth(new_month);
18850         }
18851         return new_date;
18852     },
18853
18854     moveYear: function(date, dir)
18855     {
18856         return this.moveMonth(date, dir*12);
18857     },
18858
18859     dateWithinRange: function(date)
18860     {
18861         return date >= this.startDate && date <= this.endDate;
18862     },
18863
18864     
18865     remove: function() 
18866     {
18867         this.picker().remove();
18868     },
18869     
18870     validateValue : function(value)
18871     {
18872         if(value.length < 1)  {
18873             if(this.allowBlank){
18874                 return true;
18875             }
18876             return false;
18877         }
18878         
18879         if(value.length < this.minLength){
18880             return false;
18881         }
18882         if(value.length > this.maxLength){
18883             return false;
18884         }
18885         if(this.vtype){
18886             var vt = Roo.form.VTypes;
18887             if(!vt[this.vtype](value, this)){
18888                 return false;
18889             }
18890         }
18891         if(typeof this.validator == "function"){
18892             var msg = this.validator(value);
18893             if(msg !== true){
18894                 return false;
18895             }
18896         }
18897         
18898         if(this.regex && !this.regex.test(value)){
18899             return false;
18900         }
18901         
18902         if(typeof(this.parseDate(value)) == 'undefined'){
18903             return false;
18904         }
18905         
18906         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18907             return false;
18908         }      
18909         
18910         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18911             return false;
18912         } 
18913         
18914         
18915         return true;
18916     }
18917    
18918 });
18919
18920 Roo.apply(Roo.bootstrap.DateField,  {
18921     
18922     head : {
18923         tag: 'thead',
18924         cn: [
18925         {
18926             tag: 'tr',
18927             cn: [
18928             {
18929                 tag: 'th',
18930                 cls: 'prev',
18931                 html: '<i class="fa fa-arrow-left"/>'
18932             },
18933             {
18934                 tag: 'th',
18935                 cls: 'switch',
18936                 colspan: '5'
18937             },
18938             {
18939                 tag: 'th',
18940                 cls: 'next',
18941                 html: '<i class="fa fa-arrow-right"/>'
18942             }
18943
18944             ]
18945         }
18946         ]
18947     },
18948     
18949     content : {
18950         tag: 'tbody',
18951         cn: [
18952         {
18953             tag: 'tr',
18954             cn: [
18955             {
18956                 tag: 'td',
18957                 colspan: '7'
18958             }
18959             ]
18960         }
18961         ]
18962     },
18963     
18964     footer : {
18965         tag: 'tfoot',
18966         cn: [
18967         {
18968             tag: 'tr',
18969             cn: [
18970             {
18971                 tag: 'th',
18972                 colspan: '7',
18973                 cls: 'today'
18974             }
18975                     
18976             ]
18977         }
18978         ]
18979     },
18980     
18981     dates:{
18982         en: {
18983             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18984             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18985             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18986             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18987             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18988             today: "Today"
18989         }
18990     },
18991     
18992     modes: [
18993     {
18994         clsName: 'days',
18995         navFnc: 'Month',
18996         navStep: 1
18997     },
18998     {
18999         clsName: 'months',
19000         navFnc: 'FullYear',
19001         navStep: 1
19002     },
19003     {
19004         clsName: 'years',
19005         navFnc: 'FullYear',
19006         navStep: 10
19007     }]
19008 });
19009
19010 Roo.apply(Roo.bootstrap.DateField,  {
19011   
19012     template : {
19013         tag: 'div',
19014         cls: 'datepicker dropdown-menu roo-dynamic',
19015         cn: [
19016         {
19017             tag: 'div',
19018             cls: 'datepicker-days',
19019             cn: [
19020             {
19021                 tag: 'table',
19022                 cls: 'table-condensed',
19023                 cn:[
19024                 Roo.bootstrap.DateField.head,
19025                 {
19026                     tag: 'tbody'
19027                 },
19028                 Roo.bootstrap.DateField.footer
19029                 ]
19030             }
19031             ]
19032         },
19033         {
19034             tag: 'div',
19035             cls: 'datepicker-months',
19036             cn: [
19037             {
19038                 tag: 'table',
19039                 cls: 'table-condensed',
19040                 cn:[
19041                 Roo.bootstrap.DateField.head,
19042                 Roo.bootstrap.DateField.content,
19043                 Roo.bootstrap.DateField.footer
19044                 ]
19045             }
19046             ]
19047         },
19048         {
19049             tag: 'div',
19050             cls: 'datepicker-years',
19051             cn: [
19052             {
19053                 tag: 'table',
19054                 cls: 'table-condensed',
19055                 cn:[
19056                 Roo.bootstrap.DateField.head,
19057                 Roo.bootstrap.DateField.content,
19058                 Roo.bootstrap.DateField.footer
19059                 ]
19060             }
19061             ]
19062         }
19063         ]
19064     }
19065 });
19066
19067  
19068
19069  /*
19070  * - LGPL
19071  *
19072  * TimeField
19073  * 
19074  */
19075
19076 /**
19077  * @class Roo.bootstrap.TimeField
19078  * @extends Roo.bootstrap.Input
19079  * Bootstrap DateField class
19080  * 
19081  * 
19082  * @constructor
19083  * Create a new TimeField
19084  * @param {Object} config The config object
19085  */
19086
19087 Roo.bootstrap.TimeField = function(config){
19088     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19089     this.addEvents({
19090             /**
19091              * @event show
19092              * Fires when this field show.
19093              * @param {Roo.bootstrap.DateField} thisthis
19094              * @param {Mixed} date The date value
19095              */
19096             show : true,
19097             /**
19098              * @event show
19099              * Fires when this field hide.
19100              * @param {Roo.bootstrap.DateField} this
19101              * @param {Mixed} date The date value
19102              */
19103             hide : true,
19104             /**
19105              * @event select
19106              * Fires when select a date.
19107              * @param {Roo.bootstrap.DateField} this
19108              * @param {Mixed} date The date value
19109              */
19110             select : true
19111         });
19112 };
19113
19114 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19115     
19116     /**
19117      * @cfg {String} format
19118      * The default time format string which can be overriden for localization support.  The format must be
19119      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19120      */
19121     format : "H:i",
19122        
19123     onRender: function(ct, position)
19124     {
19125         
19126         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19127                 
19128         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19129         
19130         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19131         
19132         this.pop = this.picker().select('>.datepicker-time',true).first();
19133         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19134         
19135         this.picker().on('mousedown', this.onMousedown, this);
19136         this.picker().on('click', this.onClick, this);
19137         
19138         this.picker().addClass('datepicker-dropdown');
19139     
19140         this.fillTime();
19141         this.update();
19142             
19143         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19144         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19145         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19146         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19147         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19148         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19149
19150     },
19151     
19152     fireKey: function(e){
19153         if (!this.picker().isVisible()){
19154             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19155                 this.show();
19156             }
19157             return;
19158         }
19159
19160         e.preventDefault();
19161         
19162         switch(e.keyCode){
19163             case 27: // escape
19164                 this.hide();
19165                 break;
19166             case 37: // left
19167             case 39: // right
19168                 this.onTogglePeriod();
19169                 break;
19170             case 38: // up
19171                 this.onIncrementMinutes();
19172                 break;
19173             case 40: // down
19174                 this.onDecrementMinutes();
19175                 break;
19176             case 13: // enter
19177             case 9: // tab
19178                 this.setTime();
19179                 break;
19180         }
19181     },
19182     
19183     onClick: function(e) {
19184         e.stopPropagation();
19185         e.preventDefault();
19186     },
19187     
19188     picker : function()
19189     {
19190         return this.el.select('.datepicker', true).first();
19191     },
19192     
19193     fillTime: function()
19194     {    
19195         var time = this.pop.select('tbody', true).first();
19196         
19197         time.dom.innerHTML = '';
19198         
19199         time.createChild({
19200             tag: 'tr',
19201             cn: [
19202                 {
19203                     tag: 'td',
19204                     cn: [
19205                         {
19206                             tag: 'a',
19207                             href: '#',
19208                             cls: 'btn',
19209                             cn: [
19210                                 {
19211                                     tag: 'span',
19212                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19213                                 }
19214                             ]
19215                         } 
19216                     ]
19217                 },
19218                 {
19219                     tag: 'td',
19220                     cls: 'separator'
19221                 },
19222                 {
19223                     tag: 'td',
19224                     cn: [
19225                         {
19226                             tag: 'a',
19227                             href: '#',
19228                             cls: 'btn',
19229                             cn: [
19230                                 {
19231                                     tag: 'span',
19232                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19233                                 }
19234                             ]
19235                         }
19236                     ]
19237                 },
19238                 {
19239                     tag: 'td',
19240                     cls: 'separator'
19241                 }
19242             ]
19243         });
19244         
19245         time.createChild({
19246             tag: 'tr',
19247             cn: [
19248                 {
19249                     tag: 'td',
19250                     cn: [
19251                         {
19252                             tag: 'span',
19253                             cls: 'timepicker-hour',
19254                             html: '00'
19255                         }  
19256                     ]
19257                 },
19258                 {
19259                     tag: 'td',
19260                     cls: 'separator',
19261                     html: ':'
19262                 },
19263                 {
19264                     tag: 'td',
19265                     cn: [
19266                         {
19267                             tag: 'span',
19268                             cls: 'timepicker-minute',
19269                             html: '00'
19270                         }  
19271                     ]
19272                 },
19273                 {
19274                     tag: 'td',
19275                     cls: 'separator'
19276                 },
19277                 {
19278                     tag: 'td',
19279                     cn: [
19280                         {
19281                             tag: 'button',
19282                             type: 'button',
19283                             cls: 'btn btn-primary period',
19284                             html: 'AM'
19285                             
19286                         }
19287                     ]
19288                 }
19289             ]
19290         });
19291         
19292         time.createChild({
19293             tag: 'tr',
19294             cn: [
19295                 {
19296                     tag: 'td',
19297                     cn: [
19298                         {
19299                             tag: 'a',
19300                             href: '#',
19301                             cls: 'btn',
19302                             cn: [
19303                                 {
19304                                     tag: 'span',
19305                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19306                                 }
19307                             ]
19308                         }
19309                     ]
19310                 },
19311                 {
19312                     tag: 'td',
19313                     cls: 'separator'
19314                 },
19315                 {
19316                     tag: 'td',
19317                     cn: [
19318                         {
19319                             tag: 'a',
19320                             href: '#',
19321                             cls: 'btn',
19322                             cn: [
19323                                 {
19324                                     tag: 'span',
19325                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19326                                 }
19327                             ]
19328                         }
19329                     ]
19330                 },
19331                 {
19332                     tag: 'td',
19333                     cls: 'separator'
19334                 }
19335             ]
19336         });
19337         
19338     },
19339     
19340     update: function()
19341     {
19342         
19343         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19344         
19345         this.fill();
19346     },
19347     
19348     fill: function() 
19349     {
19350         var hours = this.time.getHours();
19351         var minutes = this.time.getMinutes();
19352         var period = 'AM';
19353         
19354         if(hours > 11){
19355             period = 'PM';
19356         }
19357         
19358         if(hours == 0){
19359             hours = 12;
19360         }
19361         
19362         
19363         if(hours > 12){
19364             hours = hours - 12;
19365         }
19366         
19367         if(hours < 10){
19368             hours = '0' + hours;
19369         }
19370         
19371         if(minutes < 10){
19372             minutes = '0' + minutes;
19373         }
19374         
19375         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19376         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19377         this.pop.select('button', true).first().dom.innerHTML = period;
19378         
19379     },
19380     
19381     place: function()
19382     {   
19383         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19384         
19385         var cls = ['bottom'];
19386         
19387         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19388             cls.pop();
19389             cls.push('top');
19390         }
19391         
19392         cls.push('right');
19393         
19394         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19395             cls.pop();
19396             cls.push('left');
19397         }
19398         
19399         this.picker().addClass(cls.join('-'));
19400         
19401         var _this = this;
19402         
19403         Roo.each(cls, function(c){
19404             if(c == 'bottom'){
19405                 _this.picker().setTop(_this.inputEl().getHeight());
19406                 return;
19407             }
19408             if(c == 'top'){
19409                 _this.picker().setTop(0 - _this.picker().getHeight());
19410                 return;
19411             }
19412             
19413             if(c == 'left'){
19414                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19415                 return;
19416             }
19417             if(c == 'right'){
19418                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19419                 return;
19420             }
19421         });
19422         
19423     },
19424   
19425     onFocus : function()
19426     {
19427         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19428         this.show();
19429     },
19430     
19431     onBlur : function()
19432     {
19433         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19434         this.hide();
19435     },
19436     
19437     show : function()
19438     {
19439         this.picker().show();
19440         this.pop.show();
19441         this.update();
19442         this.place();
19443         
19444         this.fireEvent('show', this, this.date);
19445     },
19446     
19447     hide : function()
19448     {
19449         this.picker().hide();
19450         this.pop.hide();
19451         
19452         this.fireEvent('hide', this, this.date);
19453     },
19454     
19455     setTime : function()
19456     {
19457         this.hide();
19458         this.setValue(this.time.format(this.format));
19459         
19460         this.fireEvent('select', this, this.date);
19461         
19462         
19463     },
19464     
19465     onMousedown: function(e){
19466         e.stopPropagation();
19467         e.preventDefault();
19468     },
19469     
19470     onIncrementHours: function()
19471     {
19472         Roo.log('onIncrementHours');
19473         this.time = this.time.add(Date.HOUR, 1);
19474         this.update();
19475         
19476     },
19477     
19478     onDecrementHours: function()
19479     {
19480         Roo.log('onDecrementHours');
19481         this.time = this.time.add(Date.HOUR, -1);
19482         this.update();
19483     },
19484     
19485     onIncrementMinutes: function()
19486     {
19487         Roo.log('onIncrementMinutes');
19488         this.time = this.time.add(Date.MINUTE, 1);
19489         this.update();
19490     },
19491     
19492     onDecrementMinutes: function()
19493     {
19494         Roo.log('onDecrementMinutes');
19495         this.time = this.time.add(Date.MINUTE, -1);
19496         this.update();
19497     },
19498     
19499     onTogglePeriod: function()
19500     {
19501         Roo.log('onTogglePeriod');
19502         this.time = this.time.add(Date.HOUR, 12);
19503         this.update();
19504     }
19505     
19506    
19507 });
19508
19509 Roo.apply(Roo.bootstrap.TimeField,  {
19510     
19511     content : {
19512         tag: 'tbody',
19513         cn: [
19514             {
19515                 tag: 'tr',
19516                 cn: [
19517                 {
19518                     tag: 'td',
19519                     colspan: '7'
19520                 }
19521                 ]
19522             }
19523         ]
19524     },
19525     
19526     footer : {
19527         tag: 'tfoot',
19528         cn: [
19529             {
19530                 tag: 'tr',
19531                 cn: [
19532                 {
19533                     tag: 'th',
19534                     colspan: '7',
19535                     cls: '',
19536                     cn: [
19537                         {
19538                             tag: 'button',
19539                             cls: 'btn btn-info ok',
19540                             html: 'OK'
19541                         }
19542                     ]
19543                 }
19544
19545                 ]
19546             }
19547         ]
19548     }
19549 });
19550
19551 Roo.apply(Roo.bootstrap.TimeField,  {
19552   
19553     template : {
19554         tag: 'div',
19555         cls: 'datepicker dropdown-menu',
19556         cn: [
19557             {
19558                 tag: 'div',
19559                 cls: 'datepicker-time',
19560                 cn: [
19561                 {
19562                     tag: 'table',
19563                     cls: 'table-condensed',
19564                     cn:[
19565                     Roo.bootstrap.TimeField.content,
19566                     Roo.bootstrap.TimeField.footer
19567                     ]
19568                 }
19569                 ]
19570             }
19571         ]
19572     }
19573 });
19574
19575  
19576
19577  /*
19578  * - LGPL
19579  *
19580  * MonthField
19581  * 
19582  */
19583
19584 /**
19585  * @class Roo.bootstrap.MonthField
19586  * @extends Roo.bootstrap.Input
19587  * Bootstrap MonthField class
19588  * 
19589  * @cfg {String} language default en
19590  * 
19591  * @constructor
19592  * Create a new MonthField
19593  * @param {Object} config The config object
19594  */
19595
19596 Roo.bootstrap.MonthField = function(config){
19597     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19598     
19599     this.addEvents({
19600         /**
19601          * @event show
19602          * Fires when this field show.
19603          * @param {Roo.bootstrap.MonthField} this
19604          * @param {Mixed} date The date value
19605          */
19606         show : true,
19607         /**
19608          * @event show
19609          * Fires when this field hide.
19610          * @param {Roo.bootstrap.MonthField} this
19611          * @param {Mixed} date The date value
19612          */
19613         hide : true,
19614         /**
19615          * @event select
19616          * Fires when select a date.
19617          * @param {Roo.bootstrap.MonthField} this
19618          * @param {String} oldvalue The old value
19619          * @param {String} newvalue The new value
19620          */
19621         select : true
19622     });
19623 };
19624
19625 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19626     
19627     onRender: function(ct, position)
19628     {
19629         
19630         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19631         
19632         this.language = this.language || 'en';
19633         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19634         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19635         
19636         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19637         this.isInline = false;
19638         this.isInput = true;
19639         this.component = this.el.select('.add-on', true).first() || false;
19640         this.component = (this.component && this.component.length === 0) ? false : this.component;
19641         this.hasInput = this.component && this.inputEL().length;
19642         
19643         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19644         
19645         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19646         
19647         this.picker().on('mousedown', this.onMousedown, this);
19648         this.picker().on('click', this.onClick, this);
19649         
19650         this.picker().addClass('datepicker-dropdown');
19651         
19652         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19653             v.setStyle('width', '189px');
19654         });
19655         
19656         this.fillMonths();
19657         
19658         this.update();
19659         
19660         if(this.isInline) {
19661             this.show();
19662         }
19663         
19664     },
19665     
19666     setValue: function(v, suppressEvent)
19667     {   
19668         var o = this.getValue();
19669         
19670         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19671         
19672         this.update();
19673
19674         if(suppressEvent !== true){
19675             this.fireEvent('select', this, o, v);
19676         }
19677         
19678     },
19679     
19680     getValue: function()
19681     {
19682         return this.value;
19683     },
19684     
19685     onClick: function(e) 
19686     {
19687         e.stopPropagation();
19688         e.preventDefault();
19689         
19690         var target = e.getTarget();
19691         
19692         if(target.nodeName.toLowerCase() === 'i'){
19693             target = Roo.get(target).dom.parentNode;
19694         }
19695         
19696         var nodeName = target.nodeName;
19697         var className = target.className;
19698         var html = target.innerHTML;
19699         
19700         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19701             return;
19702         }
19703         
19704         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19705         
19706         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19707         
19708         this.hide();
19709                         
19710     },
19711     
19712     picker : function()
19713     {
19714         return this.pickerEl;
19715     },
19716     
19717     fillMonths: function()
19718     {    
19719         var i = 0;
19720         var months = this.picker().select('>.datepicker-months td', true).first();
19721         
19722         months.dom.innerHTML = '';
19723         
19724         while (i < 12) {
19725             var month = {
19726                 tag: 'span',
19727                 cls: 'month',
19728                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19729             };
19730             
19731             months.createChild(month);
19732         }
19733         
19734     },
19735     
19736     update: function()
19737     {
19738         var _this = this;
19739         
19740         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19741             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19742         }
19743         
19744         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19745             e.removeClass('active');
19746             
19747             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19748                 e.addClass('active');
19749             }
19750         })
19751     },
19752     
19753     place: function()
19754     {
19755         if(this.isInline) {
19756             return;
19757         }
19758         
19759         this.picker().removeClass(['bottom', 'top']);
19760         
19761         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19762             /*
19763              * place to the top of element!
19764              *
19765              */
19766             
19767             this.picker().addClass('top');
19768             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19769             
19770             return;
19771         }
19772         
19773         this.picker().addClass('bottom');
19774         
19775         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19776     },
19777     
19778     onFocus : function()
19779     {
19780         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19781         this.show();
19782     },
19783     
19784     onBlur : function()
19785     {
19786         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19787         
19788         var d = this.inputEl().getValue();
19789         
19790         this.setValue(d);
19791                 
19792         this.hide();
19793     },
19794     
19795     show : function()
19796     {
19797         this.picker().show();
19798         this.picker().select('>.datepicker-months', true).first().show();
19799         this.update();
19800         this.place();
19801         
19802         this.fireEvent('show', this, this.date);
19803     },
19804     
19805     hide : function()
19806     {
19807         if(this.isInline) {
19808             return;
19809         }
19810         this.picker().hide();
19811         this.fireEvent('hide', this, this.date);
19812         
19813     },
19814     
19815     onMousedown: function(e)
19816     {
19817         e.stopPropagation();
19818         e.preventDefault();
19819     },
19820     
19821     keyup: function(e)
19822     {
19823         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19824         this.update();
19825     },
19826
19827     fireKey: function(e)
19828     {
19829         if (!this.picker().isVisible()){
19830             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19831                 this.show();
19832             }
19833             return;
19834         }
19835         
19836         var dir;
19837         
19838         switch(e.keyCode){
19839             case 27: // escape
19840                 this.hide();
19841                 e.preventDefault();
19842                 break;
19843             case 37: // left
19844             case 39: // right
19845                 dir = e.keyCode == 37 ? -1 : 1;
19846                 
19847                 this.vIndex = this.vIndex + dir;
19848                 
19849                 if(this.vIndex < 0){
19850                     this.vIndex = 0;
19851                 }
19852                 
19853                 if(this.vIndex > 11){
19854                     this.vIndex = 11;
19855                 }
19856                 
19857                 if(isNaN(this.vIndex)){
19858                     this.vIndex = 0;
19859                 }
19860                 
19861                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19862                 
19863                 break;
19864             case 38: // up
19865             case 40: // down
19866                 
19867                 dir = e.keyCode == 38 ? -1 : 1;
19868                 
19869                 this.vIndex = this.vIndex + dir * 4;
19870                 
19871                 if(this.vIndex < 0){
19872                     this.vIndex = 0;
19873                 }
19874                 
19875                 if(this.vIndex > 11){
19876                     this.vIndex = 11;
19877                 }
19878                 
19879                 if(isNaN(this.vIndex)){
19880                     this.vIndex = 0;
19881                 }
19882                 
19883                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19884                 break;
19885                 
19886             case 13: // enter
19887                 
19888                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19889                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19890                 }
19891                 
19892                 this.hide();
19893                 e.preventDefault();
19894                 break;
19895             case 9: // tab
19896                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19897                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19898                 }
19899                 this.hide();
19900                 break;
19901             case 16: // shift
19902             case 17: // ctrl
19903             case 18: // alt
19904                 break;
19905             default :
19906                 this.hide();
19907                 
19908         }
19909     },
19910     
19911     remove: function() 
19912     {
19913         this.picker().remove();
19914     }
19915    
19916 });
19917
19918 Roo.apply(Roo.bootstrap.MonthField,  {
19919     
19920     content : {
19921         tag: 'tbody',
19922         cn: [
19923         {
19924             tag: 'tr',
19925             cn: [
19926             {
19927                 tag: 'td',
19928                 colspan: '7'
19929             }
19930             ]
19931         }
19932         ]
19933     },
19934     
19935     dates:{
19936         en: {
19937             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19938             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19939         }
19940     }
19941 });
19942
19943 Roo.apply(Roo.bootstrap.MonthField,  {
19944   
19945     template : {
19946         tag: 'div',
19947         cls: 'datepicker dropdown-menu roo-dynamic',
19948         cn: [
19949             {
19950                 tag: 'div',
19951                 cls: 'datepicker-months',
19952                 cn: [
19953                 {
19954                     tag: 'table',
19955                     cls: 'table-condensed',
19956                     cn:[
19957                         Roo.bootstrap.DateField.content
19958                     ]
19959                 }
19960                 ]
19961             }
19962         ]
19963     }
19964 });
19965
19966  
19967
19968  
19969  /*
19970  * - LGPL
19971  *
19972  * CheckBox
19973  * 
19974  */
19975
19976 /**
19977  * @class Roo.bootstrap.CheckBox
19978  * @extends Roo.bootstrap.Input
19979  * Bootstrap CheckBox class
19980  * 
19981  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19982  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19983  * @cfg {String} boxLabel The text that appears beside the checkbox
19984  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19985  * @cfg {Boolean} checked initnal the element
19986  * @cfg {Boolean} inline inline the element (default false)
19987  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19988  * 
19989  * @constructor
19990  * Create a new CheckBox
19991  * @param {Object} config The config object
19992  */
19993
19994 Roo.bootstrap.CheckBox = function(config){
19995     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19996    
19997     this.addEvents({
19998         /**
19999         * @event check
20000         * Fires when the element is checked or unchecked.
20001         * @param {Roo.bootstrap.CheckBox} this This input
20002         * @param {Boolean} checked The new checked value
20003         */
20004        check : true
20005     });
20006     
20007 };
20008
20009 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20010   
20011     inputType: 'checkbox',
20012     inputValue: 1,
20013     valueOff: 0,
20014     boxLabel: false,
20015     checked: false,
20016     weight : false,
20017     inline: false,
20018     
20019     getAutoCreate : function()
20020     {
20021         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20022         
20023         var id = Roo.id();
20024         
20025         var cfg = {};
20026         
20027         cfg.cls = 'form-group ' + this.inputType; //input-group
20028         
20029         if(this.inline){
20030             cfg.cls += ' ' + this.inputType + '-inline';
20031         }
20032         
20033         var input =  {
20034             tag: 'input',
20035             id : id,
20036             type : this.inputType,
20037             value : this.inputValue,
20038             cls : 'roo-' + this.inputType, //'form-box',
20039             placeholder : this.placeholder || ''
20040             
20041         };
20042         
20043         if(this.inputType != 'radio'){
20044             var hidden =  {
20045                 tag: 'input',
20046                 type : 'hidden',
20047                 cls : 'roo-hidden-value',
20048                 value : this.checked ? this.valueOff : this.inputValue
20049             };
20050         }
20051         
20052             
20053         if (this.weight) { // Validity check?
20054             cfg.cls += " " + this.inputType + "-" + this.weight;
20055         }
20056         
20057         if (this.disabled) {
20058             input.disabled=true;
20059         }
20060         
20061         if(this.checked){
20062             input.checked = this.checked;
20063             
20064         }
20065         
20066         
20067         if (this.name) {
20068             
20069             input.name = this.name;
20070             
20071             if(this.inputType != 'radio'){
20072                 hidden.name = this.name;
20073                 input.name = '_hidden_' + this.name;
20074             }
20075         }
20076         
20077         if (this.size) {
20078             input.cls += ' input-' + this.size;
20079         }
20080         
20081         var settings=this;
20082         
20083         ['xs','sm','md','lg'].map(function(size){
20084             if (settings[size]) {
20085                 cfg.cls += ' col-' + size + '-' + settings[size];
20086             }
20087         });
20088         
20089         var inputblock = input;
20090          
20091         if (this.before || this.after) {
20092             
20093             inputblock = {
20094                 cls : 'input-group',
20095                 cn :  [] 
20096             };
20097             
20098             if (this.before) {
20099                 inputblock.cn.push({
20100                     tag :'span',
20101                     cls : 'input-group-addon',
20102                     html : this.before
20103                 });
20104             }
20105             
20106             inputblock.cn.push(input);
20107             
20108             if(this.inputType != 'radio'){
20109                 inputblock.cn.push(hidden);
20110             }
20111             
20112             if (this.after) {
20113                 inputblock.cn.push({
20114                     tag :'span',
20115                     cls : 'input-group-addon',
20116                     html : this.after
20117                 });
20118             }
20119             
20120         }
20121         
20122         if (align ==='left' && this.fieldLabel.length) {
20123 //                Roo.log("left and has label");
20124             cfg.cn = [
20125                 {
20126                     tag: 'label',
20127                     'for' :  id,
20128                     cls : 'control-label',
20129                     html : this.fieldLabel
20130
20131                 },
20132                 {
20133                     cls : "", 
20134                     cn: [
20135                         inputblock
20136                     ]
20137                 }
20138             ];
20139             
20140             if(this.labelWidth > 12){
20141                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20142             }
20143             
20144             if(this.labelWidth < 13 && this.labelmd == 0){
20145                 this.labelmd = this.labelWidth;
20146             }
20147             
20148             if(this.labellg > 0){
20149                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20150                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20151             }
20152             
20153             if(this.labelmd > 0){
20154                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20155                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20156             }
20157             
20158             if(this.labelsm > 0){
20159                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20160                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20161             }
20162             
20163             if(this.labelxs > 0){
20164                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20165                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20166             }
20167             
20168         } else if ( this.fieldLabel.length) {
20169 //                Roo.log(" label");
20170                 cfg.cn = [
20171                    
20172                     {
20173                         tag: this.boxLabel ? 'span' : 'label',
20174                         'for': id,
20175                         cls: 'control-label box-input-label',
20176                         //cls : 'input-group-addon',
20177                         html : this.fieldLabel
20178                         
20179                     },
20180                     
20181                     inputblock
20182                     
20183                 ];
20184
20185         } else {
20186             
20187 //                Roo.log(" no label && no align");
20188                 cfg.cn = [  inputblock ] ;
20189                 
20190                 
20191         }
20192         
20193         if(this.boxLabel){
20194              var boxLabelCfg = {
20195                 tag: 'label',
20196                 //'for': id, // box label is handled by onclick - so no for...
20197                 cls: 'box-label',
20198                 html: this.boxLabel
20199             };
20200             
20201             if(this.tooltip){
20202                 boxLabelCfg.tooltip = this.tooltip;
20203             }
20204              
20205             cfg.cn.push(boxLabelCfg);
20206         }
20207         
20208         if(this.inputType != 'radio'){
20209             cfg.cn.push(hidden);
20210         }
20211         
20212         return cfg;
20213         
20214     },
20215     
20216     /**
20217      * return the real input element.
20218      */
20219     inputEl: function ()
20220     {
20221         return this.el.select('input.roo-' + this.inputType,true).first();
20222     },
20223     hiddenEl: function ()
20224     {
20225         return this.el.select('input.roo-hidden-value',true).first();
20226     },
20227     
20228     labelEl: function()
20229     {
20230         return this.el.select('label.control-label',true).first();
20231     },
20232     /* depricated... */
20233     
20234     label: function()
20235     {
20236         return this.labelEl();
20237     },
20238     
20239     boxLabelEl: function()
20240     {
20241         return this.el.select('label.box-label',true).first();
20242     },
20243     
20244     initEvents : function()
20245     {
20246 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20247         
20248         this.inputEl().on('click', this.onClick,  this);
20249         
20250         if (this.boxLabel) { 
20251             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20252         }
20253         
20254         this.startValue = this.getValue();
20255         
20256         if(this.groupId){
20257             Roo.bootstrap.CheckBox.register(this);
20258         }
20259     },
20260     
20261     onClick : function()
20262     {   
20263         this.setChecked(!this.checked);
20264     },
20265     
20266     setChecked : function(state,suppressEvent)
20267     {
20268         this.startValue = this.getValue();
20269
20270         if(this.inputType == 'radio'){
20271             
20272             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20273                 e.dom.checked = false;
20274             });
20275             
20276             this.inputEl().dom.checked = true;
20277             
20278             this.inputEl().dom.value = this.inputValue;
20279             
20280             if(suppressEvent !== true){
20281                 this.fireEvent('check', this, true);
20282             }
20283             
20284             this.validate();
20285             
20286             return;
20287         }
20288         
20289         this.checked = state;
20290         
20291         this.inputEl().dom.checked = state;
20292         
20293         
20294         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20295         
20296         if(suppressEvent !== true){
20297             this.fireEvent('check', this, state);
20298         }
20299         
20300         this.validate();
20301     },
20302     
20303     getValue : function()
20304     {
20305         if(this.inputType == 'radio'){
20306             return this.getGroupValue();
20307         }
20308         
20309         return this.hiddenEl().dom.value;
20310         
20311     },
20312     
20313     getGroupValue : function()
20314     {
20315         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20316             return '';
20317         }
20318         
20319         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20320     },
20321     
20322     setValue : function(v,suppressEvent)
20323     {
20324         if(this.inputType == 'radio'){
20325             this.setGroupValue(v, suppressEvent);
20326             return;
20327         }
20328         
20329         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20330         
20331         this.validate();
20332     },
20333     
20334     setGroupValue : function(v, suppressEvent)
20335     {
20336         this.startValue = this.getValue();
20337         
20338         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20339             e.dom.checked = false;
20340             
20341             if(e.dom.value == v){
20342                 e.dom.checked = true;
20343             }
20344         });
20345         
20346         if(suppressEvent !== true){
20347             this.fireEvent('check', this, true);
20348         }
20349
20350         this.validate();
20351         
20352         return;
20353     },
20354     
20355     validate : function()
20356     {
20357         if(
20358                 this.disabled || 
20359                 (this.inputType == 'radio' && this.validateRadio()) ||
20360                 (this.inputType == 'checkbox' && this.validateCheckbox())
20361         ){
20362             this.markValid();
20363             return true;
20364         }
20365         
20366         this.markInvalid();
20367         return false;
20368     },
20369     
20370     validateRadio : function()
20371     {
20372         if(this.allowBlank){
20373             return true;
20374         }
20375         
20376         var valid = false;
20377         
20378         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20379             if(!e.dom.checked){
20380                 return;
20381             }
20382             
20383             valid = true;
20384             
20385             return false;
20386         });
20387         
20388         return valid;
20389     },
20390     
20391     validateCheckbox : function()
20392     {
20393         if(!this.groupId){
20394             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20395             //return (this.getValue() == this.inputValue) ? true : false;
20396         }
20397         
20398         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20399         
20400         if(!group){
20401             return false;
20402         }
20403         
20404         var r = false;
20405         
20406         for(var i in group){
20407             if(r){
20408                 break;
20409             }
20410             
20411             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20412         }
20413         
20414         return r;
20415     },
20416     
20417     /**
20418      * Mark this field as valid
20419      */
20420     markValid : function()
20421     {
20422         var _this = this;
20423         
20424         this.fireEvent('valid', this);
20425         
20426         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20427         
20428         if(this.groupId){
20429             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20430         }
20431         
20432         if(label){
20433             label.markValid();
20434         }
20435
20436         if(this.inputType == 'radio'){
20437             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20438                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20439                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20440             });
20441             
20442             return;
20443         }
20444
20445         if(!this.groupId){
20446             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20447             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20448             return;
20449         }
20450         
20451         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20452         
20453         if(!group){
20454             return;
20455         }
20456         
20457         for(var i in group){
20458             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20459             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20460         }
20461     },
20462     
20463      /**
20464      * Mark this field as invalid
20465      * @param {String} msg The validation message
20466      */
20467     markInvalid : function(msg)
20468     {
20469         if(this.allowBlank){
20470             return;
20471         }
20472         
20473         var _this = this;
20474         
20475         this.fireEvent('invalid', this, msg);
20476         
20477         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20478         
20479         if(this.groupId){
20480             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20481         }
20482         
20483         if(label){
20484             label.markInvalid();
20485         }
20486             
20487         if(this.inputType == 'radio'){
20488             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20489                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20490                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20491             });
20492             
20493             return;
20494         }
20495         
20496         if(!this.groupId){
20497             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20498             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20499             return;
20500         }
20501         
20502         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20503         
20504         if(!group){
20505             return;
20506         }
20507         
20508         for(var i in group){
20509             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20510             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20511         }
20512         
20513     },
20514     
20515     clearInvalid : function()
20516     {
20517         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20518         
20519         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20520         
20521         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20522         
20523         if (label) {
20524             label.iconEl.removeClass(label.validClass);
20525             label.iconEl.removeClass(label.invalidClass);
20526         }
20527     },
20528     
20529     disable : function()
20530     {
20531         if(this.inputType != 'radio'){
20532             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20533             return;
20534         }
20535         
20536         var _this = this;
20537         
20538         if(this.rendered){
20539             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20540                 _this.getActionEl().addClass(this.disabledClass);
20541                 e.dom.disabled = true;
20542             });
20543         }
20544         
20545         this.disabled = true;
20546         this.fireEvent("disable", this);
20547         return this;
20548     },
20549
20550     enable : function()
20551     {
20552         if(this.inputType != 'radio'){
20553             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20554             return;
20555         }
20556         
20557         var _this = this;
20558         
20559         if(this.rendered){
20560             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20561                 _this.getActionEl().removeClass(this.disabledClass);
20562                 e.dom.disabled = false;
20563             });
20564         }
20565         
20566         this.disabled = false;
20567         this.fireEvent("enable", this);
20568         return this;
20569     }
20570
20571 });
20572
20573 Roo.apply(Roo.bootstrap.CheckBox, {
20574     
20575     groups: {},
20576     
20577      /**
20578     * register a CheckBox Group
20579     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20580     */
20581     register : function(checkbox)
20582     {
20583         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20584             this.groups[checkbox.groupId] = {};
20585         }
20586         
20587         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20588             return;
20589         }
20590         
20591         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20592         
20593     },
20594     /**
20595     * fetch a CheckBox Group based on the group ID
20596     * @param {string} the group ID
20597     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20598     */
20599     get: function(groupId) {
20600         if (typeof(this.groups[groupId]) == 'undefined') {
20601             return false;
20602         }
20603         
20604         return this.groups[groupId] ;
20605     }
20606     
20607     
20608 });
20609 /*
20610  * - LGPL
20611  *
20612  * RadioItem
20613  * 
20614  */
20615
20616 /**
20617  * @class Roo.bootstrap.Radio
20618  * @extends Roo.bootstrap.Component
20619  * Bootstrap Radio class
20620  * @cfg {String} boxLabel - the label associated
20621  * @cfg {String} value - the value of radio
20622  * 
20623  * @constructor
20624  * Create a new Radio
20625  * @param {Object} config The config object
20626  */
20627 Roo.bootstrap.Radio = function(config){
20628     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20629     
20630 };
20631
20632 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20633     
20634     boxLabel : '',
20635     
20636     value : '',
20637     
20638     getAutoCreate : function()
20639     {
20640         var cfg = {
20641             tag : 'div',
20642             cls : 'form-group radio',
20643             cn : [
20644                 {
20645                     tag : 'label',
20646                     cls : 'box-label',
20647                     html : this.boxLabel
20648                 }
20649             ]
20650         };
20651         
20652         return cfg;
20653     },
20654     
20655     initEvents : function() 
20656     {
20657         this.parent().register(this);
20658         
20659         this.el.on('click', this.onClick, this);
20660         
20661     },
20662     
20663     onClick : function()
20664     {
20665         this.setChecked(true);
20666     },
20667     
20668     setChecked : function(state, suppressEvent)
20669     {
20670         this.parent().setValue(this.value, suppressEvent);
20671         
20672     }
20673     
20674 });
20675  
20676
20677  //<script type="text/javascript">
20678
20679 /*
20680  * Based  Ext JS Library 1.1.1
20681  * Copyright(c) 2006-2007, Ext JS, LLC.
20682  * LGPL
20683  *
20684  */
20685  
20686 /**
20687  * @class Roo.HtmlEditorCore
20688  * @extends Roo.Component
20689  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20690  *
20691  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20692  */
20693
20694 Roo.HtmlEditorCore = function(config){
20695     
20696     
20697     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20698     
20699     
20700     this.addEvents({
20701         /**
20702          * @event initialize
20703          * Fires when the editor is fully initialized (including the iframe)
20704          * @param {Roo.HtmlEditorCore} this
20705          */
20706         initialize: true,
20707         /**
20708          * @event activate
20709          * Fires when the editor is first receives the focus. Any insertion must wait
20710          * until after this event.
20711          * @param {Roo.HtmlEditorCore} this
20712          */
20713         activate: true,
20714          /**
20715          * @event beforesync
20716          * Fires before the textarea is updated with content from the editor iframe. Return false
20717          * to cancel the sync.
20718          * @param {Roo.HtmlEditorCore} this
20719          * @param {String} html
20720          */
20721         beforesync: true,
20722          /**
20723          * @event beforepush
20724          * Fires before the iframe editor is updated with content from the textarea. Return false
20725          * to cancel the push.
20726          * @param {Roo.HtmlEditorCore} this
20727          * @param {String} html
20728          */
20729         beforepush: true,
20730          /**
20731          * @event sync
20732          * Fires when the textarea is updated with content from the editor iframe.
20733          * @param {Roo.HtmlEditorCore} this
20734          * @param {String} html
20735          */
20736         sync: true,
20737          /**
20738          * @event push
20739          * Fires when the iframe editor is updated with content from the textarea.
20740          * @param {Roo.HtmlEditorCore} this
20741          * @param {String} html
20742          */
20743         push: true,
20744         
20745         /**
20746          * @event editorevent
20747          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20748          * @param {Roo.HtmlEditorCore} this
20749          */
20750         editorevent: true
20751         
20752     });
20753     
20754     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20755     
20756     // defaults : white / black...
20757     this.applyBlacklists();
20758     
20759     
20760     
20761 };
20762
20763
20764 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20765
20766
20767      /**
20768      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20769      */
20770     
20771     owner : false,
20772     
20773      /**
20774      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20775      *                        Roo.resizable.
20776      */
20777     resizable : false,
20778      /**
20779      * @cfg {Number} height (in pixels)
20780      */   
20781     height: 300,
20782    /**
20783      * @cfg {Number} width (in pixels)
20784      */   
20785     width: 500,
20786     
20787     /**
20788      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20789      * 
20790      */
20791     stylesheets: false,
20792     
20793     // id of frame..
20794     frameId: false,
20795     
20796     // private properties
20797     validationEvent : false,
20798     deferHeight: true,
20799     initialized : false,
20800     activated : false,
20801     sourceEditMode : false,
20802     onFocus : Roo.emptyFn,
20803     iframePad:3,
20804     hideMode:'offsets',
20805     
20806     clearUp: true,
20807     
20808     // blacklist + whitelisted elements..
20809     black: false,
20810     white: false,
20811      
20812     
20813
20814     /**
20815      * Protected method that will not generally be called directly. It
20816      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20817      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20818      */
20819     getDocMarkup : function(){
20820         // body styles..
20821         var st = '';
20822         
20823         // inherit styels from page...?? 
20824         if (this.stylesheets === false) {
20825             
20826             Roo.get(document.head).select('style').each(function(node) {
20827                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20828             });
20829             
20830             Roo.get(document.head).select('link').each(function(node) { 
20831                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20832             });
20833             
20834         } else if (!this.stylesheets.length) {
20835                 // simple..
20836                 st = '<style type="text/css">' +
20837                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20838                    '</style>';
20839         } else { 
20840             
20841         }
20842         
20843         st +=  '<style type="text/css">' +
20844             'IMG { cursor: pointer } ' +
20845         '</style>';
20846
20847         
20848         return '<html><head>' + st  +
20849             //<style type="text/css">' +
20850             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20851             //'</style>' +
20852             ' </head><body class="roo-htmleditor-body"></body></html>';
20853     },
20854
20855     // private
20856     onRender : function(ct, position)
20857     {
20858         var _t = this;
20859         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20860         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20861         
20862         
20863         this.el.dom.style.border = '0 none';
20864         this.el.dom.setAttribute('tabIndex', -1);
20865         this.el.addClass('x-hidden hide');
20866         
20867         
20868         
20869         if(Roo.isIE){ // fix IE 1px bogus margin
20870             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20871         }
20872        
20873         
20874         this.frameId = Roo.id();
20875         
20876          
20877         
20878         var iframe = this.owner.wrap.createChild({
20879             tag: 'iframe',
20880             cls: 'form-control', // bootstrap..
20881             id: this.frameId,
20882             name: this.frameId,
20883             frameBorder : 'no',
20884             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20885         }, this.el
20886         );
20887         
20888         
20889         this.iframe = iframe.dom;
20890
20891          this.assignDocWin();
20892         
20893         this.doc.designMode = 'on';
20894        
20895         this.doc.open();
20896         this.doc.write(this.getDocMarkup());
20897         this.doc.close();
20898
20899         
20900         var task = { // must defer to wait for browser to be ready
20901             run : function(){
20902                 //console.log("run task?" + this.doc.readyState);
20903                 this.assignDocWin();
20904                 if(this.doc.body || this.doc.readyState == 'complete'){
20905                     try {
20906                         this.doc.designMode="on";
20907                     } catch (e) {
20908                         return;
20909                     }
20910                     Roo.TaskMgr.stop(task);
20911                     this.initEditor.defer(10, this);
20912                 }
20913             },
20914             interval : 10,
20915             duration: 10000,
20916             scope: this
20917         };
20918         Roo.TaskMgr.start(task);
20919
20920     },
20921
20922     // private
20923     onResize : function(w, h)
20924     {
20925          Roo.log('resize: ' +w + ',' + h );
20926         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20927         if(!this.iframe){
20928             return;
20929         }
20930         if(typeof w == 'number'){
20931             
20932             this.iframe.style.width = w + 'px';
20933         }
20934         if(typeof h == 'number'){
20935             
20936             this.iframe.style.height = h + 'px';
20937             if(this.doc){
20938                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20939             }
20940         }
20941         
20942     },
20943
20944     /**
20945      * Toggles the editor between standard and source edit mode.
20946      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20947      */
20948     toggleSourceEdit : function(sourceEditMode){
20949         
20950         this.sourceEditMode = sourceEditMode === true;
20951         
20952         if(this.sourceEditMode){
20953  
20954             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20955             
20956         }else{
20957             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20958             //this.iframe.className = '';
20959             this.deferFocus();
20960         }
20961         //this.setSize(this.owner.wrap.getSize());
20962         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20963     },
20964
20965     
20966   
20967
20968     /**
20969      * Protected method that will not generally be called directly. If you need/want
20970      * custom HTML cleanup, this is the method you should override.
20971      * @param {String} html The HTML to be cleaned
20972      * return {String} The cleaned HTML
20973      */
20974     cleanHtml : function(html){
20975         html = String(html);
20976         if(html.length > 5){
20977             if(Roo.isSafari){ // strip safari nonsense
20978                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20979             }
20980         }
20981         if(html == '&nbsp;'){
20982             html = '';
20983         }
20984         return html;
20985     },
20986
20987     /**
20988      * HTML Editor -> Textarea
20989      * Protected method that will not generally be called directly. Syncs the contents
20990      * of the editor iframe with the textarea.
20991      */
20992     syncValue : function(){
20993         if(this.initialized){
20994             var bd = (this.doc.body || this.doc.documentElement);
20995             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20996             var html = bd.innerHTML;
20997             if(Roo.isSafari){
20998                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20999                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21000                 if(m && m[1]){
21001                     html = '<div style="'+m[0]+'">' + html + '</div>';
21002                 }
21003             }
21004             html = this.cleanHtml(html);
21005             // fix up the special chars.. normaly like back quotes in word...
21006             // however we do not want to do this with chinese..
21007             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21008                 var cc = b.charCodeAt();
21009                 if (
21010                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21011                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21012                     (cc >= 0xf900 && cc < 0xfb00 )
21013                 ) {
21014                         return b;
21015                 }
21016                 return "&#"+cc+";" 
21017             });
21018             if(this.owner.fireEvent('beforesync', this, html) !== false){
21019                 this.el.dom.value = html;
21020                 this.owner.fireEvent('sync', this, html);
21021             }
21022         }
21023     },
21024
21025     /**
21026      * Protected method that will not generally be called directly. Pushes the value of the textarea
21027      * into the iframe editor.
21028      */
21029     pushValue : function(){
21030         if(this.initialized){
21031             var v = this.el.dom.value.trim();
21032             
21033 //            if(v.length < 1){
21034 //                v = '&#160;';
21035 //            }
21036             
21037             if(this.owner.fireEvent('beforepush', this, v) !== false){
21038                 var d = (this.doc.body || this.doc.documentElement);
21039                 d.innerHTML = v;
21040                 this.cleanUpPaste();
21041                 this.el.dom.value = d.innerHTML;
21042                 this.owner.fireEvent('push', this, v);
21043             }
21044         }
21045     },
21046
21047     // private
21048     deferFocus : function(){
21049         this.focus.defer(10, this);
21050     },
21051
21052     // doc'ed in Field
21053     focus : function(){
21054         if(this.win && !this.sourceEditMode){
21055             this.win.focus();
21056         }else{
21057             this.el.focus();
21058         }
21059     },
21060     
21061     assignDocWin: function()
21062     {
21063         var iframe = this.iframe;
21064         
21065          if(Roo.isIE){
21066             this.doc = iframe.contentWindow.document;
21067             this.win = iframe.contentWindow;
21068         } else {
21069 //            if (!Roo.get(this.frameId)) {
21070 //                return;
21071 //            }
21072 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21073 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21074             
21075             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21076                 return;
21077             }
21078             
21079             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21080             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21081         }
21082     },
21083     
21084     // private
21085     initEditor : function(){
21086         //console.log("INIT EDITOR");
21087         this.assignDocWin();
21088         
21089         
21090         
21091         this.doc.designMode="on";
21092         this.doc.open();
21093         this.doc.write(this.getDocMarkup());
21094         this.doc.close();
21095         
21096         var dbody = (this.doc.body || this.doc.documentElement);
21097         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21098         // this copies styles from the containing element into thsi one..
21099         // not sure why we need all of this..
21100         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21101         
21102         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21103         //ss['background-attachment'] = 'fixed'; // w3c
21104         dbody.bgProperties = 'fixed'; // ie
21105         //Roo.DomHelper.applyStyles(dbody, ss);
21106         Roo.EventManager.on(this.doc, {
21107             //'mousedown': this.onEditorEvent,
21108             'mouseup': this.onEditorEvent,
21109             'dblclick': this.onEditorEvent,
21110             'click': this.onEditorEvent,
21111             'keyup': this.onEditorEvent,
21112             buffer:100,
21113             scope: this
21114         });
21115         if(Roo.isGecko){
21116             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21117         }
21118         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21119             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21120         }
21121         this.initialized = true;
21122
21123         this.owner.fireEvent('initialize', this);
21124         this.pushValue();
21125     },
21126
21127     // private
21128     onDestroy : function(){
21129         
21130         
21131         
21132         if(this.rendered){
21133             
21134             //for (var i =0; i < this.toolbars.length;i++) {
21135             //    // fixme - ask toolbars for heights?
21136             //    this.toolbars[i].onDestroy();
21137            // }
21138             
21139             //this.wrap.dom.innerHTML = '';
21140             //this.wrap.remove();
21141         }
21142     },
21143
21144     // private
21145     onFirstFocus : function(){
21146         
21147         this.assignDocWin();
21148         
21149         
21150         this.activated = true;
21151          
21152     
21153         if(Roo.isGecko){ // prevent silly gecko errors
21154             this.win.focus();
21155             var s = this.win.getSelection();
21156             if(!s.focusNode || s.focusNode.nodeType != 3){
21157                 var r = s.getRangeAt(0);
21158                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21159                 r.collapse(true);
21160                 this.deferFocus();
21161             }
21162             try{
21163                 this.execCmd('useCSS', true);
21164                 this.execCmd('styleWithCSS', false);
21165             }catch(e){}
21166         }
21167         this.owner.fireEvent('activate', this);
21168     },
21169
21170     // private
21171     adjustFont: function(btn){
21172         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21173         //if(Roo.isSafari){ // safari
21174         //    adjust *= 2;
21175        // }
21176         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21177         if(Roo.isSafari){ // safari
21178             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21179             v =  (v < 10) ? 10 : v;
21180             v =  (v > 48) ? 48 : v;
21181             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21182             
21183         }
21184         
21185         
21186         v = Math.max(1, v+adjust);
21187         
21188         this.execCmd('FontSize', v  );
21189     },
21190
21191     onEditorEvent : function(e)
21192     {
21193         this.owner.fireEvent('editorevent', this, e);
21194       //  this.updateToolbar();
21195         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21196     },
21197
21198     insertTag : function(tg)
21199     {
21200         // could be a bit smarter... -> wrap the current selected tRoo..
21201         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21202             
21203             range = this.createRange(this.getSelection());
21204             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21205             wrappingNode.appendChild(range.extractContents());
21206             range.insertNode(wrappingNode);
21207
21208             return;
21209             
21210             
21211             
21212         }
21213         this.execCmd("formatblock",   tg);
21214         
21215     },
21216     
21217     insertText : function(txt)
21218     {
21219         
21220         
21221         var range = this.createRange();
21222         range.deleteContents();
21223                //alert(Sender.getAttribute('label'));
21224                
21225         range.insertNode(this.doc.createTextNode(txt));
21226     } ,
21227     
21228      
21229
21230     /**
21231      * Executes a Midas editor command on the editor document and performs necessary focus and
21232      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21233      * @param {String} cmd The Midas command
21234      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21235      */
21236     relayCmd : function(cmd, value){
21237         this.win.focus();
21238         this.execCmd(cmd, value);
21239         this.owner.fireEvent('editorevent', this);
21240         //this.updateToolbar();
21241         this.owner.deferFocus();
21242     },
21243
21244     /**
21245      * Executes a Midas editor command directly on the editor document.
21246      * For visual commands, you should use {@link #relayCmd} instead.
21247      * <b>This should only be called after the editor is initialized.</b>
21248      * @param {String} cmd The Midas command
21249      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21250      */
21251     execCmd : function(cmd, value){
21252         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21253         this.syncValue();
21254     },
21255  
21256  
21257    
21258     /**
21259      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21260      * to insert tRoo.
21261      * @param {String} text | dom node.. 
21262      */
21263     insertAtCursor : function(text)
21264     {
21265         
21266         
21267         
21268         if(!this.activated){
21269             return;
21270         }
21271         /*
21272         if(Roo.isIE){
21273             this.win.focus();
21274             var r = this.doc.selection.createRange();
21275             if(r){
21276                 r.collapse(true);
21277                 r.pasteHTML(text);
21278                 this.syncValue();
21279                 this.deferFocus();
21280             
21281             }
21282             return;
21283         }
21284         */
21285         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21286             this.win.focus();
21287             
21288             
21289             // from jquery ui (MIT licenced)
21290             var range, node;
21291             var win = this.win;
21292             
21293             if (win.getSelection && win.getSelection().getRangeAt) {
21294                 range = win.getSelection().getRangeAt(0);
21295                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21296                 range.insertNode(node);
21297             } else if (win.document.selection && win.document.selection.createRange) {
21298                 // no firefox support
21299                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21300                 win.document.selection.createRange().pasteHTML(txt);
21301             } else {
21302                 // no firefox support
21303                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21304                 this.execCmd('InsertHTML', txt);
21305             } 
21306             
21307             this.syncValue();
21308             
21309             this.deferFocus();
21310         }
21311     },
21312  // private
21313     mozKeyPress : function(e){
21314         if(e.ctrlKey){
21315             var c = e.getCharCode(), cmd;
21316           
21317             if(c > 0){
21318                 c = String.fromCharCode(c).toLowerCase();
21319                 switch(c){
21320                     case 'b':
21321                         cmd = 'bold';
21322                         break;
21323                     case 'i':
21324                         cmd = 'italic';
21325                         break;
21326                     
21327                     case 'u':
21328                         cmd = 'underline';
21329                         break;
21330                     
21331                     case 'v':
21332                         this.cleanUpPaste.defer(100, this);
21333                         return;
21334                         
21335                 }
21336                 if(cmd){
21337                     this.win.focus();
21338                     this.execCmd(cmd);
21339                     this.deferFocus();
21340                     e.preventDefault();
21341                 }
21342                 
21343             }
21344         }
21345     },
21346
21347     // private
21348     fixKeys : function(){ // load time branching for fastest keydown performance
21349         if(Roo.isIE){
21350             return function(e){
21351                 var k = e.getKey(), r;
21352                 if(k == e.TAB){
21353                     e.stopEvent();
21354                     r = this.doc.selection.createRange();
21355                     if(r){
21356                         r.collapse(true);
21357                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21358                         this.deferFocus();
21359                     }
21360                     return;
21361                 }
21362                 
21363                 if(k == e.ENTER){
21364                     r = this.doc.selection.createRange();
21365                     if(r){
21366                         var target = r.parentElement();
21367                         if(!target || target.tagName.toLowerCase() != 'li'){
21368                             e.stopEvent();
21369                             r.pasteHTML('<br />');
21370                             r.collapse(false);
21371                             r.select();
21372                         }
21373                     }
21374                 }
21375                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21376                     this.cleanUpPaste.defer(100, this);
21377                     return;
21378                 }
21379                 
21380                 
21381             };
21382         }else if(Roo.isOpera){
21383             return function(e){
21384                 var k = e.getKey();
21385                 if(k == e.TAB){
21386                     e.stopEvent();
21387                     this.win.focus();
21388                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21389                     this.deferFocus();
21390                 }
21391                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21392                     this.cleanUpPaste.defer(100, this);
21393                     return;
21394                 }
21395                 
21396             };
21397         }else if(Roo.isSafari){
21398             return function(e){
21399                 var k = e.getKey();
21400                 
21401                 if(k == e.TAB){
21402                     e.stopEvent();
21403                     this.execCmd('InsertText','\t');
21404                     this.deferFocus();
21405                     return;
21406                 }
21407                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21408                     this.cleanUpPaste.defer(100, this);
21409                     return;
21410                 }
21411                 
21412              };
21413         }
21414     }(),
21415     
21416     getAllAncestors: function()
21417     {
21418         var p = this.getSelectedNode();
21419         var a = [];
21420         if (!p) {
21421             a.push(p); // push blank onto stack..
21422             p = this.getParentElement();
21423         }
21424         
21425         
21426         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21427             a.push(p);
21428             p = p.parentNode;
21429         }
21430         a.push(this.doc.body);
21431         return a;
21432     },
21433     lastSel : false,
21434     lastSelNode : false,
21435     
21436     
21437     getSelection : function() 
21438     {
21439         this.assignDocWin();
21440         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21441     },
21442     
21443     getSelectedNode: function() 
21444     {
21445         // this may only work on Gecko!!!
21446         
21447         // should we cache this!!!!
21448         
21449         
21450         
21451          
21452         var range = this.createRange(this.getSelection()).cloneRange();
21453         
21454         if (Roo.isIE) {
21455             var parent = range.parentElement();
21456             while (true) {
21457                 var testRange = range.duplicate();
21458                 testRange.moveToElementText(parent);
21459                 if (testRange.inRange(range)) {
21460                     break;
21461                 }
21462                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21463                     break;
21464                 }
21465                 parent = parent.parentElement;
21466             }
21467             return parent;
21468         }
21469         
21470         // is ancestor a text element.
21471         var ac =  range.commonAncestorContainer;
21472         if (ac.nodeType == 3) {
21473             ac = ac.parentNode;
21474         }
21475         
21476         var ar = ac.childNodes;
21477          
21478         var nodes = [];
21479         var other_nodes = [];
21480         var has_other_nodes = false;
21481         for (var i=0;i<ar.length;i++) {
21482             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21483                 continue;
21484             }
21485             // fullly contained node.
21486             
21487             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21488                 nodes.push(ar[i]);
21489                 continue;
21490             }
21491             
21492             // probably selected..
21493             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21494                 other_nodes.push(ar[i]);
21495                 continue;
21496             }
21497             // outer..
21498             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21499                 continue;
21500             }
21501             
21502             
21503             has_other_nodes = true;
21504         }
21505         if (!nodes.length && other_nodes.length) {
21506             nodes= other_nodes;
21507         }
21508         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21509             return false;
21510         }
21511         
21512         return nodes[0];
21513     },
21514     createRange: function(sel)
21515     {
21516         // this has strange effects when using with 
21517         // top toolbar - not sure if it's a great idea.
21518         //this.editor.contentWindow.focus();
21519         if (typeof sel != "undefined") {
21520             try {
21521                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21522             } catch(e) {
21523                 return this.doc.createRange();
21524             }
21525         } else {
21526             return this.doc.createRange();
21527         }
21528     },
21529     getParentElement: function()
21530     {
21531         
21532         this.assignDocWin();
21533         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21534         
21535         var range = this.createRange(sel);
21536          
21537         try {
21538             var p = range.commonAncestorContainer;
21539             while (p.nodeType == 3) { // text node
21540                 p = p.parentNode;
21541             }
21542             return p;
21543         } catch (e) {
21544             return null;
21545         }
21546     
21547     },
21548     /***
21549      *
21550      * Range intersection.. the hard stuff...
21551      *  '-1' = before
21552      *  '0' = hits..
21553      *  '1' = after.
21554      *         [ -- selected range --- ]
21555      *   [fail]                        [fail]
21556      *
21557      *    basically..
21558      *      if end is before start or  hits it. fail.
21559      *      if start is after end or hits it fail.
21560      *
21561      *   if either hits (but other is outside. - then it's not 
21562      *   
21563      *    
21564      **/
21565     
21566     
21567     // @see http://www.thismuchiknow.co.uk/?p=64.
21568     rangeIntersectsNode : function(range, node)
21569     {
21570         var nodeRange = node.ownerDocument.createRange();
21571         try {
21572             nodeRange.selectNode(node);
21573         } catch (e) {
21574             nodeRange.selectNodeContents(node);
21575         }
21576     
21577         var rangeStartRange = range.cloneRange();
21578         rangeStartRange.collapse(true);
21579     
21580         var rangeEndRange = range.cloneRange();
21581         rangeEndRange.collapse(false);
21582     
21583         var nodeStartRange = nodeRange.cloneRange();
21584         nodeStartRange.collapse(true);
21585     
21586         var nodeEndRange = nodeRange.cloneRange();
21587         nodeEndRange.collapse(false);
21588     
21589         return rangeStartRange.compareBoundaryPoints(
21590                  Range.START_TO_START, nodeEndRange) == -1 &&
21591                rangeEndRange.compareBoundaryPoints(
21592                  Range.START_TO_START, nodeStartRange) == 1;
21593         
21594          
21595     },
21596     rangeCompareNode : function(range, node)
21597     {
21598         var nodeRange = node.ownerDocument.createRange();
21599         try {
21600             nodeRange.selectNode(node);
21601         } catch (e) {
21602             nodeRange.selectNodeContents(node);
21603         }
21604         
21605         
21606         range.collapse(true);
21607     
21608         nodeRange.collapse(true);
21609      
21610         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21611         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21612          
21613         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21614         
21615         var nodeIsBefore   =  ss == 1;
21616         var nodeIsAfter    = ee == -1;
21617         
21618         if (nodeIsBefore && nodeIsAfter) {
21619             return 0; // outer
21620         }
21621         if (!nodeIsBefore && nodeIsAfter) {
21622             return 1; //right trailed.
21623         }
21624         
21625         if (nodeIsBefore && !nodeIsAfter) {
21626             return 2;  // left trailed.
21627         }
21628         // fully contined.
21629         return 3;
21630     },
21631
21632     // private? - in a new class?
21633     cleanUpPaste :  function()
21634     {
21635         // cleans up the whole document..
21636         Roo.log('cleanuppaste');
21637         
21638         this.cleanUpChildren(this.doc.body);
21639         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21640         if (clean != this.doc.body.innerHTML) {
21641             this.doc.body.innerHTML = clean;
21642         }
21643         
21644     },
21645     
21646     cleanWordChars : function(input) {// change the chars to hex code
21647         var he = Roo.HtmlEditorCore;
21648         
21649         var output = input;
21650         Roo.each(he.swapCodes, function(sw) { 
21651             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21652             
21653             output = output.replace(swapper, sw[1]);
21654         });
21655         
21656         return output;
21657     },
21658     
21659     
21660     cleanUpChildren : function (n)
21661     {
21662         if (!n.childNodes.length) {
21663             return;
21664         }
21665         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21666            this.cleanUpChild(n.childNodes[i]);
21667         }
21668     },
21669     
21670     
21671         
21672     
21673     cleanUpChild : function (node)
21674     {
21675         var ed = this;
21676         //console.log(node);
21677         if (node.nodeName == "#text") {
21678             // clean up silly Windows -- stuff?
21679             return; 
21680         }
21681         if (node.nodeName == "#comment") {
21682             node.parentNode.removeChild(node);
21683             // clean up silly Windows -- stuff?
21684             return; 
21685         }
21686         var lcname = node.tagName.toLowerCase();
21687         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21688         // whitelist of tags..
21689         
21690         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21691             // remove node.
21692             node.parentNode.removeChild(node);
21693             return;
21694             
21695         }
21696         
21697         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21698         
21699         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21700         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21701         
21702         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21703         //    remove_keep_children = true;
21704         //}
21705         
21706         if (remove_keep_children) {
21707             this.cleanUpChildren(node);
21708             // inserts everything just before this node...
21709             while (node.childNodes.length) {
21710                 var cn = node.childNodes[0];
21711                 node.removeChild(cn);
21712                 node.parentNode.insertBefore(cn, node);
21713             }
21714             node.parentNode.removeChild(node);
21715             return;
21716         }
21717         
21718         if (!node.attributes || !node.attributes.length) {
21719             this.cleanUpChildren(node);
21720             return;
21721         }
21722         
21723         function cleanAttr(n,v)
21724         {
21725             
21726             if (v.match(/^\./) || v.match(/^\//)) {
21727                 return;
21728             }
21729             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21730                 return;
21731             }
21732             if (v.match(/^#/)) {
21733                 return;
21734             }
21735 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21736             node.removeAttribute(n);
21737             
21738         }
21739         
21740         var cwhite = this.cwhite;
21741         var cblack = this.cblack;
21742             
21743         function cleanStyle(n,v)
21744         {
21745             if (v.match(/expression/)) { //XSS?? should we even bother..
21746                 node.removeAttribute(n);
21747                 return;
21748             }
21749             
21750             var parts = v.split(/;/);
21751             var clean = [];
21752             
21753             Roo.each(parts, function(p) {
21754                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21755                 if (!p.length) {
21756                     return true;
21757                 }
21758                 var l = p.split(':').shift().replace(/\s+/g,'');
21759                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21760                 
21761                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21762 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21763                     //node.removeAttribute(n);
21764                     return true;
21765                 }
21766                 //Roo.log()
21767                 // only allow 'c whitelisted system attributes'
21768                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21769 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21770                     //node.removeAttribute(n);
21771                     return true;
21772                 }
21773                 
21774                 
21775                  
21776                 
21777                 clean.push(p);
21778                 return true;
21779             });
21780             if (clean.length) { 
21781                 node.setAttribute(n, clean.join(';'));
21782             } else {
21783                 node.removeAttribute(n);
21784             }
21785             
21786         }
21787         
21788         
21789         for (var i = node.attributes.length-1; i > -1 ; i--) {
21790             var a = node.attributes[i];
21791             //console.log(a);
21792             
21793             if (a.name.toLowerCase().substr(0,2)=='on')  {
21794                 node.removeAttribute(a.name);
21795                 continue;
21796             }
21797             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21798                 node.removeAttribute(a.name);
21799                 continue;
21800             }
21801             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21802                 cleanAttr(a.name,a.value); // fixme..
21803                 continue;
21804             }
21805             if (a.name == 'style') {
21806                 cleanStyle(a.name,a.value);
21807                 continue;
21808             }
21809             /// clean up MS crap..
21810             // tecnically this should be a list of valid class'es..
21811             
21812             
21813             if (a.name == 'class') {
21814                 if (a.value.match(/^Mso/)) {
21815                     node.className = '';
21816                 }
21817                 
21818                 if (a.value.match(/body/)) {
21819                     node.className = '';
21820                 }
21821                 continue;
21822             }
21823             
21824             // style cleanup!?
21825             // class cleanup?
21826             
21827         }
21828         
21829         
21830         this.cleanUpChildren(node);
21831         
21832         
21833     },
21834     
21835     /**
21836      * Clean up MS wordisms...
21837      */
21838     cleanWord : function(node)
21839     {
21840         
21841         
21842         if (!node) {
21843             this.cleanWord(this.doc.body);
21844             return;
21845         }
21846         if (node.nodeName == "#text") {
21847             // clean up silly Windows -- stuff?
21848             return; 
21849         }
21850         if (node.nodeName == "#comment") {
21851             node.parentNode.removeChild(node);
21852             // clean up silly Windows -- stuff?
21853             return; 
21854         }
21855         
21856         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21857             node.parentNode.removeChild(node);
21858             return;
21859         }
21860         
21861         // remove - but keep children..
21862         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21863             while (node.childNodes.length) {
21864                 var cn = node.childNodes[0];
21865                 node.removeChild(cn);
21866                 node.parentNode.insertBefore(cn, node);
21867             }
21868             node.parentNode.removeChild(node);
21869             this.iterateChildren(node, this.cleanWord);
21870             return;
21871         }
21872         // clean styles
21873         if (node.className.length) {
21874             
21875             var cn = node.className.split(/\W+/);
21876             var cna = [];
21877             Roo.each(cn, function(cls) {
21878                 if (cls.match(/Mso[a-zA-Z]+/)) {
21879                     return;
21880                 }
21881                 cna.push(cls);
21882             });
21883             node.className = cna.length ? cna.join(' ') : '';
21884             if (!cna.length) {
21885                 node.removeAttribute("class");
21886             }
21887         }
21888         
21889         if (node.hasAttribute("lang")) {
21890             node.removeAttribute("lang");
21891         }
21892         
21893         if (node.hasAttribute("style")) {
21894             
21895             var styles = node.getAttribute("style").split(";");
21896             var nstyle = [];
21897             Roo.each(styles, function(s) {
21898                 if (!s.match(/:/)) {
21899                     return;
21900                 }
21901                 var kv = s.split(":");
21902                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21903                     return;
21904                 }
21905                 // what ever is left... we allow.
21906                 nstyle.push(s);
21907             });
21908             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21909             if (!nstyle.length) {
21910                 node.removeAttribute('style');
21911             }
21912         }
21913         this.iterateChildren(node, this.cleanWord);
21914         
21915         
21916         
21917     },
21918     /**
21919      * iterateChildren of a Node, calling fn each time, using this as the scole..
21920      * @param {DomNode} node node to iterate children of.
21921      * @param {Function} fn method of this class to call on each item.
21922      */
21923     iterateChildren : function(node, fn)
21924     {
21925         if (!node.childNodes.length) {
21926                 return;
21927         }
21928         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21929            fn.call(this, node.childNodes[i])
21930         }
21931     },
21932     
21933     
21934     /**
21935      * cleanTableWidths.
21936      *
21937      * Quite often pasting from word etc.. results in tables with column and widths.
21938      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21939      *
21940      */
21941     cleanTableWidths : function(node)
21942     {
21943          
21944          
21945         if (!node) {
21946             this.cleanTableWidths(this.doc.body);
21947             return;
21948         }
21949         
21950         // ignore list...
21951         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21952             return; 
21953         }
21954         Roo.log(node.tagName);
21955         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21956             this.iterateChildren(node, this.cleanTableWidths);
21957             return;
21958         }
21959         if (node.hasAttribute('width')) {
21960             node.removeAttribute('width');
21961         }
21962         
21963          
21964         if (node.hasAttribute("style")) {
21965             // pretty basic...
21966             
21967             var styles = node.getAttribute("style").split(";");
21968             var nstyle = [];
21969             Roo.each(styles, function(s) {
21970                 if (!s.match(/:/)) {
21971                     return;
21972                 }
21973                 var kv = s.split(":");
21974                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21975                     return;
21976                 }
21977                 // what ever is left... we allow.
21978                 nstyle.push(s);
21979             });
21980             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21981             if (!nstyle.length) {
21982                 node.removeAttribute('style');
21983             }
21984         }
21985         
21986         this.iterateChildren(node, this.cleanTableWidths);
21987         
21988         
21989     },
21990     
21991     
21992     
21993     
21994     domToHTML : function(currentElement, depth, nopadtext) {
21995         
21996         depth = depth || 0;
21997         nopadtext = nopadtext || false;
21998     
21999         if (!currentElement) {
22000             return this.domToHTML(this.doc.body);
22001         }
22002         
22003         //Roo.log(currentElement);
22004         var j;
22005         var allText = false;
22006         var nodeName = currentElement.nodeName;
22007         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22008         
22009         if  (nodeName == '#text') {
22010             
22011             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22012         }
22013         
22014         
22015         var ret = '';
22016         if (nodeName != 'BODY') {
22017              
22018             var i = 0;
22019             // Prints the node tagName, such as <A>, <IMG>, etc
22020             if (tagName) {
22021                 var attr = [];
22022                 for(i = 0; i < currentElement.attributes.length;i++) {
22023                     // quoting?
22024                     var aname = currentElement.attributes.item(i).name;
22025                     if (!currentElement.attributes.item(i).value.length) {
22026                         continue;
22027                     }
22028                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22029                 }
22030                 
22031                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22032             } 
22033             else {
22034                 
22035                 // eack
22036             }
22037         } else {
22038             tagName = false;
22039         }
22040         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22041             return ret;
22042         }
22043         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22044             nopadtext = true;
22045         }
22046         
22047         
22048         // Traverse the tree
22049         i = 0;
22050         var currentElementChild = currentElement.childNodes.item(i);
22051         var allText = true;
22052         var innerHTML  = '';
22053         lastnode = '';
22054         while (currentElementChild) {
22055             // Formatting code (indent the tree so it looks nice on the screen)
22056             var nopad = nopadtext;
22057             if (lastnode == 'SPAN') {
22058                 nopad  = true;
22059             }
22060             // text
22061             if  (currentElementChild.nodeName == '#text') {
22062                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22063                 toadd = nopadtext ? toadd : toadd.trim();
22064                 if (!nopad && toadd.length > 80) {
22065                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22066                 }
22067                 innerHTML  += toadd;
22068                 
22069                 i++;
22070                 currentElementChild = currentElement.childNodes.item(i);
22071                 lastNode = '';
22072                 continue;
22073             }
22074             allText = false;
22075             
22076             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22077                 
22078             // Recursively traverse the tree structure of the child node
22079             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22080             lastnode = currentElementChild.nodeName;
22081             i++;
22082             currentElementChild=currentElement.childNodes.item(i);
22083         }
22084         
22085         ret += innerHTML;
22086         
22087         if (!allText) {
22088                 // The remaining code is mostly for formatting the tree
22089             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22090         }
22091         
22092         
22093         if (tagName) {
22094             ret+= "</"+tagName+">";
22095         }
22096         return ret;
22097         
22098     },
22099         
22100     applyBlacklists : function()
22101     {
22102         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22103         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22104         
22105         this.white = [];
22106         this.black = [];
22107         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22108             if (b.indexOf(tag) > -1) {
22109                 return;
22110             }
22111             this.white.push(tag);
22112             
22113         }, this);
22114         
22115         Roo.each(w, function(tag) {
22116             if (b.indexOf(tag) > -1) {
22117                 return;
22118             }
22119             if (this.white.indexOf(tag) > -1) {
22120                 return;
22121             }
22122             this.white.push(tag);
22123             
22124         }, this);
22125         
22126         
22127         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22128             if (w.indexOf(tag) > -1) {
22129                 return;
22130             }
22131             this.black.push(tag);
22132             
22133         }, this);
22134         
22135         Roo.each(b, function(tag) {
22136             if (w.indexOf(tag) > -1) {
22137                 return;
22138             }
22139             if (this.black.indexOf(tag) > -1) {
22140                 return;
22141             }
22142             this.black.push(tag);
22143             
22144         }, this);
22145         
22146         
22147         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22148         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22149         
22150         this.cwhite = [];
22151         this.cblack = [];
22152         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22153             if (b.indexOf(tag) > -1) {
22154                 return;
22155             }
22156             this.cwhite.push(tag);
22157             
22158         }, this);
22159         
22160         Roo.each(w, function(tag) {
22161             if (b.indexOf(tag) > -1) {
22162                 return;
22163             }
22164             if (this.cwhite.indexOf(tag) > -1) {
22165                 return;
22166             }
22167             this.cwhite.push(tag);
22168             
22169         }, this);
22170         
22171         
22172         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22173             if (w.indexOf(tag) > -1) {
22174                 return;
22175             }
22176             this.cblack.push(tag);
22177             
22178         }, this);
22179         
22180         Roo.each(b, function(tag) {
22181             if (w.indexOf(tag) > -1) {
22182                 return;
22183             }
22184             if (this.cblack.indexOf(tag) > -1) {
22185                 return;
22186             }
22187             this.cblack.push(tag);
22188             
22189         }, this);
22190     },
22191     
22192     setStylesheets : function(stylesheets)
22193     {
22194         if(typeof(stylesheets) == 'string'){
22195             Roo.get(this.iframe.contentDocument.head).createChild({
22196                 tag : 'link',
22197                 rel : 'stylesheet',
22198                 type : 'text/css',
22199                 href : stylesheets
22200             });
22201             
22202             return;
22203         }
22204         var _this = this;
22205      
22206         Roo.each(stylesheets, function(s) {
22207             if(!s.length){
22208                 return;
22209             }
22210             
22211             Roo.get(_this.iframe.contentDocument.head).createChild({
22212                 tag : 'link',
22213                 rel : 'stylesheet',
22214                 type : 'text/css',
22215                 href : s
22216             });
22217         });
22218
22219         
22220     },
22221     
22222     removeStylesheets : function()
22223     {
22224         var _this = this;
22225         
22226         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22227             s.remove();
22228         });
22229     }
22230     
22231     // hide stuff that is not compatible
22232     /**
22233      * @event blur
22234      * @hide
22235      */
22236     /**
22237      * @event change
22238      * @hide
22239      */
22240     /**
22241      * @event focus
22242      * @hide
22243      */
22244     /**
22245      * @event specialkey
22246      * @hide
22247      */
22248     /**
22249      * @cfg {String} fieldClass @hide
22250      */
22251     /**
22252      * @cfg {String} focusClass @hide
22253      */
22254     /**
22255      * @cfg {String} autoCreate @hide
22256      */
22257     /**
22258      * @cfg {String} inputType @hide
22259      */
22260     /**
22261      * @cfg {String} invalidClass @hide
22262      */
22263     /**
22264      * @cfg {String} invalidText @hide
22265      */
22266     /**
22267      * @cfg {String} msgFx @hide
22268      */
22269     /**
22270      * @cfg {String} validateOnBlur @hide
22271      */
22272 });
22273
22274 Roo.HtmlEditorCore.white = [
22275         'area', 'br', 'img', 'input', 'hr', 'wbr',
22276         
22277        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22278        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22279        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22280        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22281        'table',   'ul',         'xmp', 
22282        
22283        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22284       'thead',   'tr', 
22285      
22286       'dir', 'menu', 'ol', 'ul', 'dl',
22287        
22288       'embed',  'object'
22289 ];
22290
22291
22292 Roo.HtmlEditorCore.black = [
22293     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22294         'applet', // 
22295         'base',   'basefont', 'bgsound', 'blink',  'body', 
22296         'frame',  'frameset', 'head',    'html',   'ilayer', 
22297         'iframe', 'layer',  'link',     'meta',    'object',   
22298         'script', 'style' ,'title',  'xml' // clean later..
22299 ];
22300 Roo.HtmlEditorCore.clean = [
22301     'script', 'style', 'title', 'xml'
22302 ];
22303 Roo.HtmlEditorCore.remove = [
22304     'font'
22305 ];
22306 // attributes..
22307
22308 Roo.HtmlEditorCore.ablack = [
22309     'on'
22310 ];
22311     
22312 Roo.HtmlEditorCore.aclean = [ 
22313     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22314 ];
22315
22316 // protocols..
22317 Roo.HtmlEditorCore.pwhite= [
22318         'http',  'https',  'mailto'
22319 ];
22320
22321 // white listed style attributes.
22322 Roo.HtmlEditorCore.cwhite= [
22323       //  'text-align', /// default is to allow most things..
22324       
22325          
22326 //        'font-size'//??
22327 ];
22328
22329 // black listed style attributes.
22330 Roo.HtmlEditorCore.cblack= [
22331       //  'font-size' -- this can be set by the project 
22332 ];
22333
22334
22335 Roo.HtmlEditorCore.swapCodes   =[ 
22336     [    8211, "--" ], 
22337     [    8212, "--" ], 
22338     [    8216,  "'" ],  
22339     [    8217, "'" ],  
22340     [    8220, '"' ],  
22341     [    8221, '"' ],  
22342     [    8226, "*" ],  
22343     [    8230, "..." ]
22344 ]; 
22345
22346     /*
22347  * - LGPL
22348  *
22349  * HtmlEditor
22350  * 
22351  */
22352
22353 /**
22354  * @class Roo.bootstrap.HtmlEditor
22355  * @extends Roo.bootstrap.TextArea
22356  * Bootstrap HtmlEditor class
22357
22358  * @constructor
22359  * Create a new HtmlEditor
22360  * @param {Object} config The config object
22361  */
22362
22363 Roo.bootstrap.HtmlEditor = function(config){
22364     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22365     if (!this.toolbars) {
22366         this.toolbars = [];
22367     }
22368     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22369     this.addEvents({
22370             /**
22371              * @event initialize
22372              * Fires when the editor is fully initialized (including the iframe)
22373              * @param {HtmlEditor} this
22374              */
22375             initialize: true,
22376             /**
22377              * @event activate
22378              * Fires when the editor is first receives the focus. Any insertion must wait
22379              * until after this event.
22380              * @param {HtmlEditor} this
22381              */
22382             activate: true,
22383              /**
22384              * @event beforesync
22385              * Fires before the textarea is updated with content from the editor iframe. Return false
22386              * to cancel the sync.
22387              * @param {HtmlEditor} this
22388              * @param {String} html
22389              */
22390             beforesync: true,
22391              /**
22392              * @event beforepush
22393              * Fires before the iframe editor is updated with content from the textarea. Return false
22394              * to cancel the push.
22395              * @param {HtmlEditor} this
22396              * @param {String} html
22397              */
22398             beforepush: true,
22399              /**
22400              * @event sync
22401              * Fires when the textarea is updated with content from the editor iframe.
22402              * @param {HtmlEditor} this
22403              * @param {String} html
22404              */
22405             sync: true,
22406              /**
22407              * @event push
22408              * Fires when the iframe editor is updated with content from the textarea.
22409              * @param {HtmlEditor} this
22410              * @param {String} html
22411              */
22412             push: true,
22413              /**
22414              * @event editmodechange
22415              * Fires when the editor switches edit modes
22416              * @param {HtmlEditor} this
22417              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22418              */
22419             editmodechange: true,
22420             /**
22421              * @event editorevent
22422              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22423              * @param {HtmlEditor} this
22424              */
22425             editorevent: true,
22426             /**
22427              * @event firstfocus
22428              * Fires when on first focus - needed by toolbars..
22429              * @param {HtmlEditor} this
22430              */
22431             firstfocus: true,
22432             /**
22433              * @event autosave
22434              * Auto save the htmlEditor value as a file into Events
22435              * @param {HtmlEditor} this
22436              */
22437             autosave: true,
22438             /**
22439              * @event savedpreview
22440              * preview the saved version of htmlEditor
22441              * @param {HtmlEditor} this
22442              */
22443             savedpreview: true
22444         });
22445 };
22446
22447
22448 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22449     
22450     
22451       /**
22452      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22453      */
22454     toolbars : false,
22455    
22456      /**
22457      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22458      *                        Roo.resizable.
22459      */
22460     resizable : false,
22461      /**
22462      * @cfg {Number} height (in pixels)
22463      */   
22464     height: 300,
22465    /**
22466      * @cfg {Number} width (in pixels)
22467      */   
22468     width: false,
22469     
22470     /**
22471      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22472      * 
22473      */
22474     stylesheets: false,
22475     
22476     // id of frame..
22477     frameId: false,
22478     
22479     // private properties
22480     validationEvent : false,
22481     deferHeight: true,
22482     initialized : false,
22483     activated : false,
22484     
22485     onFocus : Roo.emptyFn,
22486     iframePad:3,
22487     hideMode:'offsets',
22488     
22489     
22490     tbContainer : false,
22491     
22492     toolbarContainer :function() {
22493         return this.wrap.select('.x-html-editor-tb',true).first();
22494     },
22495
22496     /**
22497      * Protected method that will not generally be called directly. It
22498      * is called when the editor creates its toolbar. Override this method if you need to
22499      * add custom toolbar buttons.
22500      * @param {HtmlEditor} editor
22501      */
22502     createToolbar : function(){
22503         
22504         Roo.log("create toolbars");
22505         
22506         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22507         this.toolbars[0].render(this.toolbarContainer());
22508         
22509         return;
22510         
22511 //        if (!editor.toolbars || !editor.toolbars.length) {
22512 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22513 //        }
22514 //        
22515 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22516 //            editor.toolbars[i] = Roo.factory(
22517 //                    typeof(editor.toolbars[i]) == 'string' ?
22518 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22519 //                Roo.bootstrap.HtmlEditor);
22520 //            editor.toolbars[i].init(editor);
22521 //        }
22522     },
22523
22524      
22525     // private
22526     onRender : function(ct, position)
22527     {
22528        // Roo.log("Call onRender: " + this.xtype);
22529         var _t = this;
22530         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22531       
22532         this.wrap = this.inputEl().wrap({
22533             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22534         });
22535         
22536         this.editorcore.onRender(ct, position);
22537          
22538         if (this.resizable) {
22539             this.resizeEl = new Roo.Resizable(this.wrap, {
22540                 pinned : true,
22541                 wrap: true,
22542                 dynamic : true,
22543                 minHeight : this.height,
22544                 height: this.height,
22545                 handles : this.resizable,
22546                 width: this.width,
22547                 listeners : {
22548                     resize : function(r, w, h) {
22549                         _t.onResize(w,h); // -something
22550                     }
22551                 }
22552             });
22553             
22554         }
22555         this.createToolbar(this);
22556        
22557         
22558         if(!this.width && this.resizable){
22559             this.setSize(this.wrap.getSize());
22560         }
22561         if (this.resizeEl) {
22562             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22563             // should trigger onReize..
22564         }
22565         
22566     },
22567
22568     // private
22569     onResize : function(w, h)
22570     {
22571         Roo.log('resize: ' +w + ',' + h );
22572         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22573         var ew = false;
22574         var eh = false;
22575         
22576         if(this.inputEl() ){
22577             if(typeof w == 'number'){
22578                 var aw = w - this.wrap.getFrameWidth('lr');
22579                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22580                 ew = aw;
22581             }
22582             if(typeof h == 'number'){
22583                  var tbh = -11;  // fixme it needs to tool bar size!
22584                 for (var i =0; i < this.toolbars.length;i++) {
22585                     // fixme - ask toolbars for heights?
22586                     tbh += this.toolbars[i].el.getHeight();
22587                     //if (this.toolbars[i].footer) {
22588                     //    tbh += this.toolbars[i].footer.el.getHeight();
22589                     //}
22590                 }
22591               
22592                 
22593                 
22594                 
22595                 
22596                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22597                 ah -= 5; // knock a few pixes off for look..
22598                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22599                 var eh = ah;
22600             }
22601         }
22602         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22603         this.editorcore.onResize(ew,eh);
22604         
22605     },
22606
22607     /**
22608      * Toggles the editor between standard and source edit mode.
22609      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22610      */
22611     toggleSourceEdit : function(sourceEditMode)
22612     {
22613         this.editorcore.toggleSourceEdit(sourceEditMode);
22614         
22615         if(this.editorcore.sourceEditMode){
22616             Roo.log('editor - showing textarea');
22617             
22618 //            Roo.log('in');
22619 //            Roo.log(this.syncValue());
22620             this.syncValue();
22621             this.inputEl().removeClass(['hide', 'x-hidden']);
22622             this.inputEl().dom.removeAttribute('tabIndex');
22623             this.inputEl().focus();
22624         }else{
22625             Roo.log('editor - hiding textarea');
22626 //            Roo.log('out')
22627 //            Roo.log(this.pushValue()); 
22628             this.pushValue();
22629             
22630             this.inputEl().addClass(['hide', 'x-hidden']);
22631             this.inputEl().dom.setAttribute('tabIndex', -1);
22632             //this.deferFocus();
22633         }
22634          
22635         if(this.resizable){
22636             this.setSize(this.wrap.getSize());
22637         }
22638         
22639         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22640     },
22641  
22642     // private (for BoxComponent)
22643     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22644
22645     // private (for BoxComponent)
22646     getResizeEl : function(){
22647         return this.wrap;
22648     },
22649
22650     // private (for BoxComponent)
22651     getPositionEl : function(){
22652         return this.wrap;
22653     },
22654
22655     // private
22656     initEvents : function(){
22657         this.originalValue = this.getValue();
22658     },
22659
22660 //    /**
22661 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22662 //     * @method
22663 //     */
22664 //    markInvalid : Roo.emptyFn,
22665 //    /**
22666 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22667 //     * @method
22668 //     */
22669 //    clearInvalid : Roo.emptyFn,
22670
22671     setValue : function(v){
22672         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22673         this.editorcore.pushValue();
22674     },
22675
22676      
22677     // private
22678     deferFocus : function(){
22679         this.focus.defer(10, this);
22680     },
22681
22682     // doc'ed in Field
22683     focus : function(){
22684         this.editorcore.focus();
22685         
22686     },
22687       
22688
22689     // private
22690     onDestroy : function(){
22691         
22692         
22693         
22694         if(this.rendered){
22695             
22696             for (var i =0; i < this.toolbars.length;i++) {
22697                 // fixme - ask toolbars for heights?
22698                 this.toolbars[i].onDestroy();
22699             }
22700             
22701             this.wrap.dom.innerHTML = '';
22702             this.wrap.remove();
22703         }
22704     },
22705
22706     // private
22707     onFirstFocus : function(){
22708         //Roo.log("onFirstFocus");
22709         this.editorcore.onFirstFocus();
22710          for (var i =0; i < this.toolbars.length;i++) {
22711             this.toolbars[i].onFirstFocus();
22712         }
22713         
22714     },
22715     
22716     // private
22717     syncValue : function()
22718     {   
22719         this.editorcore.syncValue();
22720     },
22721     
22722     pushValue : function()
22723     {   
22724         this.editorcore.pushValue();
22725     }
22726      
22727     
22728     // hide stuff that is not compatible
22729     /**
22730      * @event blur
22731      * @hide
22732      */
22733     /**
22734      * @event change
22735      * @hide
22736      */
22737     /**
22738      * @event focus
22739      * @hide
22740      */
22741     /**
22742      * @event specialkey
22743      * @hide
22744      */
22745     /**
22746      * @cfg {String} fieldClass @hide
22747      */
22748     /**
22749      * @cfg {String} focusClass @hide
22750      */
22751     /**
22752      * @cfg {String} autoCreate @hide
22753      */
22754     /**
22755      * @cfg {String} inputType @hide
22756      */
22757     /**
22758      * @cfg {String} invalidClass @hide
22759      */
22760     /**
22761      * @cfg {String} invalidText @hide
22762      */
22763     /**
22764      * @cfg {String} msgFx @hide
22765      */
22766     /**
22767      * @cfg {String} validateOnBlur @hide
22768      */
22769 });
22770  
22771     
22772    
22773    
22774    
22775       
22776 Roo.namespace('Roo.bootstrap.htmleditor');
22777 /**
22778  * @class Roo.bootstrap.HtmlEditorToolbar1
22779  * Basic Toolbar
22780  * 
22781  * Usage:
22782  *
22783  new Roo.bootstrap.HtmlEditor({
22784     ....
22785     toolbars : [
22786         new Roo.bootstrap.HtmlEditorToolbar1({
22787             disable : { fonts: 1 , format: 1, ..., ... , ...],
22788             btns : [ .... ]
22789         })
22790     }
22791      
22792  * 
22793  * @cfg {Object} disable List of elements to disable..
22794  * @cfg {Array} btns List of additional buttons.
22795  * 
22796  * 
22797  * NEEDS Extra CSS? 
22798  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22799  */
22800  
22801 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22802 {
22803     
22804     Roo.apply(this, config);
22805     
22806     // default disabled, based on 'good practice'..
22807     this.disable = this.disable || {};
22808     Roo.applyIf(this.disable, {
22809         fontSize : true,
22810         colors : true,
22811         specialElements : true
22812     });
22813     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22814     
22815     this.editor = config.editor;
22816     this.editorcore = config.editor.editorcore;
22817     
22818     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22819     
22820     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22821     // dont call parent... till later.
22822 }
22823 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22824      
22825     bar : true,
22826     
22827     editor : false,
22828     editorcore : false,
22829     
22830     
22831     formats : [
22832         "p" ,  
22833         "h1","h2","h3","h4","h5","h6", 
22834         "pre", "code", 
22835         "abbr", "acronym", "address", "cite", "samp", "var",
22836         'div','span'
22837     ],
22838     
22839     onRender : function(ct, position)
22840     {
22841        // Roo.log("Call onRender: " + this.xtype);
22842         
22843        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22844        Roo.log(this.el);
22845        this.el.dom.style.marginBottom = '0';
22846        var _this = this;
22847        var editorcore = this.editorcore;
22848        var editor= this.editor;
22849        
22850        var children = [];
22851        var btn = function(id,cmd , toggle, handler){
22852        
22853             var  event = toggle ? 'toggle' : 'click';
22854        
22855             var a = {
22856                 size : 'sm',
22857                 xtype: 'Button',
22858                 xns: Roo.bootstrap,
22859                 glyphicon : id,
22860                 cmd : id || cmd,
22861                 enableToggle:toggle !== false,
22862                 //html : 'submit'
22863                 pressed : toggle ? false : null,
22864                 listeners : {}
22865             };
22866             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22867                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22868             };
22869             children.push(a);
22870             return a;
22871        }
22872         
22873         var style = {
22874                 xtype: 'Button',
22875                 size : 'sm',
22876                 xns: Roo.bootstrap,
22877                 glyphicon : 'font',
22878                 //html : 'submit'
22879                 menu : {
22880                     xtype: 'Menu',
22881                     xns: Roo.bootstrap,
22882                     items:  []
22883                 }
22884         };
22885         Roo.each(this.formats, function(f) {
22886             style.menu.items.push({
22887                 xtype :'MenuItem',
22888                 xns: Roo.bootstrap,
22889                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22890                 tagname : f,
22891                 listeners : {
22892                     click : function()
22893                     {
22894                         editorcore.insertTag(this.tagname);
22895                         editor.focus();
22896                     }
22897                 }
22898                 
22899             });
22900         });
22901          children.push(style);   
22902             
22903             
22904         btn('bold',false,true);
22905         btn('italic',false,true);
22906         btn('align-left', 'justifyleft',true);
22907         btn('align-center', 'justifycenter',true);
22908         btn('align-right' , 'justifyright',true);
22909         btn('link', false, false, function(btn) {
22910             //Roo.log("create link?");
22911             var url = prompt(this.createLinkText, this.defaultLinkValue);
22912             if(url && url != 'http:/'+'/'){
22913                 this.editorcore.relayCmd('createlink', url);
22914             }
22915         }),
22916         btn('list','insertunorderedlist',true);
22917         btn('pencil', false,true, function(btn){
22918                 Roo.log(this);
22919                 
22920                 this.toggleSourceEdit(btn.pressed);
22921         });
22922         /*
22923         var cog = {
22924                 xtype: 'Button',
22925                 size : 'sm',
22926                 xns: Roo.bootstrap,
22927                 glyphicon : 'cog',
22928                 //html : 'submit'
22929                 menu : {
22930                     xtype: 'Menu',
22931                     xns: Roo.bootstrap,
22932                     items:  []
22933                 }
22934         };
22935         
22936         cog.menu.items.push({
22937             xtype :'MenuItem',
22938             xns: Roo.bootstrap,
22939             html : Clean styles,
22940             tagname : f,
22941             listeners : {
22942                 click : function()
22943                 {
22944                     editorcore.insertTag(this.tagname);
22945                     editor.focus();
22946                 }
22947             }
22948             
22949         });
22950        */
22951         
22952          
22953        this.xtype = 'NavSimplebar';
22954         
22955         for(var i=0;i< children.length;i++) {
22956             
22957             this.buttons.add(this.addxtypeChild(children[i]));
22958             
22959         }
22960         
22961         editor.on('editorevent', this.updateToolbar, this);
22962     },
22963     onBtnClick : function(id)
22964     {
22965        this.editorcore.relayCmd(id);
22966        this.editorcore.focus();
22967     },
22968     
22969     /**
22970      * Protected method that will not generally be called directly. It triggers
22971      * a toolbar update by reading the markup state of the current selection in the editor.
22972      */
22973     updateToolbar: function(){
22974
22975         if(!this.editorcore.activated){
22976             this.editor.onFirstFocus(); // is this neeed?
22977             return;
22978         }
22979
22980         var btns = this.buttons; 
22981         var doc = this.editorcore.doc;
22982         btns.get('bold').setActive(doc.queryCommandState('bold'));
22983         btns.get('italic').setActive(doc.queryCommandState('italic'));
22984         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22985         
22986         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22987         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22988         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22989         
22990         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22991         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22992          /*
22993         
22994         var ans = this.editorcore.getAllAncestors();
22995         if (this.formatCombo) {
22996             
22997             
22998             var store = this.formatCombo.store;
22999             this.formatCombo.setValue("");
23000             for (var i =0; i < ans.length;i++) {
23001                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23002                     // select it..
23003                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23004                     break;
23005                 }
23006             }
23007         }
23008         
23009         
23010         
23011         // hides menus... - so this cant be on a menu...
23012         Roo.bootstrap.MenuMgr.hideAll();
23013         */
23014         Roo.bootstrap.MenuMgr.hideAll();
23015         //this.editorsyncValue();
23016     },
23017     onFirstFocus: function() {
23018         this.buttons.each(function(item){
23019            item.enable();
23020         });
23021     },
23022     toggleSourceEdit : function(sourceEditMode){
23023         
23024           
23025         if(sourceEditMode){
23026             Roo.log("disabling buttons");
23027            this.buttons.each( function(item){
23028                 if(item.cmd != 'pencil'){
23029                     item.disable();
23030                 }
23031             });
23032           
23033         }else{
23034             Roo.log("enabling buttons");
23035             if(this.editorcore.initialized){
23036                 this.buttons.each( function(item){
23037                     item.enable();
23038                 });
23039             }
23040             
23041         }
23042         Roo.log("calling toggole on editor");
23043         // tell the editor that it's been pressed..
23044         this.editor.toggleSourceEdit(sourceEditMode);
23045        
23046     }
23047 });
23048
23049
23050
23051
23052
23053 /**
23054  * @class Roo.bootstrap.Table.AbstractSelectionModel
23055  * @extends Roo.util.Observable
23056  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23057  * implemented by descendant classes.  This class should not be directly instantiated.
23058  * @constructor
23059  */
23060 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23061     this.locked = false;
23062     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23063 };
23064
23065
23066 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23067     /** @ignore Called by the grid automatically. Do not call directly. */
23068     init : function(grid){
23069         this.grid = grid;
23070         this.initEvents();
23071     },
23072
23073     /**
23074      * Locks the selections.
23075      */
23076     lock : function(){
23077         this.locked = true;
23078     },
23079
23080     /**
23081      * Unlocks the selections.
23082      */
23083     unlock : function(){
23084         this.locked = false;
23085     },
23086
23087     /**
23088      * Returns true if the selections are locked.
23089      * @return {Boolean}
23090      */
23091     isLocked : function(){
23092         return this.locked;
23093     }
23094 });
23095 /**
23096  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23097  * @class Roo.bootstrap.Table.RowSelectionModel
23098  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23099  * It supports multiple selections and keyboard selection/navigation. 
23100  * @constructor
23101  * @param {Object} config
23102  */
23103
23104 Roo.bootstrap.Table.RowSelectionModel = function(config){
23105     Roo.apply(this, config);
23106     this.selections = new Roo.util.MixedCollection(false, function(o){
23107         return o.id;
23108     });
23109
23110     this.last = false;
23111     this.lastActive = false;
23112
23113     this.addEvents({
23114         /**
23115              * @event selectionchange
23116              * Fires when the selection changes
23117              * @param {SelectionModel} this
23118              */
23119             "selectionchange" : true,
23120         /**
23121              * @event afterselectionchange
23122              * Fires after the selection changes (eg. by key press or clicking)
23123              * @param {SelectionModel} this
23124              */
23125             "afterselectionchange" : true,
23126         /**
23127              * @event beforerowselect
23128              * Fires when a row is selected being selected, return false to cancel.
23129              * @param {SelectionModel} this
23130              * @param {Number} rowIndex The selected index
23131              * @param {Boolean} keepExisting False if other selections will be cleared
23132              */
23133             "beforerowselect" : true,
23134         /**
23135              * @event rowselect
23136              * Fires when a row is selected.
23137              * @param {SelectionModel} this
23138              * @param {Number} rowIndex The selected index
23139              * @param {Roo.data.Record} r The record
23140              */
23141             "rowselect" : true,
23142         /**
23143              * @event rowdeselect
23144              * Fires when a row is deselected.
23145              * @param {SelectionModel} this
23146              * @param {Number} rowIndex The selected index
23147              */
23148         "rowdeselect" : true
23149     });
23150     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23151     this.locked = false;
23152  };
23153
23154 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23155     /**
23156      * @cfg {Boolean} singleSelect
23157      * True to allow selection of only one row at a time (defaults to false)
23158      */
23159     singleSelect : false,
23160
23161     // private
23162     initEvents : function()
23163     {
23164
23165         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23166         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23167         //}else{ // allow click to work like normal
23168          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23169         //}
23170         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23171         this.grid.on("rowclick", this.handleMouseDown, this);
23172         
23173         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23174             "up" : function(e){
23175                 if(!e.shiftKey){
23176                     this.selectPrevious(e.shiftKey);
23177                 }else if(this.last !== false && this.lastActive !== false){
23178                     var last = this.last;
23179                     this.selectRange(this.last,  this.lastActive-1);
23180                     this.grid.getView().focusRow(this.lastActive);
23181                     if(last !== false){
23182                         this.last = last;
23183                     }
23184                 }else{
23185                     this.selectFirstRow();
23186                 }
23187                 this.fireEvent("afterselectionchange", this);
23188             },
23189             "down" : function(e){
23190                 if(!e.shiftKey){
23191                     this.selectNext(e.shiftKey);
23192                 }else if(this.last !== false && this.lastActive !== false){
23193                     var last = this.last;
23194                     this.selectRange(this.last,  this.lastActive+1);
23195                     this.grid.getView().focusRow(this.lastActive);
23196                     if(last !== false){
23197                         this.last = last;
23198                     }
23199                 }else{
23200                     this.selectFirstRow();
23201                 }
23202                 this.fireEvent("afterselectionchange", this);
23203             },
23204             scope: this
23205         });
23206         this.grid.store.on('load', function(){
23207             this.selections.clear();
23208         },this);
23209         /*
23210         var view = this.grid.view;
23211         view.on("refresh", this.onRefresh, this);
23212         view.on("rowupdated", this.onRowUpdated, this);
23213         view.on("rowremoved", this.onRemove, this);
23214         */
23215     },
23216
23217     // private
23218     onRefresh : function()
23219     {
23220         var ds = this.grid.store, i, v = this.grid.view;
23221         var s = this.selections;
23222         s.each(function(r){
23223             if((i = ds.indexOfId(r.id)) != -1){
23224                 v.onRowSelect(i);
23225             }else{
23226                 s.remove(r);
23227             }
23228         });
23229     },
23230
23231     // private
23232     onRemove : function(v, index, r){
23233         this.selections.remove(r);
23234     },
23235
23236     // private
23237     onRowUpdated : function(v, index, r){
23238         if(this.isSelected(r)){
23239             v.onRowSelect(index);
23240         }
23241     },
23242
23243     /**
23244      * Select records.
23245      * @param {Array} records The records to select
23246      * @param {Boolean} keepExisting (optional) True to keep existing selections
23247      */
23248     selectRecords : function(records, keepExisting)
23249     {
23250         if(!keepExisting){
23251             this.clearSelections();
23252         }
23253             var ds = this.grid.store;
23254         for(var i = 0, len = records.length; i < len; i++){
23255             this.selectRow(ds.indexOf(records[i]), true);
23256         }
23257     },
23258
23259     /**
23260      * Gets the number of selected rows.
23261      * @return {Number}
23262      */
23263     getCount : function(){
23264         return this.selections.length;
23265     },
23266
23267     /**
23268      * Selects the first row in the grid.
23269      */
23270     selectFirstRow : function(){
23271         this.selectRow(0);
23272     },
23273
23274     /**
23275      * Select the last row.
23276      * @param {Boolean} keepExisting (optional) True to keep existing selections
23277      */
23278     selectLastRow : function(keepExisting){
23279         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23280         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23281     },
23282
23283     /**
23284      * Selects the row immediately following the last selected row.
23285      * @param {Boolean} keepExisting (optional) True to keep existing selections
23286      */
23287     selectNext : function(keepExisting)
23288     {
23289             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23290             this.selectRow(this.last+1, keepExisting);
23291             this.grid.getView().focusRow(this.last);
23292         }
23293     },
23294
23295     /**
23296      * Selects the row that precedes the last selected row.
23297      * @param {Boolean} keepExisting (optional) True to keep existing selections
23298      */
23299     selectPrevious : function(keepExisting){
23300         if(this.last){
23301             this.selectRow(this.last-1, keepExisting);
23302             this.grid.getView().focusRow(this.last);
23303         }
23304     },
23305
23306     /**
23307      * Returns the selected records
23308      * @return {Array} Array of selected records
23309      */
23310     getSelections : function(){
23311         return [].concat(this.selections.items);
23312     },
23313
23314     /**
23315      * Returns the first selected record.
23316      * @return {Record}
23317      */
23318     getSelected : function(){
23319         return this.selections.itemAt(0);
23320     },
23321
23322
23323     /**
23324      * Clears all selections.
23325      */
23326     clearSelections : function(fast)
23327     {
23328         if(this.locked) {
23329             return;
23330         }
23331         if(fast !== true){
23332                 var ds = this.grid.store;
23333             var s = this.selections;
23334             s.each(function(r){
23335                 this.deselectRow(ds.indexOfId(r.id));
23336             }, this);
23337             s.clear();
23338         }else{
23339             this.selections.clear();
23340         }
23341         this.last = false;
23342     },
23343
23344
23345     /**
23346      * Selects all rows.
23347      */
23348     selectAll : function(){
23349         if(this.locked) {
23350             return;
23351         }
23352         this.selections.clear();
23353         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23354             this.selectRow(i, true);
23355         }
23356     },
23357
23358     /**
23359      * Returns True if there is a selection.
23360      * @return {Boolean}
23361      */
23362     hasSelection : function(){
23363         return this.selections.length > 0;
23364     },
23365
23366     /**
23367      * Returns True if the specified row is selected.
23368      * @param {Number/Record} record The record or index of the record to check
23369      * @return {Boolean}
23370      */
23371     isSelected : function(index){
23372             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23373         return (r && this.selections.key(r.id) ? true : false);
23374     },
23375
23376     /**
23377      * Returns True if the specified record id is selected.
23378      * @param {String} id The id of record to check
23379      * @return {Boolean}
23380      */
23381     isIdSelected : function(id){
23382         return (this.selections.key(id) ? true : false);
23383     },
23384
23385
23386     // private
23387     handleMouseDBClick : function(e, t){
23388         
23389     },
23390     // private
23391     handleMouseDown : function(e, t)
23392     {
23393             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23394         if(this.isLocked() || rowIndex < 0 ){
23395             return;
23396         };
23397         if(e.shiftKey && this.last !== false){
23398             var last = this.last;
23399             this.selectRange(last, rowIndex, e.ctrlKey);
23400             this.last = last; // reset the last
23401             t.focus();
23402     
23403         }else{
23404             var isSelected = this.isSelected(rowIndex);
23405             //Roo.log("select row:" + rowIndex);
23406             if(isSelected){
23407                 this.deselectRow(rowIndex);
23408             } else {
23409                         this.selectRow(rowIndex, true);
23410             }
23411     
23412             /*
23413                 if(e.button !== 0 && isSelected){
23414                 alert('rowIndex 2: ' + rowIndex);
23415                     view.focusRow(rowIndex);
23416                 }else if(e.ctrlKey && isSelected){
23417                     this.deselectRow(rowIndex);
23418                 }else if(!isSelected){
23419                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23420                     view.focusRow(rowIndex);
23421                 }
23422             */
23423         }
23424         this.fireEvent("afterselectionchange", this);
23425     },
23426     // private
23427     handleDragableRowClick :  function(grid, rowIndex, e) 
23428     {
23429         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23430             this.selectRow(rowIndex, false);
23431             grid.view.focusRow(rowIndex);
23432              this.fireEvent("afterselectionchange", this);
23433         }
23434     },
23435     
23436     /**
23437      * Selects multiple rows.
23438      * @param {Array} rows Array of the indexes of the row to select
23439      * @param {Boolean} keepExisting (optional) True to keep existing selections
23440      */
23441     selectRows : function(rows, keepExisting){
23442         if(!keepExisting){
23443             this.clearSelections();
23444         }
23445         for(var i = 0, len = rows.length; i < len; i++){
23446             this.selectRow(rows[i], true);
23447         }
23448     },
23449
23450     /**
23451      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23452      * @param {Number} startRow The index of the first row in the range
23453      * @param {Number} endRow The index of the last row in the range
23454      * @param {Boolean} keepExisting (optional) True to retain existing selections
23455      */
23456     selectRange : function(startRow, endRow, keepExisting){
23457         if(this.locked) {
23458             return;
23459         }
23460         if(!keepExisting){
23461             this.clearSelections();
23462         }
23463         if(startRow <= endRow){
23464             for(var i = startRow; i <= endRow; i++){
23465                 this.selectRow(i, true);
23466             }
23467         }else{
23468             for(var i = startRow; i >= endRow; i--){
23469                 this.selectRow(i, true);
23470             }
23471         }
23472     },
23473
23474     /**
23475      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23476      * @param {Number} startRow The index of the first row in the range
23477      * @param {Number} endRow The index of the last row in the range
23478      */
23479     deselectRange : function(startRow, endRow, preventViewNotify){
23480         if(this.locked) {
23481             return;
23482         }
23483         for(var i = startRow; i <= endRow; i++){
23484             this.deselectRow(i, preventViewNotify);
23485         }
23486     },
23487
23488     /**
23489      * Selects a row.
23490      * @param {Number} row The index of the row to select
23491      * @param {Boolean} keepExisting (optional) True to keep existing selections
23492      */
23493     selectRow : function(index, keepExisting, preventViewNotify)
23494     {
23495             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23496             return;
23497         }
23498         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23499             if(!keepExisting || this.singleSelect){
23500                 this.clearSelections();
23501             }
23502             
23503             var r = this.grid.store.getAt(index);
23504             //console.log('selectRow - record id :' + r.id);
23505             
23506             this.selections.add(r);
23507             this.last = this.lastActive = index;
23508             if(!preventViewNotify){
23509                 var proxy = new Roo.Element(
23510                                 this.grid.getRowDom(index)
23511                 );
23512                 proxy.addClass('bg-info info');
23513             }
23514             this.fireEvent("rowselect", this, index, r);
23515             this.fireEvent("selectionchange", this);
23516         }
23517     },
23518
23519     /**
23520      * Deselects a row.
23521      * @param {Number} row The index of the row to deselect
23522      */
23523     deselectRow : function(index, preventViewNotify)
23524     {
23525         if(this.locked) {
23526             return;
23527         }
23528         if(this.last == index){
23529             this.last = false;
23530         }
23531         if(this.lastActive == index){
23532             this.lastActive = false;
23533         }
23534         
23535         var r = this.grid.store.getAt(index);
23536         if (!r) {
23537             return;
23538         }
23539         
23540         this.selections.remove(r);
23541         //.console.log('deselectRow - record id :' + r.id);
23542         if(!preventViewNotify){
23543         
23544             var proxy = new Roo.Element(
23545                 this.grid.getRowDom(index)
23546             );
23547             proxy.removeClass('bg-info info');
23548         }
23549         this.fireEvent("rowdeselect", this, index);
23550         this.fireEvent("selectionchange", this);
23551     },
23552
23553     // private
23554     restoreLast : function(){
23555         if(this._last){
23556             this.last = this._last;
23557         }
23558     },
23559
23560     // private
23561     acceptsNav : function(row, col, cm){
23562         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23563     },
23564
23565     // private
23566     onEditorKey : function(field, e){
23567         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23568         if(k == e.TAB){
23569             e.stopEvent();
23570             ed.completeEdit();
23571             if(e.shiftKey){
23572                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23573             }else{
23574                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23575             }
23576         }else if(k == e.ENTER && !e.ctrlKey){
23577             e.stopEvent();
23578             ed.completeEdit();
23579             if(e.shiftKey){
23580                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23581             }else{
23582                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23583             }
23584         }else if(k == e.ESC){
23585             ed.cancelEdit();
23586         }
23587         if(newCell){
23588             g.startEditing(newCell[0], newCell[1]);
23589         }
23590     }
23591 });
23592 /*
23593  * Based on:
23594  * Ext JS Library 1.1.1
23595  * Copyright(c) 2006-2007, Ext JS, LLC.
23596  *
23597  * Originally Released Under LGPL - original licence link has changed is not relivant.
23598  *
23599  * Fork - LGPL
23600  * <script type="text/javascript">
23601  */
23602  
23603 /**
23604  * @class Roo.bootstrap.PagingToolbar
23605  * @extends Roo.bootstrap.NavSimplebar
23606  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23607  * @constructor
23608  * Create a new PagingToolbar
23609  * @param {Object} config The config object
23610  * @param {Roo.data.Store} store
23611  */
23612 Roo.bootstrap.PagingToolbar = function(config)
23613 {
23614     // old args format still supported... - xtype is prefered..
23615         // created from xtype...
23616     
23617     this.ds = config.dataSource;
23618     
23619     if (config.store && !this.ds) {
23620         this.store= Roo.factory(config.store, Roo.data);
23621         this.ds = this.store;
23622         this.ds.xmodule = this.xmodule || false;
23623     }
23624     
23625     this.toolbarItems = [];
23626     if (config.items) {
23627         this.toolbarItems = config.items;
23628     }
23629     
23630     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23631     
23632     this.cursor = 0;
23633     
23634     if (this.ds) { 
23635         this.bind(this.ds);
23636     }
23637     
23638     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23639     
23640 };
23641
23642 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23643     /**
23644      * @cfg {Roo.data.Store} dataSource
23645      * The underlying data store providing the paged data
23646      */
23647     /**
23648      * @cfg {String/HTMLElement/Element} container
23649      * container The id or element that will contain the toolbar
23650      */
23651     /**
23652      * @cfg {Boolean} displayInfo
23653      * True to display the displayMsg (defaults to false)
23654      */
23655     /**
23656      * @cfg {Number} pageSize
23657      * The number of records to display per page (defaults to 20)
23658      */
23659     pageSize: 20,
23660     /**
23661      * @cfg {String} displayMsg
23662      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23663      */
23664     displayMsg : 'Displaying {0} - {1} of {2}',
23665     /**
23666      * @cfg {String} emptyMsg
23667      * The message to display when no records are found (defaults to "No data to display")
23668      */
23669     emptyMsg : 'No data to display',
23670     /**
23671      * Customizable piece of the default paging text (defaults to "Page")
23672      * @type String
23673      */
23674     beforePageText : "Page",
23675     /**
23676      * Customizable piece of the default paging text (defaults to "of %0")
23677      * @type String
23678      */
23679     afterPageText : "of {0}",
23680     /**
23681      * Customizable piece of the default paging text (defaults to "First Page")
23682      * @type String
23683      */
23684     firstText : "First Page",
23685     /**
23686      * Customizable piece of the default paging text (defaults to "Previous Page")
23687      * @type String
23688      */
23689     prevText : "Previous Page",
23690     /**
23691      * Customizable piece of the default paging text (defaults to "Next Page")
23692      * @type String
23693      */
23694     nextText : "Next Page",
23695     /**
23696      * Customizable piece of the default paging text (defaults to "Last Page")
23697      * @type String
23698      */
23699     lastText : "Last Page",
23700     /**
23701      * Customizable piece of the default paging text (defaults to "Refresh")
23702      * @type String
23703      */
23704     refreshText : "Refresh",
23705
23706     buttons : false,
23707     // private
23708     onRender : function(ct, position) 
23709     {
23710         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23711         this.navgroup.parentId = this.id;
23712         this.navgroup.onRender(this.el, null);
23713         // add the buttons to the navgroup
23714         
23715         if(this.displayInfo){
23716             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23717             this.displayEl = this.el.select('.x-paging-info', true).first();
23718 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23719 //            this.displayEl = navel.el.select('span',true).first();
23720         }
23721         
23722         var _this = this;
23723         
23724         if(this.buttons){
23725             Roo.each(_this.buttons, function(e){ // this might need to use render????
23726                Roo.factory(e).onRender(_this.el, null);
23727             });
23728         }
23729             
23730         Roo.each(_this.toolbarItems, function(e) {
23731             _this.navgroup.addItem(e);
23732         });
23733         
23734         
23735         this.first = this.navgroup.addItem({
23736             tooltip: this.firstText,
23737             cls: "prev",
23738             icon : 'fa fa-backward',
23739             disabled: true,
23740             preventDefault: true,
23741             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23742         });
23743         
23744         this.prev =  this.navgroup.addItem({
23745             tooltip: this.prevText,
23746             cls: "prev",
23747             icon : 'fa fa-step-backward',
23748             disabled: true,
23749             preventDefault: true,
23750             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23751         });
23752     //this.addSeparator();
23753         
23754         
23755         var field = this.navgroup.addItem( {
23756             tagtype : 'span',
23757             cls : 'x-paging-position',
23758             
23759             html : this.beforePageText  +
23760                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23761                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23762          } ); //?? escaped?
23763         
23764         this.field = field.el.select('input', true).first();
23765         this.field.on("keydown", this.onPagingKeydown, this);
23766         this.field.on("focus", function(){this.dom.select();});
23767     
23768     
23769         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23770         //this.field.setHeight(18);
23771         //this.addSeparator();
23772         this.next = this.navgroup.addItem({
23773             tooltip: this.nextText,
23774             cls: "next",
23775             html : ' <i class="fa fa-step-forward">',
23776             disabled: true,
23777             preventDefault: true,
23778             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23779         });
23780         this.last = this.navgroup.addItem({
23781             tooltip: this.lastText,
23782             icon : 'fa fa-forward',
23783             cls: "next",
23784             disabled: true,
23785             preventDefault: true,
23786             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23787         });
23788     //this.addSeparator();
23789         this.loading = this.navgroup.addItem({
23790             tooltip: this.refreshText,
23791             icon: 'fa fa-refresh',
23792             preventDefault: true,
23793             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23794         });
23795         
23796     },
23797
23798     // private
23799     updateInfo : function(){
23800         if(this.displayEl){
23801             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23802             var msg = count == 0 ?
23803                 this.emptyMsg :
23804                 String.format(
23805                     this.displayMsg,
23806                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23807                 );
23808             this.displayEl.update(msg);
23809         }
23810     },
23811
23812     // private
23813     onLoad : function(ds, r, o){
23814        this.cursor = o.params ? o.params.start : 0;
23815        var d = this.getPageData(),
23816             ap = d.activePage,
23817             ps = d.pages;
23818         
23819        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23820        this.field.dom.value = ap;
23821        this.first.setDisabled(ap == 1);
23822        this.prev.setDisabled(ap == 1);
23823        this.next.setDisabled(ap == ps);
23824        this.last.setDisabled(ap == ps);
23825        this.loading.enable();
23826        this.updateInfo();
23827     },
23828
23829     // private
23830     getPageData : function(){
23831         var total = this.ds.getTotalCount();
23832         return {
23833             total : total,
23834             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23835             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23836         };
23837     },
23838
23839     // private
23840     onLoadError : function(){
23841         this.loading.enable();
23842     },
23843
23844     // private
23845     onPagingKeydown : function(e){
23846         var k = e.getKey();
23847         var d = this.getPageData();
23848         if(k == e.RETURN){
23849             var v = this.field.dom.value, pageNum;
23850             if(!v || isNaN(pageNum = parseInt(v, 10))){
23851                 this.field.dom.value = d.activePage;
23852                 return;
23853             }
23854             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23855             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23856             e.stopEvent();
23857         }
23858         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))
23859         {
23860           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23861           this.field.dom.value = pageNum;
23862           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23863           e.stopEvent();
23864         }
23865         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23866         {
23867           var v = this.field.dom.value, pageNum; 
23868           var increment = (e.shiftKey) ? 10 : 1;
23869           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23870                 increment *= -1;
23871           }
23872           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23873             this.field.dom.value = d.activePage;
23874             return;
23875           }
23876           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23877           {
23878             this.field.dom.value = parseInt(v, 10) + increment;
23879             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23880             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23881           }
23882           e.stopEvent();
23883         }
23884     },
23885
23886     // private
23887     beforeLoad : function(){
23888         if(this.loading){
23889             this.loading.disable();
23890         }
23891     },
23892
23893     // private
23894     onClick : function(which){
23895         
23896         var ds = this.ds;
23897         if (!ds) {
23898             return;
23899         }
23900         
23901         switch(which){
23902             case "first":
23903                 ds.load({params:{start: 0, limit: this.pageSize}});
23904             break;
23905             case "prev":
23906                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23907             break;
23908             case "next":
23909                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23910             break;
23911             case "last":
23912                 var total = ds.getTotalCount();
23913                 var extra = total % this.pageSize;
23914                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23915                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23916             break;
23917             case "refresh":
23918                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23919             break;
23920         }
23921     },
23922
23923     /**
23924      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23925      * @param {Roo.data.Store} store The data store to unbind
23926      */
23927     unbind : function(ds){
23928         ds.un("beforeload", this.beforeLoad, this);
23929         ds.un("load", this.onLoad, this);
23930         ds.un("loadexception", this.onLoadError, this);
23931         ds.un("remove", this.updateInfo, this);
23932         ds.un("add", this.updateInfo, this);
23933         this.ds = undefined;
23934     },
23935
23936     /**
23937      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23938      * @param {Roo.data.Store} store The data store to bind
23939      */
23940     bind : function(ds){
23941         ds.on("beforeload", this.beforeLoad, this);
23942         ds.on("load", this.onLoad, this);
23943         ds.on("loadexception", this.onLoadError, this);
23944         ds.on("remove", this.updateInfo, this);
23945         ds.on("add", this.updateInfo, this);
23946         this.ds = ds;
23947     }
23948 });/*
23949  * - LGPL
23950  *
23951  * element
23952  * 
23953  */
23954
23955 /**
23956  * @class Roo.bootstrap.MessageBar
23957  * @extends Roo.bootstrap.Component
23958  * Bootstrap MessageBar class
23959  * @cfg {String} html contents of the MessageBar
23960  * @cfg {String} weight (info | success | warning | danger) default info
23961  * @cfg {String} beforeClass insert the bar before the given class
23962  * @cfg {Boolean} closable (true | false) default false
23963  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23964  * 
23965  * @constructor
23966  * Create a new Element
23967  * @param {Object} config The config object
23968  */
23969
23970 Roo.bootstrap.MessageBar = function(config){
23971     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23972 };
23973
23974 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23975     
23976     html: '',
23977     weight: 'info',
23978     closable: false,
23979     fixed: false,
23980     beforeClass: 'bootstrap-sticky-wrap',
23981     
23982     getAutoCreate : function(){
23983         
23984         var cfg = {
23985             tag: 'div',
23986             cls: 'alert alert-dismissable alert-' + this.weight,
23987             cn: [
23988                 {
23989                     tag: 'span',
23990                     cls: 'message',
23991                     html: this.html || ''
23992                 }
23993             ]
23994         };
23995         
23996         if(this.fixed){
23997             cfg.cls += ' alert-messages-fixed';
23998         }
23999         
24000         if(this.closable){
24001             cfg.cn.push({
24002                 tag: 'button',
24003                 cls: 'close',
24004                 html: 'x'
24005             });
24006         }
24007         
24008         return cfg;
24009     },
24010     
24011     onRender : function(ct, position)
24012     {
24013         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24014         
24015         if(!this.el){
24016             var cfg = Roo.apply({},  this.getAutoCreate());
24017             cfg.id = Roo.id();
24018             
24019             if (this.cls) {
24020                 cfg.cls += ' ' + this.cls;
24021             }
24022             if (this.style) {
24023                 cfg.style = this.style;
24024             }
24025             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24026             
24027             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24028         }
24029         
24030         this.el.select('>button.close').on('click', this.hide, this);
24031         
24032     },
24033     
24034     show : function()
24035     {
24036         if (!this.rendered) {
24037             this.render();
24038         }
24039         
24040         this.el.show();
24041         
24042         this.fireEvent('show', this);
24043         
24044     },
24045     
24046     hide : function()
24047     {
24048         if (!this.rendered) {
24049             this.render();
24050         }
24051         
24052         this.el.hide();
24053         
24054         this.fireEvent('hide', this);
24055     },
24056     
24057     update : function()
24058     {
24059 //        var e = this.el.dom.firstChild;
24060 //        
24061 //        if(this.closable){
24062 //            e = e.nextSibling;
24063 //        }
24064 //        
24065 //        e.data = this.html || '';
24066
24067         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24068     }
24069    
24070 });
24071
24072  
24073
24074      /*
24075  * - LGPL
24076  *
24077  * Graph
24078  * 
24079  */
24080
24081
24082 /**
24083  * @class Roo.bootstrap.Graph
24084  * @extends Roo.bootstrap.Component
24085  * Bootstrap Graph class
24086 > Prameters
24087  -sm {number} sm 4
24088  -md {number} md 5
24089  @cfg {String} graphtype  bar | vbar | pie
24090  @cfg {number} g_x coodinator | centre x (pie)
24091  @cfg {number} g_y coodinator | centre y (pie)
24092  @cfg {number} g_r radius (pie)
24093  @cfg {number} g_height height of the chart (respected by all elements in the set)
24094  @cfg {number} g_width width of the chart (respected by all elements in the set)
24095  @cfg {Object} title The title of the chart
24096     
24097  -{Array}  values
24098  -opts (object) options for the chart 
24099      o {
24100      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24101      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24102      o vgutter (number)
24103      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.
24104      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24105      o to
24106      o stretch (boolean)
24107      o }
24108  -opts (object) options for the pie
24109      o{
24110      o cut
24111      o startAngle (number)
24112      o endAngle (number)
24113      } 
24114  *
24115  * @constructor
24116  * Create a new Input
24117  * @param {Object} config The config object
24118  */
24119
24120 Roo.bootstrap.Graph = function(config){
24121     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24122     
24123     this.addEvents({
24124         // img events
24125         /**
24126          * @event click
24127          * The img click event for the img.
24128          * @param {Roo.EventObject} e
24129          */
24130         "click" : true
24131     });
24132 };
24133
24134 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24135     
24136     sm: 4,
24137     md: 5,
24138     graphtype: 'bar',
24139     g_height: 250,
24140     g_width: 400,
24141     g_x: 50,
24142     g_y: 50,
24143     g_r: 30,
24144     opts:{
24145         //g_colors: this.colors,
24146         g_type: 'soft',
24147         g_gutter: '20%'
24148
24149     },
24150     title : false,
24151
24152     getAutoCreate : function(){
24153         
24154         var cfg = {
24155             tag: 'div',
24156             html : null
24157         };
24158         
24159         
24160         return  cfg;
24161     },
24162
24163     onRender : function(ct,position){
24164         
24165         
24166         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24167         
24168         if (typeof(Raphael) == 'undefined') {
24169             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24170             return;
24171         }
24172         
24173         this.raphael = Raphael(this.el.dom);
24174         
24175                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24176                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24177                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24178                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24179                 /*
24180                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24181                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24182                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24183                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24184                 
24185                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24186                 r.barchart(330, 10, 300, 220, data1);
24187                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24188                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24189                 */
24190                 
24191                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24192                 // r.barchart(30, 30, 560, 250,  xdata, {
24193                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24194                 //     axis : "0 0 1 1",
24195                 //     axisxlabels :  xdata
24196                 //     //yvalues : cols,
24197                    
24198                 // });
24199 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24200 //        
24201 //        this.load(null,xdata,{
24202 //                axis : "0 0 1 1",
24203 //                axisxlabels :  xdata
24204 //                });
24205
24206     },
24207
24208     load : function(graphtype,xdata,opts)
24209     {
24210         this.raphael.clear();
24211         if(!graphtype) {
24212             graphtype = this.graphtype;
24213         }
24214         if(!opts){
24215             opts = this.opts;
24216         }
24217         var r = this.raphael,
24218             fin = function () {
24219                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24220             },
24221             fout = function () {
24222                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24223             },
24224             pfin = function() {
24225                 this.sector.stop();
24226                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24227
24228                 if (this.label) {
24229                     this.label[0].stop();
24230                     this.label[0].attr({ r: 7.5 });
24231                     this.label[1].attr({ "font-weight": 800 });
24232                 }
24233             },
24234             pfout = function() {
24235                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24236
24237                 if (this.label) {
24238                     this.label[0].animate({ r: 5 }, 500, "bounce");
24239                     this.label[1].attr({ "font-weight": 400 });
24240                 }
24241             };
24242
24243         switch(graphtype){
24244             case 'bar':
24245                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24246                 break;
24247             case 'hbar':
24248                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24249                 break;
24250             case 'pie':
24251 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24252 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24253 //            
24254                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24255                 
24256                 break;
24257
24258         }
24259         
24260         if(this.title){
24261             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24262         }
24263         
24264     },
24265     
24266     setTitle: function(o)
24267     {
24268         this.title = o;
24269     },
24270     
24271     initEvents: function() {
24272         
24273         if(!this.href){
24274             this.el.on('click', this.onClick, this);
24275         }
24276     },
24277     
24278     onClick : function(e)
24279     {
24280         Roo.log('img onclick');
24281         this.fireEvent('click', this, e);
24282     }
24283    
24284 });
24285
24286  
24287 /*
24288  * - LGPL
24289  *
24290  * numberBox
24291  * 
24292  */
24293 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24294
24295 /**
24296  * @class Roo.bootstrap.dash.NumberBox
24297  * @extends Roo.bootstrap.Component
24298  * Bootstrap NumberBox class
24299  * @cfg {String} headline Box headline
24300  * @cfg {String} content Box content
24301  * @cfg {String} icon Box icon
24302  * @cfg {String} footer Footer text
24303  * @cfg {String} fhref Footer href
24304  * 
24305  * @constructor
24306  * Create a new NumberBox
24307  * @param {Object} config The config object
24308  */
24309
24310
24311 Roo.bootstrap.dash.NumberBox = function(config){
24312     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24313     
24314 };
24315
24316 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24317     
24318     headline : '',
24319     content : '',
24320     icon : '',
24321     footer : '',
24322     fhref : '',
24323     ficon : '',
24324     
24325     getAutoCreate : function(){
24326         
24327         var cfg = {
24328             tag : 'div',
24329             cls : 'small-box ',
24330             cn : [
24331                 {
24332                     tag : 'div',
24333                     cls : 'inner',
24334                     cn :[
24335                         {
24336                             tag : 'h3',
24337                             cls : 'roo-headline',
24338                             html : this.headline
24339                         },
24340                         {
24341                             tag : 'p',
24342                             cls : 'roo-content',
24343                             html : this.content
24344                         }
24345                     ]
24346                 }
24347             ]
24348         };
24349         
24350         if(this.icon){
24351             cfg.cn.push({
24352                 tag : 'div',
24353                 cls : 'icon',
24354                 cn :[
24355                     {
24356                         tag : 'i',
24357                         cls : 'ion ' + this.icon
24358                     }
24359                 ]
24360             });
24361         }
24362         
24363         if(this.footer){
24364             var footer = {
24365                 tag : 'a',
24366                 cls : 'small-box-footer',
24367                 href : this.fhref || '#',
24368                 html : this.footer
24369             };
24370             
24371             cfg.cn.push(footer);
24372             
24373         }
24374         
24375         return  cfg;
24376     },
24377
24378     onRender : function(ct,position){
24379         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24380
24381
24382        
24383                 
24384     },
24385
24386     setHeadline: function (value)
24387     {
24388         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24389     },
24390     
24391     setFooter: function (value, href)
24392     {
24393         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24394         
24395         if(href){
24396             this.el.select('a.small-box-footer',true).first().attr('href', href);
24397         }
24398         
24399     },
24400
24401     setContent: function (value)
24402     {
24403         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24404     },
24405
24406     initEvents: function() 
24407     {   
24408         
24409     }
24410     
24411 });
24412
24413  
24414 /*
24415  * - LGPL
24416  *
24417  * TabBox
24418  * 
24419  */
24420 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24421
24422 /**
24423  * @class Roo.bootstrap.dash.TabBox
24424  * @extends Roo.bootstrap.Component
24425  * Bootstrap TabBox class
24426  * @cfg {String} title Title of the TabBox
24427  * @cfg {String} icon Icon of the TabBox
24428  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24429  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24430  * 
24431  * @constructor
24432  * Create a new TabBox
24433  * @param {Object} config The config object
24434  */
24435
24436
24437 Roo.bootstrap.dash.TabBox = function(config){
24438     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24439     this.addEvents({
24440         // raw events
24441         /**
24442          * @event addpane
24443          * When a pane is added
24444          * @param {Roo.bootstrap.dash.TabPane} pane
24445          */
24446         "addpane" : true,
24447         /**
24448          * @event activatepane
24449          * When a pane is activated
24450          * @param {Roo.bootstrap.dash.TabPane} pane
24451          */
24452         "activatepane" : true
24453         
24454          
24455     });
24456     
24457     this.panes = [];
24458 };
24459
24460 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24461
24462     title : '',
24463     icon : false,
24464     showtabs : true,
24465     tabScrollable : false,
24466     
24467     getChildContainer : function()
24468     {
24469         return this.el.select('.tab-content', true).first();
24470     },
24471     
24472     getAutoCreate : function(){
24473         
24474         var header = {
24475             tag: 'li',
24476             cls: 'pull-left header',
24477             html: this.title,
24478             cn : []
24479         };
24480         
24481         if(this.icon){
24482             header.cn.push({
24483                 tag: 'i',
24484                 cls: 'fa ' + this.icon
24485             });
24486         }
24487         
24488         var h = {
24489             tag: 'ul',
24490             cls: 'nav nav-tabs pull-right',
24491             cn: [
24492                 header
24493             ]
24494         };
24495         
24496         if(this.tabScrollable){
24497             h = {
24498                 tag: 'div',
24499                 cls: 'tab-header',
24500                 cn: [
24501                     {
24502                         tag: 'ul',
24503                         cls: 'nav nav-tabs pull-right',
24504                         cn: [
24505                             header
24506                         ]
24507                     }
24508                 ]
24509             };
24510         }
24511         
24512         var cfg = {
24513             tag: 'div',
24514             cls: 'nav-tabs-custom',
24515             cn: [
24516                 h,
24517                 {
24518                     tag: 'div',
24519                     cls: 'tab-content no-padding',
24520                     cn: []
24521                 }
24522             ]
24523         };
24524
24525         return  cfg;
24526     },
24527     initEvents : function()
24528     {
24529         //Roo.log('add add pane handler');
24530         this.on('addpane', this.onAddPane, this);
24531     },
24532      /**
24533      * Updates the box title
24534      * @param {String} html to set the title to.
24535      */
24536     setTitle : function(value)
24537     {
24538         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24539     },
24540     onAddPane : function(pane)
24541     {
24542         this.panes.push(pane);
24543         //Roo.log('addpane');
24544         //Roo.log(pane);
24545         // tabs are rendere left to right..
24546         if(!this.showtabs){
24547             return;
24548         }
24549         
24550         var ctr = this.el.select('.nav-tabs', true).first();
24551          
24552          
24553         var existing = ctr.select('.nav-tab',true);
24554         var qty = existing.getCount();;
24555         
24556         
24557         var tab = ctr.createChild({
24558             tag : 'li',
24559             cls : 'nav-tab' + (qty ? '' : ' active'),
24560             cn : [
24561                 {
24562                     tag : 'a',
24563                     href:'#',
24564                     html : pane.title
24565                 }
24566             ]
24567         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24568         pane.tab = tab;
24569         
24570         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24571         if (!qty) {
24572             pane.el.addClass('active');
24573         }
24574         
24575                 
24576     },
24577     onTabClick : function(ev,un,ob,pane)
24578     {
24579         //Roo.log('tab - prev default');
24580         ev.preventDefault();
24581         
24582         
24583         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24584         pane.tab.addClass('active');
24585         //Roo.log(pane.title);
24586         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24587         // technically we should have a deactivate event.. but maybe add later.
24588         // and it should not de-activate the selected tab...
24589         this.fireEvent('activatepane', pane);
24590         pane.el.addClass('active');
24591         pane.fireEvent('activate');
24592         
24593         
24594     },
24595     
24596     getActivePane : function()
24597     {
24598         var r = false;
24599         Roo.each(this.panes, function(p) {
24600             if(p.el.hasClass('active')){
24601                 r = p;
24602                 return false;
24603             }
24604             
24605             return;
24606         });
24607         
24608         return r;
24609     }
24610     
24611     
24612 });
24613
24614  
24615 /*
24616  * - LGPL
24617  *
24618  * Tab pane
24619  * 
24620  */
24621 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24622 /**
24623  * @class Roo.bootstrap.TabPane
24624  * @extends Roo.bootstrap.Component
24625  * Bootstrap TabPane class
24626  * @cfg {Boolean} active (false | true) Default false
24627  * @cfg {String} title title of panel
24628
24629  * 
24630  * @constructor
24631  * Create a new TabPane
24632  * @param {Object} config The config object
24633  */
24634
24635 Roo.bootstrap.dash.TabPane = function(config){
24636     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24637     
24638     this.addEvents({
24639         // raw events
24640         /**
24641          * @event activate
24642          * When a pane is activated
24643          * @param {Roo.bootstrap.dash.TabPane} pane
24644          */
24645         "activate" : true
24646          
24647     });
24648 };
24649
24650 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24651     
24652     active : false,
24653     title : '',
24654     
24655     // the tabBox that this is attached to.
24656     tab : false,
24657      
24658     getAutoCreate : function() 
24659     {
24660         var cfg = {
24661             tag: 'div',
24662             cls: 'tab-pane'
24663         };
24664         
24665         if(this.active){
24666             cfg.cls += ' active';
24667         }
24668         
24669         return cfg;
24670     },
24671     initEvents  : function()
24672     {
24673         //Roo.log('trigger add pane handler');
24674         this.parent().fireEvent('addpane', this)
24675     },
24676     
24677      /**
24678      * Updates the tab title 
24679      * @param {String} html to set the title to.
24680      */
24681     setTitle: function(str)
24682     {
24683         if (!this.tab) {
24684             return;
24685         }
24686         this.title = str;
24687         this.tab.select('a', true).first().dom.innerHTML = str;
24688         
24689     }
24690     
24691     
24692     
24693 });
24694
24695  
24696
24697
24698  /*
24699  * - LGPL
24700  *
24701  * menu
24702  * 
24703  */
24704 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24705
24706 /**
24707  * @class Roo.bootstrap.menu.Menu
24708  * @extends Roo.bootstrap.Component
24709  * Bootstrap Menu class - container for Menu
24710  * @cfg {String} html Text of the menu
24711  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24712  * @cfg {String} icon Font awesome icon
24713  * @cfg {String} pos Menu align to (top | bottom) default bottom
24714  * 
24715  * 
24716  * @constructor
24717  * Create a new Menu
24718  * @param {Object} config The config object
24719  */
24720
24721
24722 Roo.bootstrap.menu.Menu = function(config){
24723     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24724     
24725     this.addEvents({
24726         /**
24727          * @event beforeshow
24728          * Fires before this menu is displayed
24729          * @param {Roo.bootstrap.menu.Menu} this
24730          */
24731         beforeshow : true,
24732         /**
24733          * @event beforehide
24734          * Fires before this menu is hidden
24735          * @param {Roo.bootstrap.menu.Menu} this
24736          */
24737         beforehide : true,
24738         /**
24739          * @event show
24740          * Fires after this menu is displayed
24741          * @param {Roo.bootstrap.menu.Menu} this
24742          */
24743         show : true,
24744         /**
24745          * @event hide
24746          * Fires after this menu is hidden
24747          * @param {Roo.bootstrap.menu.Menu} this
24748          */
24749         hide : true,
24750         /**
24751          * @event click
24752          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24753          * @param {Roo.bootstrap.menu.Menu} this
24754          * @param {Roo.EventObject} e
24755          */
24756         click : true
24757     });
24758     
24759 };
24760
24761 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24762     
24763     submenu : false,
24764     html : '',
24765     weight : 'default',
24766     icon : false,
24767     pos : 'bottom',
24768     
24769     
24770     getChildContainer : function() {
24771         if(this.isSubMenu){
24772             return this.el;
24773         }
24774         
24775         return this.el.select('ul.dropdown-menu', true).first();  
24776     },
24777     
24778     getAutoCreate : function()
24779     {
24780         var text = [
24781             {
24782                 tag : 'span',
24783                 cls : 'roo-menu-text',
24784                 html : this.html
24785             }
24786         ];
24787         
24788         if(this.icon){
24789             text.unshift({
24790                 tag : 'i',
24791                 cls : 'fa ' + this.icon
24792             })
24793         }
24794         
24795         
24796         var cfg = {
24797             tag : 'div',
24798             cls : 'btn-group',
24799             cn : [
24800                 {
24801                     tag : 'button',
24802                     cls : 'dropdown-button btn btn-' + this.weight,
24803                     cn : text
24804                 },
24805                 {
24806                     tag : 'button',
24807                     cls : 'dropdown-toggle btn btn-' + this.weight,
24808                     cn : [
24809                         {
24810                             tag : 'span',
24811                             cls : 'caret'
24812                         }
24813                     ]
24814                 },
24815                 {
24816                     tag : 'ul',
24817                     cls : 'dropdown-menu'
24818                 }
24819             ]
24820             
24821         };
24822         
24823         if(this.pos == 'top'){
24824             cfg.cls += ' dropup';
24825         }
24826         
24827         if(this.isSubMenu){
24828             cfg = {
24829                 tag : 'ul',
24830                 cls : 'dropdown-menu'
24831             }
24832         }
24833         
24834         return cfg;
24835     },
24836     
24837     onRender : function(ct, position)
24838     {
24839         this.isSubMenu = ct.hasClass('dropdown-submenu');
24840         
24841         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24842     },
24843     
24844     initEvents : function() 
24845     {
24846         if(this.isSubMenu){
24847             return;
24848         }
24849         
24850         this.hidden = true;
24851         
24852         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24853         this.triggerEl.on('click', this.onTriggerPress, this);
24854         
24855         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24856         this.buttonEl.on('click', this.onClick, this);
24857         
24858     },
24859     
24860     list : function()
24861     {
24862         if(this.isSubMenu){
24863             return this.el;
24864         }
24865         
24866         return this.el.select('ul.dropdown-menu', true).first();
24867     },
24868     
24869     onClick : function(e)
24870     {
24871         this.fireEvent("click", this, e);
24872     },
24873     
24874     onTriggerPress  : function(e)
24875     {   
24876         if (this.isVisible()) {
24877             this.hide();
24878         } else {
24879             this.show();
24880         }
24881     },
24882     
24883     isVisible : function(){
24884         return !this.hidden;
24885     },
24886     
24887     show : function()
24888     {
24889         this.fireEvent("beforeshow", this);
24890         
24891         this.hidden = false;
24892         this.el.addClass('open');
24893         
24894         Roo.get(document).on("mouseup", this.onMouseUp, this);
24895         
24896         this.fireEvent("show", this);
24897         
24898         
24899     },
24900     
24901     hide : function()
24902     {
24903         this.fireEvent("beforehide", this);
24904         
24905         this.hidden = true;
24906         this.el.removeClass('open');
24907         
24908         Roo.get(document).un("mouseup", this.onMouseUp);
24909         
24910         this.fireEvent("hide", this);
24911     },
24912     
24913     onMouseUp : function()
24914     {
24915         this.hide();
24916     }
24917     
24918 });
24919
24920  
24921  /*
24922  * - LGPL
24923  *
24924  * menu item
24925  * 
24926  */
24927 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24928
24929 /**
24930  * @class Roo.bootstrap.menu.Item
24931  * @extends Roo.bootstrap.Component
24932  * Bootstrap MenuItem class
24933  * @cfg {Boolean} submenu (true | false) default false
24934  * @cfg {String} html text of the item
24935  * @cfg {String} href the link
24936  * @cfg {Boolean} disable (true | false) default false
24937  * @cfg {Boolean} preventDefault (true | false) default true
24938  * @cfg {String} icon Font awesome icon
24939  * @cfg {String} pos Submenu align to (left | right) default right 
24940  * 
24941  * 
24942  * @constructor
24943  * Create a new Item
24944  * @param {Object} config The config object
24945  */
24946
24947
24948 Roo.bootstrap.menu.Item = function(config){
24949     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24950     this.addEvents({
24951         /**
24952          * @event mouseover
24953          * Fires when the mouse is hovering over this menu
24954          * @param {Roo.bootstrap.menu.Item} this
24955          * @param {Roo.EventObject} e
24956          */
24957         mouseover : true,
24958         /**
24959          * @event mouseout
24960          * Fires when the mouse exits this menu
24961          * @param {Roo.bootstrap.menu.Item} this
24962          * @param {Roo.EventObject} e
24963          */
24964         mouseout : true,
24965         // raw events
24966         /**
24967          * @event click
24968          * The raw click event for the entire grid.
24969          * @param {Roo.EventObject} e
24970          */
24971         click : true
24972     });
24973 };
24974
24975 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24976     
24977     submenu : false,
24978     href : '',
24979     html : '',
24980     preventDefault: true,
24981     disable : false,
24982     icon : false,
24983     pos : 'right',
24984     
24985     getAutoCreate : function()
24986     {
24987         var text = [
24988             {
24989                 tag : 'span',
24990                 cls : 'roo-menu-item-text',
24991                 html : this.html
24992             }
24993         ];
24994         
24995         if(this.icon){
24996             text.unshift({
24997                 tag : 'i',
24998                 cls : 'fa ' + this.icon
24999             })
25000         }
25001         
25002         var cfg = {
25003             tag : 'li',
25004             cn : [
25005                 {
25006                     tag : 'a',
25007                     href : this.href || '#',
25008                     cn : text
25009                 }
25010             ]
25011         };
25012         
25013         if(this.disable){
25014             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25015         }
25016         
25017         if(this.submenu){
25018             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25019             
25020             if(this.pos == 'left'){
25021                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25022             }
25023         }
25024         
25025         return cfg;
25026     },
25027     
25028     initEvents : function() 
25029     {
25030         this.el.on('mouseover', this.onMouseOver, this);
25031         this.el.on('mouseout', this.onMouseOut, this);
25032         
25033         this.el.select('a', true).first().on('click', this.onClick, this);
25034         
25035     },
25036     
25037     onClick : function(e)
25038     {
25039         if(this.preventDefault){
25040             e.preventDefault();
25041         }
25042         
25043         this.fireEvent("click", this, e);
25044     },
25045     
25046     onMouseOver : function(e)
25047     {
25048         if(this.submenu && this.pos == 'left'){
25049             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25050         }
25051         
25052         this.fireEvent("mouseover", this, e);
25053     },
25054     
25055     onMouseOut : function(e)
25056     {
25057         this.fireEvent("mouseout", this, e);
25058     }
25059 });
25060
25061  
25062
25063  /*
25064  * - LGPL
25065  *
25066  * menu separator
25067  * 
25068  */
25069 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25070
25071 /**
25072  * @class Roo.bootstrap.menu.Separator
25073  * @extends Roo.bootstrap.Component
25074  * Bootstrap Separator class
25075  * 
25076  * @constructor
25077  * Create a new Separator
25078  * @param {Object} config The config object
25079  */
25080
25081
25082 Roo.bootstrap.menu.Separator = function(config){
25083     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25084 };
25085
25086 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25087     
25088     getAutoCreate : function(){
25089         var cfg = {
25090             tag : 'li',
25091             cls: 'divider'
25092         };
25093         
25094         return cfg;
25095     }
25096    
25097 });
25098
25099  
25100
25101  /*
25102  * - LGPL
25103  *
25104  * Tooltip
25105  * 
25106  */
25107
25108 /**
25109  * @class Roo.bootstrap.Tooltip
25110  * Bootstrap Tooltip class
25111  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25112  * to determine which dom element triggers the tooltip.
25113  * 
25114  * It needs to add support for additional attributes like tooltip-position
25115  * 
25116  * @constructor
25117  * Create a new Toolti
25118  * @param {Object} config The config object
25119  */
25120
25121 Roo.bootstrap.Tooltip = function(config){
25122     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25123     
25124     this.alignment = Roo.bootstrap.Tooltip.alignment;
25125     
25126     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25127         this.alignment = config.alignment;
25128     }
25129     
25130 };
25131
25132 Roo.apply(Roo.bootstrap.Tooltip, {
25133     /**
25134      * @function init initialize tooltip monitoring.
25135      * @static
25136      */
25137     currentEl : false,
25138     currentTip : false,
25139     currentRegion : false,
25140     
25141     //  init : delay?
25142     
25143     init : function()
25144     {
25145         Roo.get(document).on('mouseover', this.enter ,this);
25146         Roo.get(document).on('mouseout', this.leave, this);
25147          
25148         
25149         this.currentTip = new Roo.bootstrap.Tooltip();
25150     },
25151     
25152     enter : function(ev)
25153     {
25154         var dom = ev.getTarget();
25155         
25156         //Roo.log(['enter',dom]);
25157         var el = Roo.fly(dom);
25158         if (this.currentEl) {
25159             //Roo.log(dom);
25160             //Roo.log(this.currentEl);
25161             //Roo.log(this.currentEl.contains(dom));
25162             if (this.currentEl == el) {
25163                 return;
25164             }
25165             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25166                 return;
25167             }
25168
25169         }
25170         
25171         if (this.currentTip.el) {
25172             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25173         }    
25174         //Roo.log(ev);
25175         
25176         if(!el || el.dom == document){
25177             return;
25178         }
25179         
25180         var bindEl = el;
25181         
25182         // you can not look for children, as if el is the body.. then everythign is the child..
25183         if (!el.attr('tooltip')) { //
25184             if (!el.select("[tooltip]").elements.length) {
25185                 return;
25186             }
25187             // is the mouse over this child...?
25188             bindEl = el.select("[tooltip]").first();
25189             var xy = ev.getXY();
25190             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25191                 //Roo.log("not in region.");
25192                 return;
25193             }
25194             //Roo.log("child element over..");
25195             
25196         }
25197         this.currentEl = bindEl;
25198         this.currentTip.bind(bindEl);
25199         this.currentRegion = Roo.lib.Region.getRegion(dom);
25200         this.currentTip.enter();
25201         
25202     },
25203     leave : function(ev)
25204     {
25205         var dom = ev.getTarget();
25206         //Roo.log(['leave',dom]);
25207         if (!this.currentEl) {
25208             return;
25209         }
25210         
25211         
25212         if (dom != this.currentEl.dom) {
25213             return;
25214         }
25215         var xy = ev.getXY();
25216         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25217             return;
25218         }
25219         // only activate leave if mouse cursor is outside... bounding box..
25220         
25221         
25222         
25223         
25224         if (this.currentTip) {
25225             this.currentTip.leave();
25226         }
25227         //Roo.log('clear currentEl');
25228         this.currentEl = false;
25229         
25230         
25231     },
25232     alignment : {
25233         'left' : ['r-l', [-2,0], 'right'],
25234         'right' : ['l-r', [2,0], 'left'],
25235         'bottom' : ['t-b', [0,2], 'top'],
25236         'top' : [ 'b-t', [0,-2], 'bottom']
25237     }
25238     
25239 });
25240
25241
25242 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25243     
25244     
25245     bindEl : false,
25246     
25247     delay : null, // can be { show : 300 , hide: 500}
25248     
25249     timeout : null,
25250     
25251     hoverState : null, //???
25252     
25253     placement : 'bottom', 
25254     
25255     alignment : false,
25256     
25257     getAutoCreate : function(){
25258     
25259         var cfg = {
25260            cls : 'tooltip',
25261            role : 'tooltip',
25262            cn : [
25263                 {
25264                     cls : 'tooltip-arrow'
25265                 },
25266                 {
25267                     cls : 'tooltip-inner'
25268                 }
25269            ]
25270         };
25271         
25272         return cfg;
25273     },
25274     bind : function(el)
25275     {
25276         this.bindEl = el;
25277     },
25278       
25279     
25280     enter : function () {
25281        
25282         if (this.timeout != null) {
25283             clearTimeout(this.timeout);
25284         }
25285         
25286         this.hoverState = 'in';
25287          //Roo.log("enter - show");
25288         if (!this.delay || !this.delay.show) {
25289             this.show();
25290             return;
25291         }
25292         var _t = this;
25293         this.timeout = setTimeout(function () {
25294             if (_t.hoverState == 'in') {
25295                 _t.show();
25296             }
25297         }, this.delay.show);
25298     },
25299     leave : function()
25300     {
25301         clearTimeout(this.timeout);
25302     
25303         this.hoverState = 'out';
25304          if (!this.delay || !this.delay.hide) {
25305             this.hide();
25306             return;
25307         }
25308        
25309         var _t = this;
25310         this.timeout = setTimeout(function () {
25311             //Roo.log("leave - timeout");
25312             
25313             if (_t.hoverState == 'out') {
25314                 _t.hide();
25315                 Roo.bootstrap.Tooltip.currentEl = false;
25316             }
25317         }, delay);
25318     },
25319     
25320     show : function (msg)
25321     {
25322         if (!this.el) {
25323             this.render(document.body);
25324         }
25325         // set content.
25326         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25327         
25328         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25329         
25330         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25331         
25332         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25333         
25334         var placement = typeof this.placement == 'function' ?
25335             this.placement.call(this, this.el, on_el) :
25336             this.placement;
25337             
25338         var autoToken = /\s?auto?\s?/i;
25339         var autoPlace = autoToken.test(placement);
25340         if (autoPlace) {
25341             placement = placement.replace(autoToken, '') || 'top';
25342         }
25343         
25344         //this.el.detach()
25345         //this.el.setXY([0,0]);
25346         this.el.show();
25347         //this.el.dom.style.display='block';
25348         
25349         //this.el.appendTo(on_el);
25350         
25351         var p = this.getPosition();
25352         var box = this.el.getBox();
25353         
25354         if (autoPlace) {
25355             // fixme..
25356         }
25357         
25358         var align = this.alignment[placement];
25359         
25360         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25361         
25362         if(placement == 'top' || placement == 'bottom'){
25363             if(xy[0] < 0){
25364                 placement = 'right';
25365             }
25366             
25367             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25368                 placement = 'left';
25369             }
25370             
25371             var scroll = Roo.select('body', true).first().getScroll();
25372             
25373             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25374                 placement = 'top';
25375             }
25376             
25377         }
25378         
25379         this.el.alignTo(this.bindEl, align[0],align[1]);
25380         //var arrow = this.el.select('.arrow',true).first();
25381         //arrow.set(align[2], 
25382         
25383         this.el.addClass(placement);
25384         
25385         this.el.addClass('in fade');
25386         
25387         this.hoverState = null;
25388         
25389         if (this.el.hasClass('fade')) {
25390             // fade it?
25391         }
25392         
25393     },
25394     hide : function()
25395     {
25396          
25397         if (!this.el) {
25398             return;
25399         }
25400         //this.el.setXY([0,0]);
25401         this.el.removeClass('in');
25402         //this.el.hide();
25403         
25404     }
25405     
25406 });
25407  
25408
25409  /*
25410  * - LGPL
25411  *
25412  * Location Picker
25413  * 
25414  */
25415
25416 /**
25417  * @class Roo.bootstrap.LocationPicker
25418  * @extends Roo.bootstrap.Component
25419  * Bootstrap LocationPicker class
25420  * @cfg {Number} latitude Position when init default 0
25421  * @cfg {Number} longitude Position when init default 0
25422  * @cfg {Number} zoom default 15
25423  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25424  * @cfg {Boolean} mapTypeControl default false
25425  * @cfg {Boolean} disableDoubleClickZoom default false
25426  * @cfg {Boolean} scrollwheel default true
25427  * @cfg {Boolean} streetViewControl default false
25428  * @cfg {Number} radius default 0
25429  * @cfg {String} locationName
25430  * @cfg {Boolean} draggable default true
25431  * @cfg {Boolean} enableAutocomplete default false
25432  * @cfg {Boolean} enableReverseGeocode default true
25433  * @cfg {String} markerTitle
25434  * 
25435  * @constructor
25436  * Create a new LocationPicker
25437  * @param {Object} config The config object
25438  */
25439
25440
25441 Roo.bootstrap.LocationPicker = function(config){
25442     
25443     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25444     
25445     this.addEvents({
25446         /**
25447          * @event initial
25448          * Fires when the picker initialized.
25449          * @param {Roo.bootstrap.LocationPicker} this
25450          * @param {Google Location} location
25451          */
25452         initial : true,
25453         /**
25454          * @event positionchanged
25455          * Fires when the picker position changed.
25456          * @param {Roo.bootstrap.LocationPicker} this
25457          * @param {Google Location} location
25458          */
25459         positionchanged : true,
25460         /**
25461          * @event resize
25462          * Fires when the map resize.
25463          * @param {Roo.bootstrap.LocationPicker} this
25464          */
25465         resize : true,
25466         /**
25467          * @event show
25468          * Fires when the map show.
25469          * @param {Roo.bootstrap.LocationPicker} this
25470          */
25471         show : true,
25472         /**
25473          * @event hide
25474          * Fires when the map hide.
25475          * @param {Roo.bootstrap.LocationPicker} this
25476          */
25477         hide : true,
25478         /**
25479          * @event mapClick
25480          * Fires when click the map.
25481          * @param {Roo.bootstrap.LocationPicker} this
25482          * @param {Map event} e
25483          */
25484         mapClick : true,
25485         /**
25486          * @event mapRightClick
25487          * Fires when right click the map.
25488          * @param {Roo.bootstrap.LocationPicker} this
25489          * @param {Map event} e
25490          */
25491         mapRightClick : true,
25492         /**
25493          * @event markerClick
25494          * Fires when click the marker.
25495          * @param {Roo.bootstrap.LocationPicker} this
25496          * @param {Map event} e
25497          */
25498         markerClick : true,
25499         /**
25500          * @event markerRightClick
25501          * Fires when right click the marker.
25502          * @param {Roo.bootstrap.LocationPicker} this
25503          * @param {Map event} e
25504          */
25505         markerRightClick : true,
25506         /**
25507          * @event OverlayViewDraw
25508          * Fires when OverlayView Draw
25509          * @param {Roo.bootstrap.LocationPicker} this
25510          */
25511         OverlayViewDraw : true,
25512         /**
25513          * @event OverlayViewOnAdd
25514          * Fires when OverlayView Draw
25515          * @param {Roo.bootstrap.LocationPicker} this
25516          */
25517         OverlayViewOnAdd : true,
25518         /**
25519          * @event OverlayViewOnRemove
25520          * Fires when OverlayView Draw
25521          * @param {Roo.bootstrap.LocationPicker} this
25522          */
25523         OverlayViewOnRemove : true,
25524         /**
25525          * @event OverlayViewShow
25526          * Fires when OverlayView Draw
25527          * @param {Roo.bootstrap.LocationPicker} this
25528          * @param {Pixel} cpx
25529          */
25530         OverlayViewShow : true,
25531         /**
25532          * @event OverlayViewHide
25533          * Fires when OverlayView Draw
25534          * @param {Roo.bootstrap.LocationPicker} this
25535          */
25536         OverlayViewHide : true,
25537         /**
25538          * @event loadexception
25539          * Fires when load google lib failed.
25540          * @param {Roo.bootstrap.LocationPicker} this
25541          */
25542         loadexception : true
25543     });
25544         
25545 };
25546
25547 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25548     
25549     gMapContext: false,
25550     
25551     latitude: 0,
25552     longitude: 0,
25553     zoom: 15,
25554     mapTypeId: false,
25555     mapTypeControl: false,
25556     disableDoubleClickZoom: false,
25557     scrollwheel: true,
25558     streetViewControl: false,
25559     radius: 0,
25560     locationName: '',
25561     draggable: true,
25562     enableAutocomplete: false,
25563     enableReverseGeocode: true,
25564     markerTitle: '',
25565     
25566     getAutoCreate: function()
25567     {
25568
25569         var cfg = {
25570             tag: 'div',
25571             cls: 'roo-location-picker'
25572         };
25573         
25574         return cfg
25575     },
25576     
25577     initEvents: function(ct, position)
25578     {       
25579         if(!this.el.getWidth() || this.isApplied()){
25580             return;
25581         }
25582         
25583         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25584         
25585         this.initial();
25586     },
25587     
25588     initial: function()
25589     {
25590         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25591             this.fireEvent('loadexception', this);
25592             return;
25593         }
25594         
25595         if(!this.mapTypeId){
25596             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25597         }
25598         
25599         this.gMapContext = this.GMapContext();
25600         
25601         this.initOverlayView();
25602         
25603         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25604         
25605         var _this = this;
25606                 
25607         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25608             _this.setPosition(_this.gMapContext.marker.position);
25609         });
25610         
25611         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25612             _this.fireEvent('mapClick', this, event);
25613             
25614         });
25615
25616         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25617             _this.fireEvent('mapRightClick', this, event);
25618             
25619         });
25620         
25621         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25622             _this.fireEvent('markerClick', this, event);
25623             
25624         });
25625
25626         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25627             _this.fireEvent('markerRightClick', this, event);
25628             
25629         });
25630         
25631         this.setPosition(this.gMapContext.location);
25632         
25633         this.fireEvent('initial', this, this.gMapContext.location);
25634     },
25635     
25636     initOverlayView: function()
25637     {
25638         var _this = this;
25639         
25640         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25641             
25642             draw: function()
25643             {
25644                 _this.fireEvent('OverlayViewDraw', _this);
25645             },
25646             
25647             onAdd: function()
25648             {
25649                 _this.fireEvent('OverlayViewOnAdd', _this);
25650             },
25651             
25652             onRemove: function()
25653             {
25654                 _this.fireEvent('OverlayViewOnRemove', _this);
25655             },
25656             
25657             show: function(cpx)
25658             {
25659                 _this.fireEvent('OverlayViewShow', _this, cpx);
25660             },
25661             
25662             hide: function()
25663             {
25664                 _this.fireEvent('OverlayViewHide', _this);
25665             }
25666             
25667         });
25668     },
25669     
25670     fromLatLngToContainerPixel: function(event)
25671     {
25672         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25673     },
25674     
25675     isApplied: function() 
25676     {
25677         return this.getGmapContext() == false ? false : true;
25678     },
25679     
25680     getGmapContext: function() 
25681     {
25682         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25683     },
25684     
25685     GMapContext: function() 
25686     {
25687         var position = new google.maps.LatLng(this.latitude, this.longitude);
25688         
25689         var _map = new google.maps.Map(this.el.dom, {
25690             center: position,
25691             zoom: this.zoom,
25692             mapTypeId: this.mapTypeId,
25693             mapTypeControl: this.mapTypeControl,
25694             disableDoubleClickZoom: this.disableDoubleClickZoom,
25695             scrollwheel: this.scrollwheel,
25696             streetViewControl: this.streetViewControl,
25697             locationName: this.locationName,
25698             draggable: this.draggable,
25699             enableAutocomplete: this.enableAutocomplete,
25700             enableReverseGeocode: this.enableReverseGeocode
25701         });
25702         
25703         var _marker = new google.maps.Marker({
25704             position: position,
25705             map: _map,
25706             title: this.markerTitle,
25707             draggable: this.draggable
25708         });
25709         
25710         return {
25711             map: _map,
25712             marker: _marker,
25713             circle: null,
25714             location: position,
25715             radius: this.radius,
25716             locationName: this.locationName,
25717             addressComponents: {
25718                 formatted_address: null,
25719                 addressLine1: null,
25720                 addressLine2: null,
25721                 streetName: null,
25722                 streetNumber: null,
25723                 city: null,
25724                 district: null,
25725                 state: null,
25726                 stateOrProvince: null
25727             },
25728             settings: this,
25729             domContainer: this.el.dom,
25730             geodecoder: new google.maps.Geocoder()
25731         };
25732     },
25733     
25734     drawCircle: function(center, radius, options) 
25735     {
25736         if (this.gMapContext.circle != null) {
25737             this.gMapContext.circle.setMap(null);
25738         }
25739         if (radius > 0) {
25740             radius *= 1;
25741             options = Roo.apply({}, options, {
25742                 strokeColor: "#0000FF",
25743                 strokeOpacity: .35,
25744                 strokeWeight: 2,
25745                 fillColor: "#0000FF",
25746                 fillOpacity: .2
25747             });
25748             
25749             options.map = this.gMapContext.map;
25750             options.radius = radius;
25751             options.center = center;
25752             this.gMapContext.circle = new google.maps.Circle(options);
25753             return this.gMapContext.circle;
25754         }
25755         
25756         return null;
25757     },
25758     
25759     setPosition: function(location) 
25760     {
25761         this.gMapContext.location = location;
25762         this.gMapContext.marker.setPosition(location);
25763         this.gMapContext.map.panTo(location);
25764         this.drawCircle(location, this.gMapContext.radius, {});
25765         
25766         var _this = this;
25767         
25768         if (this.gMapContext.settings.enableReverseGeocode) {
25769             this.gMapContext.geodecoder.geocode({
25770                 latLng: this.gMapContext.location
25771             }, function(results, status) {
25772                 
25773                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25774                     _this.gMapContext.locationName = results[0].formatted_address;
25775                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25776                     
25777                     _this.fireEvent('positionchanged', this, location);
25778                 }
25779             });
25780             
25781             return;
25782         }
25783         
25784         this.fireEvent('positionchanged', this, location);
25785     },
25786     
25787     resize: function()
25788     {
25789         google.maps.event.trigger(this.gMapContext.map, "resize");
25790         
25791         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25792         
25793         this.fireEvent('resize', this);
25794     },
25795     
25796     setPositionByLatLng: function(latitude, longitude)
25797     {
25798         this.setPosition(new google.maps.LatLng(latitude, longitude));
25799     },
25800     
25801     getCurrentPosition: function() 
25802     {
25803         return {
25804             latitude: this.gMapContext.location.lat(),
25805             longitude: this.gMapContext.location.lng()
25806         };
25807     },
25808     
25809     getAddressName: function() 
25810     {
25811         return this.gMapContext.locationName;
25812     },
25813     
25814     getAddressComponents: function() 
25815     {
25816         return this.gMapContext.addressComponents;
25817     },
25818     
25819     address_component_from_google_geocode: function(address_components) 
25820     {
25821         var result = {};
25822         
25823         for (var i = 0; i < address_components.length; i++) {
25824             var component = address_components[i];
25825             if (component.types.indexOf("postal_code") >= 0) {
25826                 result.postalCode = component.short_name;
25827             } else if (component.types.indexOf("street_number") >= 0) {
25828                 result.streetNumber = component.short_name;
25829             } else if (component.types.indexOf("route") >= 0) {
25830                 result.streetName = component.short_name;
25831             } else if (component.types.indexOf("neighborhood") >= 0) {
25832                 result.city = component.short_name;
25833             } else if (component.types.indexOf("locality") >= 0) {
25834                 result.city = component.short_name;
25835             } else if (component.types.indexOf("sublocality") >= 0) {
25836                 result.district = component.short_name;
25837             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25838                 result.stateOrProvince = component.short_name;
25839             } else if (component.types.indexOf("country") >= 0) {
25840                 result.country = component.short_name;
25841             }
25842         }
25843         
25844         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25845         result.addressLine2 = "";
25846         return result;
25847     },
25848     
25849     setZoomLevel: function(zoom)
25850     {
25851         this.gMapContext.map.setZoom(zoom);
25852     },
25853     
25854     show: function()
25855     {
25856         if(!this.el){
25857             return;
25858         }
25859         
25860         this.el.show();
25861         
25862         this.resize();
25863         
25864         this.fireEvent('show', this);
25865     },
25866     
25867     hide: function()
25868     {
25869         if(!this.el){
25870             return;
25871         }
25872         
25873         this.el.hide();
25874         
25875         this.fireEvent('hide', this);
25876     }
25877     
25878 });
25879
25880 Roo.apply(Roo.bootstrap.LocationPicker, {
25881     
25882     OverlayView : function(map, options)
25883     {
25884         options = options || {};
25885         
25886         this.setMap(map);
25887     }
25888     
25889     
25890 });/*
25891  * - LGPL
25892  *
25893  * Alert
25894  * 
25895  */
25896
25897 /**
25898  * @class Roo.bootstrap.Alert
25899  * @extends Roo.bootstrap.Component
25900  * Bootstrap Alert class
25901  * @cfg {String} title The title of alert
25902  * @cfg {String} html The content of alert
25903  * @cfg {String} weight (  success | info | warning | danger )
25904  * @cfg {String} faicon font-awesomeicon
25905  * 
25906  * @constructor
25907  * Create a new alert
25908  * @param {Object} config The config object
25909  */
25910
25911
25912 Roo.bootstrap.Alert = function(config){
25913     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25914     
25915 };
25916
25917 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25918     
25919     title: '',
25920     html: '',
25921     weight: false,
25922     faicon: false,
25923     
25924     getAutoCreate : function()
25925     {
25926         
25927         var cfg = {
25928             tag : 'div',
25929             cls : 'alert',
25930             cn : [
25931                 {
25932                     tag : 'i',
25933                     cls : 'roo-alert-icon'
25934                     
25935                 },
25936                 {
25937                     tag : 'b',
25938                     cls : 'roo-alert-title',
25939                     html : this.title
25940                 },
25941                 {
25942                     tag : 'span',
25943                     cls : 'roo-alert-text',
25944                     html : this.html
25945                 }
25946             ]
25947         };
25948         
25949         if(this.faicon){
25950             cfg.cn[0].cls += ' fa ' + this.faicon;
25951         }
25952         
25953         if(this.weight){
25954             cfg.cls += ' alert-' + this.weight;
25955         }
25956         
25957         return cfg;
25958     },
25959     
25960     initEvents: function() 
25961     {
25962         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25963     },
25964     
25965     setTitle : function(str)
25966     {
25967         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25968     },
25969     
25970     setText : function(str)
25971     {
25972         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25973     },
25974     
25975     setWeight : function(weight)
25976     {
25977         if(this.weight){
25978             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25979         }
25980         
25981         this.weight = weight;
25982         
25983         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25984     },
25985     
25986     setIcon : function(icon)
25987     {
25988         if(this.faicon){
25989             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25990         }
25991         
25992         this.faicon = icon;
25993         
25994         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25995     },
25996     
25997     hide: function() 
25998     {
25999         this.el.hide();   
26000     },
26001     
26002     show: function() 
26003     {  
26004         this.el.show();   
26005     }
26006     
26007 });
26008
26009  
26010 /*
26011 * Licence: LGPL
26012 */
26013
26014 /**
26015  * @class Roo.bootstrap.UploadCropbox
26016  * @extends Roo.bootstrap.Component
26017  * Bootstrap UploadCropbox class
26018  * @cfg {String} emptyText show when image has been loaded
26019  * @cfg {String} rotateNotify show when image too small to rotate
26020  * @cfg {Number} errorTimeout default 3000
26021  * @cfg {Number} minWidth default 300
26022  * @cfg {Number} minHeight default 300
26023  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26024  * @cfg {Boolean} isDocument (true|false) default false
26025  * @cfg {String} url action url
26026  * @cfg {String} paramName default 'imageUpload'
26027  * @cfg {String} method default POST
26028  * @cfg {Boolean} loadMask (true|false) default true
26029  * @cfg {Boolean} loadingText default 'Loading...'
26030  * 
26031  * @constructor
26032  * Create a new UploadCropbox
26033  * @param {Object} config The config object
26034  */
26035
26036 Roo.bootstrap.UploadCropbox = function(config){
26037     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26038     
26039     this.addEvents({
26040         /**
26041          * @event beforeselectfile
26042          * Fire before select file
26043          * @param {Roo.bootstrap.UploadCropbox} this
26044          */
26045         "beforeselectfile" : true,
26046         /**
26047          * @event initial
26048          * Fire after initEvent
26049          * @param {Roo.bootstrap.UploadCropbox} this
26050          */
26051         "initial" : true,
26052         /**
26053          * @event crop
26054          * Fire after initEvent
26055          * @param {Roo.bootstrap.UploadCropbox} this
26056          * @param {String} data
26057          */
26058         "crop" : true,
26059         /**
26060          * @event prepare
26061          * Fire when preparing the file data
26062          * @param {Roo.bootstrap.UploadCropbox} this
26063          * @param {Object} file
26064          */
26065         "prepare" : true,
26066         /**
26067          * @event exception
26068          * Fire when get exception
26069          * @param {Roo.bootstrap.UploadCropbox} this
26070          * @param {XMLHttpRequest} xhr
26071          */
26072         "exception" : true,
26073         /**
26074          * @event beforeloadcanvas
26075          * Fire before load the canvas
26076          * @param {Roo.bootstrap.UploadCropbox} this
26077          * @param {String} src
26078          */
26079         "beforeloadcanvas" : true,
26080         /**
26081          * @event trash
26082          * Fire when trash image
26083          * @param {Roo.bootstrap.UploadCropbox} this
26084          */
26085         "trash" : true,
26086         /**
26087          * @event download
26088          * Fire when download the image
26089          * @param {Roo.bootstrap.UploadCropbox} this
26090          */
26091         "download" : true,
26092         /**
26093          * @event footerbuttonclick
26094          * Fire when footerbuttonclick
26095          * @param {Roo.bootstrap.UploadCropbox} this
26096          * @param {String} type
26097          */
26098         "footerbuttonclick" : true,
26099         /**
26100          * @event resize
26101          * Fire when resize
26102          * @param {Roo.bootstrap.UploadCropbox} this
26103          */
26104         "resize" : true,
26105         /**
26106          * @event rotate
26107          * Fire when rotate the image
26108          * @param {Roo.bootstrap.UploadCropbox} this
26109          * @param {String} pos
26110          */
26111         "rotate" : true,
26112         /**
26113          * @event inspect
26114          * Fire when inspect the file
26115          * @param {Roo.bootstrap.UploadCropbox} this
26116          * @param {Object} file
26117          */
26118         "inspect" : true,
26119         /**
26120          * @event upload
26121          * Fire when xhr upload the file
26122          * @param {Roo.bootstrap.UploadCropbox} this
26123          * @param {Object} data
26124          */
26125         "upload" : true,
26126         /**
26127          * @event arrange
26128          * Fire when arrange the file data
26129          * @param {Roo.bootstrap.UploadCropbox} this
26130          * @param {Object} formData
26131          */
26132         "arrange" : true
26133     });
26134     
26135     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26136 };
26137
26138 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26139     
26140     emptyText : 'Click to upload image',
26141     rotateNotify : 'Image is too small to rotate',
26142     errorTimeout : 3000,
26143     scale : 0,
26144     baseScale : 1,
26145     rotate : 0,
26146     dragable : false,
26147     pinching : false,
26148     mouseX : 0,
26149     mouseY : 0,
26150     cropData : false,
26151     minWidth : 300,
26152     minHeight : 300,
26153     file : false,
26154     exif : {},
26155     baseRotate : 1,
26156     cropType : 'image/jpeg',
26157     buttons : false,
26158     canvasLoaded : false,
26159     isDocument : false,
26160     method : 'POST',
26161     paramName : 'imageUpload',
26162     loadMask : true,
26163     loadingText : 'Loading...',
26164     maskEl : false,
26165     
26166     getAutoCreate : function()
26167     {
26168         var cfg = {
26169             tag : 'div',
26170             cls : 'roo-upload-cropbox',
26171             cn : [
26172                 {
26173                     tag : 'input',
26174                     cls : 'roo-upload-cropbox-selector',
26175                     type : 'file'
26176                 },
26177                 {
26178                     tag : 'div',
26179                     cls : 'roo-upload-cropbox-body',
26180                     style : 'cursor:pointer',
26181                     cn : [
26182                         {
26183                             tag : 'div',
26184                             cls : 'roo-upload-cropbox-preview'
26185                         },
26186                         {
26187                             tag : 'div',
26188                             cls : 'roo-upload-cropbox-thumb'
26189                         },
26190                         {
26191                             tag : 'div',
26192                             cls : 'roo-upload-cropbox-empty-notify',
26193                             html : this.emptyText
26194                         },
26195                         {
26196                             tag : 'div',
26197                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26198                             html : this.rotateNotify
26199                         }
26200                     ]
26201                 },
26202                 {
26203                     tag : 'div',
26204                     cls : 'roo-upload-cropbox-footer',
26205                     cn : {
26206                         tag : 'div',
26207                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26208                         cn : []
26209                     }
26210                 }
26211             ]
26212         };
26213         
26214         return cfg;
26215     },
26216     
26217     onRender : function(ct, position)
26218     {
26219         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26220         
26221         if (this.buttons.length) {
26222             
26223             Roo.each(this.buttons, function(bb) {
26224                 
26225                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26226                 
26227                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26228                 
26229             }, this);
26230         }
26231         
26232         if(this.loadMask){
26233             this.maskEl = this.el;
26234         }
26235     },
26236     
26237     initEvents : function()
26238     {
26239         this.urlAPI = (window.createObjectURL && window) || 
26240                                 (window.URL && URL.revokeObjectURL && URL) || 
26241                                 (window.webkitURL && webkitURL);
26242                         
26243         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26244         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26245         
26246         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26247         this.selectorEl.hide();
26248         
26249         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26250         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26251         
26252         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26253         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26254         this.thumbEl.hide();
26255         
26256         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26257         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26258         
26259         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26260         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26261         this.errorEl.hide();
26262         
26263         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26264         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26265         this.footerEl.hide();
26266         
26267         this.setThumbBoxSize();
26268         
26269         this.bind();
26270         
26271         this.resize();
26272         
26273         this.fireEvent('initial', this);
26274     },
26275
26276     bind : function()
26277     {
26278         var _this = this;
26279         
26280         window.addEventListener("resize", function() { _this.resize(); } );
26281         
26282         this.bodyEl.on('click', this.beforeSelectFile, this);
26283         
26284         if(Roo.isTouch){
26285             this.bodyEl.on('touchstart', this.onTouchStart, this);
26286             this.bodyEl.on('touchmove', this.onTouchMove, this);
26287             this.bodyEl.on('touchend', this.onTouchEnd, this);
26288         }
26289         
26290         if(!Roo.isTouch){
26291             this.bodyEl.on('mousedown', this.onMouseDown, this);
26292             this.bodyEl.on('mousemove', this.onMouseMove, this);
26293             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26294             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26295             Roo.get(document).on('mouseup', this.onMouseUp, this);
26296         }
26297         
26298         this.selectorEl.on('change', this.onFileSelected, this);
26299     },
26300     
26301     reset : function()
26302     {    
26303         this.scale = 0;
26304         this.baseScale = 1;
26305         this.rotate = 0;
26306         this.baseRotate = 1;
26307         this.dragable = false;
26308         this.pinching = false;
26309         this.mouseX = 0;
26310         this.mouseY = 0;
26311         this.cropData = false;
26312         this.notifyEl.dom.innerHTML = this.emptyText;
26313         
26314         this.selectorEl.dom.value = '';
26315         
26316     },
26317     
26318     resize : function()
26319     {
26320         if(this.fireEvent('resize', this) != false){
26321             this.setThumbBoxPosition();
26322             this.setCanvasPosition();
26323         }
26324     },
26325     
26326     onFooterButtonClick : function(e, el, o, type)
26327     {
26328         switch (type) {
26329             case 'rotate-left' :
26330                 this.onRotateLeft(e);
26331                 break;
26332             case 'rotate-right' :
26333                 this.onRotateRight(e);
26334                 break;
26335             case 'picture' :
26336                 this.beforeSelectFile(e);
26337                 break;
26338             case 'trash' :
26339                 this.trash(e);
26340                 break;
26341             case 'crop' :
26342                 this.crop(e);
26343                 break;
26344             case 'download' :
26345                 this.download(e);
26346                 break;
26347             default :
26348                 break;
26349         }
26350         
26351         this.fireEvent('footerbuttonclick', this, type);
26352     },
26353     
26354     beforeSelectFile : function(e)
26355     {
26356         e.preventDefault();
26357         
26358         if(this.fireEvent('beforeselectfile', this) != false){
26359             this.selectorEl.dom.click();
26360         }
26361     },
26362     
26363     onFileSelected : function(e)
26364     {
26365         e.preventDefault();
26366         
26367         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26368             return;
26369         }
26370         
26371         var file = this.selectorEl.dom.files[0];
26372         
26373         if(this.fireEvent('inspect', this, file) != false){
26374             this.prepare(file);
26375         }
26376         
26377     },
26378     
26379     trash : function(e)
26380     {
26381         this.fireEvent('trash', this);
26382     },
26383     
26384     download : function(e)
26385     {
26386         this.fireEvent('download', this);
26387     },
26388     
26389     loadCanvas : function(src)
26390     {   
26391         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26392             
26393             this.reset();
26394             
26395             this.imageEl = document.createElement('img');
26396             
26397             var _this = this;
26398             
26399             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26400             
26401             this.imageEl.src = src;
26402         }
26403     },
26404     
26405     onLoadCanvas : function()
26406     {   
26407         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26408         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26409         
26410         this.bodyEl.un('click', this.beforeSelectFile, this);
26411         
26412         this.notifyEl.hide();
26413         this.thumbEl.show();
26414         this.footerEl.show();
26415         
26416         this.baseRotateLevel();
26417         
26418         if(this.isDocument){
26419             this.setThumbBoxSize();
26420         }
26421         
26422         this.setThumbBoxPosition();
26423         
26424         this.baseScaleLevel();
26425         
26426         this.draw();
26427         
26428         this.resize();
26429         
26430         this.canvasLoaded = true;
26431         
26432         if(this.loadMask){
26433             this.maskEl.unmask();
26434         }
26435         
26436     },
26437     
26438     setCanvasPosition : function()
26439     {   
26440         if(!this.canvasEl){
26441             return;
26442         }
26443         
26444         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26445         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26446         
26447         this.previewEl.setLeft(pw);
26448         this.previewEl.setTop(ph);
26449         
26450     },
26451     
26452     onMouseDown : function(e)
26453     {   
26454         e.stopEvent();
26455         
26456         this.dragable = true;
26457         this.pinching = false;
26458         
26459         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26460             this.dragable = false;
26461             return;
26462         }
26463         
26464         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26465         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26466         
26467     },
26468     
26469     onMouseMove : function(e)
26470     {   
26471         e.stopEvent();
26472         
26473         if(!this.canvasLoaded){
26474             return;
26475         }
26476         
26477         if (!this.dragable){
26478             return;
26479         }
26480         
26481         var minX = Math.ceil(this.thumbEl.getLeft(true));
26482         var minY = Math.ceil(this.thumbEl.getTop(true));
26483         
26484         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26485         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26486         
26487         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26488         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26489         
26490         x = x - this.mouseX;
26491         y = y - this.mouseY;
26492         
26493         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26494         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26495         
26496         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26497         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26498         
26499         this.previewEl.setLeft(bgX);
26500         this.previewEl.setTop(bgY);
26501         
26502         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26503         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26504     },
26505     
26506     onMouseUp : function(e)
26507     {   
26508         e.stopEvent();
26509         
26510         this.dragable = false;
26511     },
26512     
26513     onMouseWheel : function(e)
26514     {   
26515         e.stopEvent();
26516         
26517         this.startScale = this.scale;
26518         
26519         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26520         
26521         if(!this.zoomable()){
26522             this.scale = this.startScale;
26523             return;
26524         }
26525         
26526         this.draw();
26527         
26528         return;
26529     },
26530     
26531     zoomable : function()
26532     {
26533         var minScale = this.thumbEl.getWidth() / this.minWidth;
26534         
26535         if(this.minWidth < this.minHeight){
26536             minScale = this.thumbEl.getHeight() / this.minHeight;
26537         }
26538         
26539         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26540         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26541         
26542         if(
26543                 this.isDocument &&
26544                 (this.rotate == 0 || this.rotate == 180) && 
26545                 (
26546                     width > this.imageEl.OriginWidth || 
26547                     height > this.imageEl.OriginHeight ||
26548                     (width < this.minWidth && height < this.minHeight)
26549                 )
26550         ){
26551             return false;
26552         }
26553         
26554         if(
26555                 this.isDocument &&
26556                 (this.rotate == 90 || this.rotate == 270) && 
26557                 (
26558                     width > this.imageEl.OriginWidth || 
26559                     height > this.imageEl.OriginHeight ||
26560                     (width < this.minHeight && height < this.minWidth)
26561                 )
26562         ){
26563             return false;
26564         }
26565         
26566         if(
26567                 !this.isDocument &&
26568                 (this.rotate == 0 || this.rotate == 180) && 
26569                 (
26570                     width < this.minWidth || 
26571                     width > this.imageEl.OriginWidth || 
26572                     height < this.minHeight || 
26573                     height > this.imageEl.OriginHeight
26574                 )
26575         ){
26576             return false;
26577         }
26578         
26579         if(
26580                 !this.isDocument &&
26581                 (this.rotate == 90 || this.rotate == 270) && 
26582                 (
26583                     width < this.minHeight || 
26584                     width > this.imageEl.OriginWidth || 
26585                     height < this.minWidth || 
26586                     height > this.imageEl.OriginHeight
26587                 )
26588         ){
26589             return false;
26590         }
26591         
26592         return true;
26593         
26594     },
26595     
26596     onRotateLeft : function(e)
26597     {   
26598         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26599             
26600             var minScale = this.thumbEl.getWidth() / this.minWidth;
26601             
26602             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26603             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26604             
26605             this.startScale = this.scale;
26606             
26607             while (this.getScaleLevel() < minScale){
26608             
26609                 this.scale = this.scale + 1;
26610                 
26611                 if(!this.zoomable()){
26612                     break;
26613                 }
26614                 
26615                 if(
26616                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26617                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26618                 ){
26619                     continue;
26620                 }
26621                 
26622                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26623
26624                 this.draw();
26625                 
26626                 return;
26627             }
26628             
26629             this.scale = this.startScale;
26630             
26631             this.onRotateFail();
26632             
26633             return false;
26634         }
26635         
26636         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26637
26638         if(this.isDocument){
26639             this.setThumbBoxSize();
26640             this.setThumbBoxPosition();
26641             this.setCanvasPosition();
26642         }
26643         
26644         this.draw();
26645         
26646         this.fireEvent('rotate', this, 'left');
26647         
26648     },
26649     
26650     onRotateRight : function(e)
26651     {
26652         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26653             
26654             var minScale = this.thumbEl.getWidth() / this.minWidth;
26655         
26656             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26657             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26658             
26659             this.startScale = this.scale;
26660             
26661             while (this.getScaleLevel() < minScale){
26662             
26663                 this.scale = this.scale + 1;
26664                 
26665                 if(!this.zoomable()){
26666                     break;
26667                 }
26668                 
26669                 if(
26670                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26671                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26672                 ){
26673                     continue;
26674                 }
26675                 
26676                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26677
26678                 this.draw();
26679                 
26680                 return;
26681             }
26682             
26683             this.scale = this.startScale;
26684             
26685             this.onRotateFail();
26686             
26687             return false;
26688         }
26689         
26690         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26691
26692         if(this.isDocument){
26693             this.setThumbBoxSize();
26694             this.setThumbBoxPosition();
26695             this.setCanvasPosition();
26696         }
26697         
26698         this.draw();
26699         
26700         this.fireEvent('rotate', this, 'right');
26701     },
26702     
26703     onRotateFail : function()
26704     {
26705         this.errorEl.show(true);
26706         
26707         var _this = this;
26708         
26709         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26710     },
26711     
26712     draw : function()
26713     {
26714         this.previewEl.dom.innerHTML = '';
26715         
26716         var canvasEl = document.createElement("canvas");
26717         
26718         var contextEl = canvasEl.getContext("2d");
26719         
26720         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26721         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26722         var center = this.imageEl.OriginWidth / 2;
26723         
26724         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26725             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26726             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26727             center = this.imageEl.OriginHeight / 2;
26728         }
26729         
26730         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26731         
26732         contextEl.translate(center, center);
26733         contextEl.rotate(this.rotate * Math.PI / 180);
26734
26735         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26736         
26737         this.canvasEl = document.createElement("canvas");
26738         
26739         this.contextEl = this.canvasEl.getContext("2d");
26740         
26741         switch (this.rotate) {
26742             case 0 :
26743                 
26744                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26745                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26746                 
26747                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26748                 
26749                 break;
26750             case 90 : 
26751                 
26752                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26753                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26754                 
26755                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26756                     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);
26757                     break;
26758                 }
26759                 
26760                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26761                 
26762                 break;
26763             case 180 :
26764                 
26765                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26766                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26767                 
26768                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26769                     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);
26770                     break;
26771                 }
26772                 
26773                 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);
26774                 
26775                 break;
26776             case 270 :
26777                 
26778                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26779                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26780         
26781                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26782                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26783                     break;
26784                 }
26785                 
26786                 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);
26787                 
26788                 break;
26789             default : 
26790                 break;
26791         }
26792         
26793         this.previewEl.appendChild(this.canvasEl);
26794         
26795         this.setCanvasPosition();
26796     },
26797     
26798     crop : function()
26799     {
26800         if(!this.canvasLoaded){
26801             return;
26802         }
26803         
26804         var imageCanvas = document.createElement("canvas");
26805         
26806         var imageContext = imageCanvas.getContext("2d");
26807         
26808         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26809         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26810         
26811         var center = imageCanvas.width / 2;
26812         
26813         imageContext.translate(center, center);
26814         
26815         imageContext.rotate(this.rotate * Math.PI / 180);
26816         
26817         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26818         
26819         var canvas = document.createElement("canvas");
26820         
26821         var context = canvas.getContext("2d");
26822                 
26823         canvas.width = this.minWidth;
26824         canvas.height = this.minHeight;
26825
26826         switch (this.rotate) {
26827             case 0 :
26828                 
26829                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26830                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26831                 
26832                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26833                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26834                 
26835                 var targetWidth = this.minWidth - 2 * x;
26836                 var targetHeight = this.minHeight - 2 * y;
26837                 
26838                 var scale = 1;
26839                 
26840                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26841                     scale = targetWidth / width;
26842                 }
26843                 
26844                 if(x > 0 && y == 0){
26845                     scale = targetHeight / height;
26846                 }
26847                 
26848                 if(x > 0 && y > 0){
26849                     scale = targetWidth / width;
26850                     
26851                     if(width < height){
26852                         scale = targetHeight / height;
26853                     }
26854                 }
26855                 
26856                 context.scale(scale, scale);
26857                 
26858                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26859                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26860
26861                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26862                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26863
26864                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26865                 
26866                 break;
26867             case 90 : 
26868                 
26869                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26870                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26871                 
26872                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26873                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26874                 
26875                 var targetWidth = this.minWidth - 2 * x;
26876                 var targetHeight = this.minHeight - 2 * y;
26877                 
26878                 var scale = 1;
26879                 
26880                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26881                     scale = targetWidth / width;
26882                 }
26883                 
26884                 if(x > 0 && y == 0){
26885                     scale = targetHeight / height;
26886                 }
26887                 
26888                 if(x > 0 && y > 0){
26889                     scale = targetWidth / width;
26890                     
26891                     if(width < height){
26892                         scale = targetHeight / height;
26893                     }
26894                 }
26895                 
26896                 context.scale(scale, scale);
26897                 
26898                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26899                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26900
26901                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26902                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26903                 
26904                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26905                 
26906                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26907                 
26908                 break;
26909             case 180 :
26910                 
26911                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26912                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26913                 
26914                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26915                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26916                 
26917                 var targetWidth = this.minWidth - 2 * x;
26918                 var targetHeight = this.minHeight - 2 * y;
26919                 
26920                 var scale = 1;
26921                 
26922                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26923                     scale = targetWidth / width;
26924                 }
26925                 
26926                 if(x > 0 && y == 0){
26927                     scale = targetHeight / height;
26928                 }
26929                 
26930                 if(x > 0 && y > 0){
26931                     scale = targetWidth / width;
26932                     
26933                     if(width < height){
26934                         scale = targetHeight / height;
26935                     }
26936                 }
26937                 
26938                 context.scale(scale, scale);
26939                 
26940                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26941                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26942
26943                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26944                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26945
26946                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26947                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26948                 
26949                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26950                 
26951                 break;
26952             case 270 :
26953                 
26954                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26955                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26956                 
26957                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26958                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26959                 
26960                 var targetWidth = this.minWidth - 2 * x;
26961                 var targetHeight = this.minHeight - 2 * y;
26962                 
26963                 var scale = 1;
26964                 
26965                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26966                     scale = targetWidth / width;
26967                 }
26968                 
26969                 if(x > 0 && y == 0){
26970                     scale = targetHeight / height;
26971                 }
26972                 
26973                 if(x > 0 && y > 0){
26974                     scale = targetWidth / width;
26975                     
26976                     if(width < height){
26977                         scale = targetHeight / height;
26978                     }
26979                 }
26980                 
26981                 context.scale(scale, scale);
26982                 
26983                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26984                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26985
26986                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26987                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26988                 
26989                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26990                 
26991                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26992                 
26993                 break;
26994             default : 
26995                 break;
26996         }
26997         
26998         this.cropData = canvas.toDataURL(this.cropType);
26999         
27000         if(this.fireEvent('crop', this, this.cropData) !== false){
27001             this.process(this.file, this.cropData);
27002         }
27003         
27004         return;
27005         
27006     },
27007     
27008     setThumbBoxSize : function()
27009     {
27010         var width, height;
27011         
27012         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27013             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27014             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27015             
27016             this.minWidth = width;
27017             this.minHeight = height;
27018             
27019             if(this.rotate == 90 || this.rotate == 270){
27020                 this.minWidth = height;
27021                 this.minHeight = width;
27022             }
27023         }
27024         
27025         height = 300;
27026         width = Math.ceil(this.minWidth * height / this.minHeight);
27027         
27028         if(this.minWidth > this.minHeight){
27029             width = 300;
27030             height = Math.ceil(this.minHeight * width / this.minWidth);
27031         }
27032         
27033         this.thumbEl.setStyle({
27034             width : width + 'px',
27035             height : height + 'px'
27036         });
27037
27038         return;
27039             
27040     },
27041     
27042     setThumbBoxPosition : function()
27043     {
27044         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27045         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27046         
27047         this.thumbEl.setLeft(x);
27048         this.thumbEl.setTop(y);
27049         
27050     },
27051     
27052     baseRotateLevel : function()
27053     {
27054         this.baseRotate = 1;
27055         
27056         if(
27057                 typeof(this.exif) != 'undefined' &&
27058                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27059                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27060         ){
27061             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27062         }
27063         
27064         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27065         
27066     },
27067     
27068     baseScaleLevel : function()
27069     {
27070         var width, height;
27071         
27072         if(this.isDocument){
27073             
27074             if(this.baseRotate == 6 || this.baseRotate == 8){
27075             
27076                 height = this.thumbEl.getHeight();
27077                 this.baseScale = height / this.imageEl.OriginWidth;
27078
27079                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27080                     width = this.thumbEl.getWidth();
27081                     this.baseScale = width / this.imageEl.OriginHeight;
27082                 }
27083
27084                 return;
27085             }
27086
27087             height = this.thumbEl.getHeight();
27088             this.baseScale = height / this.imageEl.OriginHeight;
27089
27090             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27091                 width = this.thumbEl.getWidth();
27092                 this.baseScale = width / this.imageEl.OriginWidth;
27093             }
27094
27095             return;
27096         }
27097         
27098         if(this.baseRotate == 6 || this.baseRotate == 8){
27099             
27100             width = this.thumbEl.getHeight();
27101             this.baseScale = width / this.imageEl.OriginHeight;
27102             
27103             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27104                 height = this.thumbEl.getWidth();
27105                 this.baseScale = height / this.imageEl.OriginHeight;
27106             }
27107             
27108             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27109                 height = this.thumbEl.getWidth();
27110                 this.baseScale = height / this.imageEl.OriginHeight;
27111                 
27112                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27113                     width = this.thumbEl.getHeight();
27114                     this.baseScale = width / this.imageEl.OriginWidth;
27115                 }
27116             }
27117             
27118             return;
27119         }
27120         
27121         width = this.thumbEl.getWidth();
27122         this.baseScale = width / this.imageEl.OriginWidth;
27123         
27124         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27125             height = this.thumbEl.getHeight();
27126             this.baseScale = height / this.imageEl.OriginHeight;
27127         }
27128         
27129         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27130             
27131             height = this.thumbEl.getHeight();
27132             this.baseScale = height / this.imageEl.OriginHeight;
27133             
27134             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27135                 width = this.thumbEl.getWidth();
27136                 this.baseScale = width / this.imageEl.OriginWidth;
27137             }
27138             
27139         }
27140         
27141         return;
27142     },
27143     
27144     getScaleLevel : function()
27145     {
27146         return this.baseScale * Math.pow(1.1, this.scale);
27147     },
27148     
27149     onTouchStart : function(e)
27150     {
27151         if(!this.canvasLoaded){
27152             this.beforeSelectFile(e);
27153             return;
27154         }
27155         
27156         var touches = e.browserEvent.touches;
27157         
27158         if(!touches){
27159             return;
27160         }
27161         
27162         if(touches.length == 1){
27163             this.onMouseDown(e);
27164             return;
27165         }
27166         
27167         if(touches.length != 2){
27168             return;
27169         }
27170         
27171         var coords = [];
27172         
27173         for(var i = 0, finger; finger = touches[i]; i++){
27174             coords.push(finger.pageX, finger.pageY);
27175         }
27176         
27177         var x = Math.pow(coords[0] - coords[2], 2);
27178         var y = Math.pow(coords[1] - coords[3], 2);
27179         
27180         this.startDistance = Math.sqrt(x + y);
27181         
27182         this.startScale = this.scale;
27183         
27184         this.pinching = true;
27185         this.dragable = false;
27186         
27187     },
27188     
27189     onTouchMove : function(e)
27190     {
27191         if(!this.pinching && !this.dragable){
27192             return;
27193         }
27194         
27195         var touches = e.browserEvent.touches;
27196         
27197         if(!touches){
27198             return;
27199         }
27200         
27201         if(this.dragable){
27202             this.onMouseMove(e);
27203             return;
27204         }
27205         
27206         var coords = [];
27207         
27208         for(var i = 0, finger; finger = touches[i]; i++){
27209             coords.push(finger.pageX, finger.pageY);
27210         }
27211         
27212         var x = Math.pow(coords[0] - coords[2], 2);
27213         var y = Math.pow(coords[1] - coords[3], 2);
27214         
27215         this.endDistance = Math.sqrt(x + y);
27216         
27217         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27218         
27219         if(!this.zoomable()){
27220             this.scale = this.startScale;
27221             return;
27222         }
27223         
27224         this.draw();
27225         
27226     },
27227     
27228     onTouchEnd : function(e)
27229     {
27230         this.pinching = false;
27231         this.dragable = false;
27232         
27233     },
27234     
27235     process : function(file, crop)
27236     {
27237         if(this.loadMask){
27238             this.maskEl.mask(this.loadingText);
27239         }
27240         
27241         this.xhr = new XMLHttpRequest();
27242         
27243         file.xhr = this.xhr;
27244
27245         this.xhr.open(this.method, this.url, true);
27246         
27247         var headers = {
27248             "Accept": "application/json",
27249             "Cache-Control": "no-cache",
27250             "X-Requested-With": "XMLHttpRequest"
27251         };
27252         
27253         for (var headerName in headers) {
27254             var headerValue = headers[headerName];
27255             if (headerValue) {
27256                 this.xhr.setRequestHeader(headerName, headerValue);
27257             }
27258         }
27259         
27260         var _this = this;
27261         
27262         this.xhr.onload = function()
27263         {
27264             _this.xhrOnLoad(_this.xhr);
27265         }
27266         
27267         this.xhr.onerror = function()
27268         {
27269             _this.xhrOnError(_this.xhr);
27270         }
27271         
27272         var formData = new FormData();
27273
27274         formData.append('returnHTML', 'NO');
27275         
27276         if(crop){
27277             formData.append('crop', crop);
27278         }
27279         
27280         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27281             formData.append(this.paramName, file, file.name);
27282         }
27283         
27284         if(typeof(file.filename) != 'undefined'){
27285             formData.append('filename', file.filename);
27286         }
27287         
27288         if(typeof(file.mimetype) != 'undefined'){
27289             formData.append('mimetype', file.mimetype);
27290         }
27291         
27292         if(this.fireEvent('arrange', this, formData) != false){
27293             this.xhr.send(formData);
27294         };
27295     },
27296     
27297     xhrOnLoad : function(xhr)
27298     {
27299         if(this.loadMask){
27300             this.maskEl.unmask();
27301         }
27302         
27303         if (xhr.readyState !== 4) {
27304             this.fireEvent('exception', this, xhr);
27305             return;
27306         }
27307
27308         var response = Roo.decode(xhr.responseText);
27309         
27310         if(!response.success){
27311             this.fireEvent('exception', this, xhr);
27312             return;
27313         }
27314         
27315         var response = Roo.decode(xhr.responseText);
27316         
27317         this.fireEvent('upload', this, response);
27318         
27319     },
27320     
27321     xhrOnError : function()
27322     {
27323         if(this.loadMask){
27324             this.maskEl.unmask();
27325         }
27326         
27327         Roo.log('xhr on error');
27328         
27329         var response = Roo.decode(xhr.responseText);
27330           
27331         Roo.log(response);
27332         
27333     },
27334     
27335     prepare : function(file)
27336     {   
27337         if(this.loadMask){
27338             this.maskEl.mask(this.loadingText);
27339         }
27340         
27341         this.file = false;
27342         this.exif = {};
27343         
27344         if(typeof(file) === 'string'){
27345             this.loadCanvas(file);
27346             return;
27347         }
27348         
27349         if(!file || !this.urlAPI){
27350             return;
27351         }
27352         
27353         this.file = file;
27354         this.cropType = file.type;
27355         
27356         var _this = this;
27357         
27358         if(this.fireEvent('prepare', this, this.file) != false){
27359             
27360             var reader = new FileReader();
27361             
27362             reader.onload = function (e) {
27363                 if (e.target.error) {
27364                     Roo.log(e.target.error);
27365                     return;
27366                 }
27367                 
27368                 var buffer = e.target.result,
27369                     dataView = new DataView(buffer),
27370                     offset = 2,
27371                     maxOffset = dataView.byteLength - 4,
27372                     markerBytes,
27373                     markerLength;
27374                 
27375                 if (dataView.getUint16(0) === 0xffd8) {
27376                     while (offset < maxOffset) {
27377                         markerBytes = dataView.getUint16(offset);
27378                         
27379                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27380                             markerLength = dataView.getUint16(offset + 2) + 2;
27381                             if (offset + markerLength > dataView.byteLength) {
27382                                 Roo.log('Invalid meta data: Invalid segment size.');
27383                                 break;
27384                             }
27385                             
27386                             if(markerBytes == 0xffe1){
27387                                 _this.parseExifData(
27388                                     dataView,
27389                                     offset,
27390                                     markerLength
27391                                 );
27392                             }
27393                             
27394                             offset += markerLength;
27395                             
27396                             continue;
27397                         }
27398                         
27399                         break;
27400                     }
27401                     
27402                 }
27403                 
27404                 var url = _this.urlAPI.createObjectURL(_this.file);
27405                 
27406                 _this.loadCanvas(url);
27407                 
27408                 return;
27409             }
27410             
27411             reader.readAsArrayBuffer(this.file);
27412             
27413         }
27414         
27415     },
27416     
27417     parseExifData : function(dataView, offset, length)
27418     {
27419         var tiffOffset = offset + 10,
27420             littleEndian,
27421             dirOffset;
27422     
27423         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27424             // No Exif data, might be XMP data instead
27425             return;
27426         }
27427         
27428         // Check for the ASCII code for "Exif" (0x45786966):
27429         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27430             // No Exif data, might be XMP data instead
27431             return;
27432         }
27433         if (tiffOffset + 8 > dataView.byteLength) {
27434             Roo.log('Invalid Exif data: Invalid segment size.');
27435             return;
27436         }
27437         // Check for the two null bytes:
27438         if (dataView.getUint16(offset + 8) !== 0x0000) {
27439             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27440             return;
27441         }
27442         // Check the byte alignment:
27443         switch (dataView.getUint16(tiffOffset)) {
27444         case 0x4949:
27445             littleEndian = true;
27446             break;
27447         case 0x4D4D:
27448             littleEndian = false;
27449             break;
27450         default:
27451             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27452             return;
27453         }
27454         // Check for the TIFF tag marker (0x002A):
27455         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27456             Roo.log('Invalid Exif data: Missing TIFF marker.');
27457             return;
27458         }
27459         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27460         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27461         
27462         this.parseExifTags(
27463             dataView,
27464             tiffOffset,
27465             tiffOffset + dirOffset,
27466             littleEndian
27467         );
27468     },
27469     
27470     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27471     {
27472         var tagsNumber,
27473             dirEndOffset,
27474             i;
27475         if (dirOffset + 6 > dataView.byteLength) {
27476             Roo.log('Invalid Exif data: Invalid directory offset.');
27477             return;
27478         }
27479         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27480         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27481         if (dirEndOffset + 4 > dataView.byteLength) {
27482             Roo.log('Invalid Exif data: Invalid directory size.');
27483             return;
27484         }
27485         for (i = 0; i < tagsNumber; i += 1) {
27486             this.parseExifTag(
27487                 dataView,
27488                 tiffOffset,
27489                 dirOffset + 2 + 12 * i, // tag offset
27490                 littleEndian
27491             );
27492         }
27493         // Return the offset to the next directory:
27494         return dataView.getUint32(dirEndOffset, littleEndian);
27495     },
27496     
27497     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27498     {
27499         var tag = dataView.getUint16(offset, littleEndian);
27500         
27501         this.exif[tag] = this.getExifValue(
27502             dataView,
27503             tiffOffset,
27504             offset,
27505             dataView.getUint16(offset + 2, littleEndian), // tag type
27506             dataView.getUint32(offset + 4, littleEndian), // tag length
27507             littleEndian
27508         );
27509     },
27510     
27511     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27512     {
27513         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27514             tagSize,
27515             dataOffset,
27516             values,
27517             i,
27518             str,
27519             c;
27520     
27521         if (!tagType) {
27522             Roo.log('Invalid Exif data: Invalid tag type.');
27523             return;
27524         }
27525         
27526         tagSize = tagType.size * length;
27527         // Determine if the value is contained in the dataOffset bytes,
27528         // or if the value at the dataOffset is a pointer to the actual data:
27529         dataOffset = tagSize > 4 ?
27530                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27531         if (dataOffset + tagSize > dataView.byteLength) {
27532             Roo.log('Invalid Exif data: Invalid data offset.');
27533             return;
27534         }
27535         if (length === 1) {
27536             return tagType.getValue(dataView, dataOffset, littleEndian);
27537         }
27538         values = [];
27539         for (i = 0; i < length; i += 1) {
27540             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27541         }
27542         
27543         if (tagType.ascii) {
27544             str = '';
27545             // Concatenate the chars:
27546             for (i = 0; i < values.length; i += 1) {
27547                 c = values[i];
27548                 // Ignore the terminating NULL byte(s):
27549                 if (c === '\u0000') {
27550                     break;
27551                 }
27552                 str += c;
27553             }
27554             return str;
27555         }
27556         return values;
27557     }
27558     
27559 });
27560
27561 Roo.apply(Roo.bootstrap.UploadCropbox, {
27562     tags : {
27563         'Orientation': 0x0112
27564     },
27565     
27566     Orientation: {
27567             1: 0, //'top-left',
27568 //            2: 'top-right',
27569             3: 180, //'bottom-right',
27570 //            4: 'bottom-left',
27571 //            5: 'left-top',
27572             6: 90, //'right-top',
27573 //            7: 'right-bottom',
27574             8: 270 //'left-bottom'
27575     },
27576     
27577     exifTagTypes : {
27578         // byte, 8-bit unsigned int:
27579         1: {
27580             getValue: function (dataView, dataOffset) {
27581                 return dataView.getUint8(dataOffset);
27582             },
27583             size: 1
27584         },
27585         // ascii, 8-bit byte:
27586         2: {
27587             getValue: function (dataView, dataOffset) {
27588                 return String.fromCharCode(dataView.getUint8(dataOffset));
27589             },
27590             size: 1,
27591             ascii: true
27592         },
27593         // short, 16 bit int:
27594         3: {
27595             getValue: function (dataView, dataOffset, littleEndian) {
27596                 return dataView.getUint16(dataOffset, littleEndian);
27597             },
27598             size: 2
27599         },
27600         // long, 32 bit int:
27601         4: {
27602             getValue: function (dataView, dataOffset, littleEndian) {
27603                 return dataView.getUint32(dataOffset, littleEndian);
27604             },
27605             size: 4
27606         },
27607         // rational = two long values, first is numerator, second is denominator:
27608         5: {
27609             getValue: function (dataView, dataOffset, littleEndian) {
27610                 return dataView.getUint32(dataOffset, littleEndian) /
27611                     dataView.getUint32(dataOffset + 4, littleEndian);
27612             },
27613             size: 8
27614         },
27615         // slong, 32 bit signed int:
27616         9: {
27617             getValue: function (dataView, dataOffset, littleEndian) {
27618                 return dataView.getInt32(dataOffset, littleEndian);
27619             },
27620             size: 4
27621         },
27622         // srational, two slongs, first is numerator, second is denominator:
27623         10: {
27624             getValue: function (dataView, dataOffset, littleEndian) {
27625                 return dataView.getInt32(dataOffset, littleEndian) /
27626                     dataView.getInt32(dataOffset + 4, littleEndian);
27627             },
27628             size: 8
27629         }
27630     },
27631     
27632     footer : {
27633         STANDARD : [
27634             {
27635                 tag : 'div',
27636                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27637                 action : 'rotate-left',
27638                 cn : [
27639                     {
27640                         tag : 'button',
27641                         cls : 'btn btn-default',
27642                         html : '<i class="fa fa-undo"></i>'
27643                     }
27644                 ]
27645             },
27646             {
27647                 tag : 'div',
27648                 cls : 'btn-group roo-upload-cropbox-picture',
27649                 action : 'picture',
27650                 cn : [
27651                     {
27652                         tag : 'button',
27653                         cls : 'btn btn-default',
27654                         html : '<i class="fa fa-picture-o"></i>'
27655                     }
27656                 ]
27657             },
27658             {
27659                 tag : 'div',
27660                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27661                 action : 'rotate-right',
27662                 cn : [
27663                     {
27664                         tag : 'button',
27665                         cls : 'btn btn-default',
27666                         html : '<i class="fa fa-repeat"></i>'
27667                     }
27668                 ]
27669             }
27670         ],
27671         DOCUMENT : [
27672             {
27673                 tag : 'div',
27674                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27675                 action : 'rotate-left',
27676                 cn : [
27677                     {
27678                         tag : 'button',
27679                         cls : 'btn btn-default',
27680                         html : '<i class="fa fa-undo"></i>'
27681                     }
27682                 ]
27683             },
27684             {
27685                 tag : 'div',
27686                 cls : 'btn-group roo-upload-cropbox-download',
27687                 action : 'download',
27688                 cn : [
27689                     {
27690                         tag : 'button',
27691                         cls : 'btn btn-default',
27692                         html : '<i class="fa fa-download"></i>'
27693                     }
27694                 ]
27695             },
27696             {
27697                 tag : 'div',
27698                 cls : 'btn-group roo-upload-cropbox-crop',
27699                 action : 'crop',
27700                 cn : [
27701                     {
27702                         tag : 'button',
27703                         cls : 'btn btn-default',
27704                         html : '<i class="fa fa-crop"></i>'
27705                     }
27706                 ]
27707             },
27708             {
27709                 tag : 'div',
27710                 cls : 'btn-group roo-upload-cropbox-trash',
27711                 action : 'trash',
27712                 cn : [
27713                     {
27714                         tag : 'button',
27715                         cls : 'btn btn-default',
27716                         html : '<i class="fa fa-trash"></i>'
27717                     }
27718                 ]
27719             },
27720             {
27721                 tag : 'div',
27722                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27723                 action : 'rotate-right',
27724                 cn : [
27725                     {
27726                         tag : 'button',
27727                         cls : 'btn btn-default',
27728                         html : '<i class="fa fa-repeat"></i>'
27729                     }
27730                 ]
27731             }
27732         ],
27733         ROTATOR : [
27734             {
27735                 tag : 'div',
27736                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27737                 action : 'rotate-left',
27738                 cn : [
27739                     {
27740                         tag : 'button',
27741                         cls : 'btn btn-default',
27742                         html : '<i class="fa fa-undo"></i>'
27743                     }
27744                 ]
27745             },
27746             {
27747                 tag : 'div',
27748                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27749                 action : 'rotate-right',
27750                 cn : [
27751                     {
27752                         tag : 'button',
27753                         cls : 'btn btn-default',
27754                         html : '<i class="fa fa-repeat"></i>'
27755                     }
27756                 ]
27757             }
27758         ]
27759     }
27760 });
27761
27762 /*
27763 * Licence: LGPL
27764 */
27765
27766 /**
27767  * @class Roo.bootstrap.DocumentManager
27768  * @extends Roo.bootstrap.Component
27769  * Bootstrap DocumentManager class
27770  * @cfg {String} paramName default 'imageUpload'
27771  * @cfg {String} toolTipName default 'filename'
27772  * @cfg {String} method default POST
27773  * @cfg {String} url action url
27774  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27775  * @cfg {Boolean} multiple multiple upload default true
27776  * @cfg {Number} thumbSize default 300
27777  * @cfg {String} fieldLabel
27778  * @cfg {Number} labelWidth default 4
27779  * @cfg {String} labelAlign (left|top) default left
27780  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27781 * @cfg {Number} labellg set the width of label (1-12)
27782  * @cfg {Number} labelmd set the width of label (1-12)
27783  * @cfg {Number} labelsm set the width of label (1-12)
27784  * @cfg {Number} labelxs set the width of label (1-12)
27785  * 
27786  * @constructor
27787  * Create a new DocumentManager
27788  * @param {Object} config The config object
27789  */
27790
27791 Roo.bootstrap.DocumentManager = function(config){
27792     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27793     
27794     this.files = [];
27795     this.delegates = [];
27796     
27797     this.addEvents({
27798         /**
27799          * @event initial
27800          * Fire when initial the DocumentManager
27801          * @param {Roo.bootstrap.DocumentManager} this
27802          */
27803         "initial" : true,
27804         /**
27805          * @event inspect
27806          * inspect selected file
27807          * @param {Roo.bootstrap.DocumentManager} this
27808          * @param {File} file
27809          */
27810         "inspect" : true,
27811         /**
27812          * @event exception
27813          * Fire when xhr load exception
27814          * @param {Roo.bootstrap.DocumentManager} this
27815          * @param {XMLHttpRequest} xhr
27816          */
27817         "exception" : true,
27818         /**
27819          * @event afterupload
27820          * Fire when xhr load exception
27821          * @param {Roo.bootstrap.DocumentManager} this
27822          * @param {XMLHttpRequest} xhr
27823          */
27824         "afterupload" : true,
27825         /**
27826          * @event prepare
27827          * prepare the form data
27828          * @param {Roo.bootstrap.DocumentManager} this
27829          * @param {Object} formData
27830          */
27831         "prepare" : true,
27832         /**
27833          * @event remove
27834          * Fire when remove the file
27835          * @param {Roo.bootstrap.DocumentManager} this
27836          * @param {Object} file
27837          */
27838         "remove" : true,
27839         /**
27840          * @event refresh
27841          * Fire after refresh the file
27842          * @param {Roo.bootstrap.DocumentManager} this
27843          */
27844         "refresh" : true,
27845         /**
27846          * @event click
27847          * Fire after click the image
27848          * @param {Roo.bootstrap.DocumentManager} this
27849          * @param {Object} file
27850          */
27851         "click" : true,
27852         /**
27853          * @event edit
27854          * Fire when upload a image and editable set to true
27855          * @param {Roo.bootstrap.DocumentManager} this
27856          * @param {Object} file
27857          */
27858         "edit" : true,
27859         /**
27860          * @event beforeselectfile
27861          * Fire before select file
27862          * @param {Roo.bootstrap.DocumentManager} this
27863          */
27864         "beforeselectfile" : true,
27865         /**
27866          * @event process
27867          * Fire before process file
27868          * @param {Roo.bootstrap.DocumentManager} this
27869          * @param {Object} file
27870          */
27871         "process" : true
27872         
27873     });
27874 };
27875
27876 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27877     
27878     boxes : 0,
27879     inputName : '',
27880     thumbSize : 300,
27881     multiple : true,
27882     files : false,
27883     method : 'POST',
27884     url : '',
27885     paramName : 'imageUpload',
27886     toolTipName : 'filename',
27887     fieldLabel : '',
27888     labelWidth : 4,
27889     labelAlign : 'left',
27890     editable : true,
27891     delegates : false,
27892     xhr : false, 
27893     
27894     labellg : 0,
27895     labelmd : 0,
27896     labelsm : 0,
27897     labelxs : 0,
27898     
27899     getAutoCreate : function()
27900     {   
27901         var managerWidget = {
27902             tag : 'div',
27903             cls : 'roo-document-manager',
27904             cn : [
27905                 {
27906                     tag : 'input',
27907                     cls : 'roo-document-manager-selector',
27908                     type : 'file'
27909                 },
27910                 {
27911                     tag : 'div',
27912                     cls : 'roo-document-manager-uploader',
27913                     cn : [
27914                         {
27915                             tag : 'div',
27916                             cls : 'roo-document-manager-upload-btn',
27917                             html : '<i class="fa fa-plus"></i>'
27918                         }
27919                     ]
27920                     
27921                 }
27922             ]
27923         };
27924         
27925         var content = [
27926             {
27927                 tag : 'div',
27928                 cls : 'column col-md-12',
27929                 cn : managerWidget
27930             }
27931         ];
27932         
27933         if(this.fieldLabel.length){
27934             
27935             content = [
27936                 {
27937                     tag : 'div',
27938                     cls : 'column col-md-12',
27939                     html : this.fieldLabel
27940                 },
27941                 {
27942                     tag : 'div',
27943                     cls : 'column col-md-12',
27944                     cn : managerWidget
27945                 }
27946             ];
27947
27948             if(this.labelAlign == 'left'){
27949                 content = [
27950                     {
27951                         tag : 'div',
27952                         cls : 'column',
27953                         html : this.fieldLabel
27954                     },
27955                     {
27956                         tag : 'div',
27957                         cls : 'column',
27958                         cn : managerWidget
27959                     }
27960                 ];
27961                 
27962                 if(this.labelWidth > 12){
27963                     content[0].style = "width: " + this.labelWidth + 'px';
27964                 }
27965
27966                 if(this.labelWidth < 13 && this.labelmd == 0){
27967                     this.labelmd = this.labelWidth;
27968                 }
27969
27970                 if(this.labellg > 0){
27971                     content[0].cls += ' col-lg-' + this.labellg;
27972                     content[1].cls += ' col-lg-' + (12 - this.labellg);
27973                 }
27974
27975                 if(this.labelmd > 0){
27976                     content[0].cls += ' col-md-' + this.labelmd;
27977                     content[1].cls += ' col-md-' + (12 - this.labelmd);
27978                 }
27979
27980                 if(this.labelsm > 0){
27981                     content[0].cls += ' col-sm-' + this.labelsm;
27982                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
27983                 }
27984
27985                 if(this.labelxs > 0){
27986                     content[0].cls += ' col-xs-' + this.labelxs;
27987                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
27988                 }
27989                 
27990             }
27991         }
27992         
27993         var cfg = {
27994             tag : 'div',
27995             cls : 'row clearfix',
27996             cn : content
27997         };
27998         
27999         return cfg;
28000         
28001     },
28002     
28003     initEvents : function()
28004     {
28005         this.managerEl = this.el.select('.roo-document-manager', true).first();
28006         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28007         
28008         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28009         this.selectorEl.hide();
28010         
28011         if(this.multiple){
28012             this.selectorEl.attr('multiple', 'multiple');
28013         }
28014         
28015         this.selectorEl.on('change', this.onFileSelected, this);
28016         
28017         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28018         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28019         
28020         this.uploader.on('click', this.onUploaderClick, this);
28021         
28022         this.renderProgressDialog();
28023         
28024         var _this = this;
28025         
28026         window.addEventListener("resize", function() { _this.refresh(); } );
28027         
28028         this.fireEvent('initial', this);
28029     },
28030     
28031     renderProgressDialog : function()
28032     {
28033         var _this = this;
28034         
28035         this.progressDialog = new Roo.bootstrap.Modal({
28036             cls : 'roo-document-manager-progress-dialog',
28037             allow_close : false,
28038             title : '',
28039             buttons : [
28040                 {
28041                     name  :'cancel',
28042                     weight : 'danger',
28043                     html : 'Cancel'
28044                 }
28045             ], 
28046             listeners : { 
28047                 btnclick : function() {
28048                     _this.uploadCancel();
28049                     this.hide();
28050                 }
28051             }
28052         });
28053          
28054         this.progressDialog.render(Roo.get(document.body));
28055          
28056         this.progress = new Roo.bootstrap.Progress({
28057             cls : 'roo-document-manager-progress',
28058             active : true,
28059             striped : true
28060         });
28061         
28062         this.progress.render(this.progressDialog.getChildContainer());
28063         
28064         this.progressBar = new Roo.bootstrap.ProgressBar({
28065             cls : 'roo-document-manager-progress-bar',
28066             aria_valuenow : 0,
28067             aria_valuemin : 0,
28068             aria_valuemax : 12,
28069             panel : 'success'
28070         });
28071         
28072         this.progressBar.render(this.progress.getChildContainer());
28073     },
28074     
28075     onUploaderClick : function(e)
28076     {
28077         e.preventDefault();
28078      
28079         if(this.fireEvent('beforeselectfile', this) != false){
28080             this.selectorEl.dom.click();
28081         }
28082         
28083     },
28084     
28085     onFileSelected : function(e)
28086     {
28087         e.preventDefault();
28088         
28089         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28090             return;
28091         }
28092         
28093         Roo.each(this.selectorEl.dom.files, function(file){
28094             if(this.fireEvent('inspect', this, file) != false){
28095                 this.files.push(file);
28096             }
28097         }, this);
28098         
28099         this.queue();
28100         
28101     },
28102     
28103     queue : function()
28104     {
28105         this.selectorEl.dom.value = '';
28106         
28107         if(!this.files.length){
28108             return;
28109         }
28110         
28111         if(this.boxes > 0 && this.files.length > this.boxes){
28112             this.files = this.files.slice(0, this.boxes);
28113         }
28114         
28115         this.uploader.show();
28116         
28117         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28118             this.uploader.hide();
28119         }
28120         
28121         var _this = this;
28122         
28123         var files = [];
28124         
28125         var docs = [];
28126         
28127         Roo.each(this.files, function(file){
28128             
28129             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28130                 var f = this.renderPreview(file);
28131                 files.push(f);
28132                 return;
28133             }
28134             
28135             if(file.type.indexOf('image') != -1){
28136                 this.delegates.push(
28137                     (function(){
28138                         _this.process(file);
28139                     }).createDelegate(this)
28140                 );
28141         
28142                 return;
28143             }
28144             
28145             docs.push(
28146                 (function(){
28147                     _this.process(file);
28148                 }).createDelegate(this)
28149             );
28150             
28151         }, this);
28152         
28153         this.files = files;
28154         
28155         this.delegates = this.delegates.concat(docs);
28156         
28157         if(!this.delegates.length){
28158             this.refresh();
28159             return;
28160         }
28161         
28162         this.progressBar.aria_valuemax = this.delegates.length;
28163         
28164         this.arrange();
28165         
28166         return;
28167     },
28168     
28169     arrange : function()
28170     {
28171         if(!this.delegates.length){
28172             this.progressDialog.hide();
28173             this.refresh();
28174             return;
28175         }
28176         
28177         var delegate = this.delegates.shift();
28178         
28179         this.progressDialog.show();
28180         
28181         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28182         
28183         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28184         
28185         delegate();
28186     },
28187     
28188     refresh : function()
28189     {
28190         this.uploader.show();
28191         
28192         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28193             this.uploader.hide();
28194         }
28195         
28196         Roo.isTouch ? this.closable(false) : this.closable(true);
28197         
28198         this.fireEvent('refresh', this);
28199     },
28200     
28201     onRemove : function(e, el, o)
28202     {
28203         e.preventDefault();
28204         
28205         this.fireEvent('remove', this, o);
28206         
28207     },
28208     
28209     remove : function(o)
28210     {
28211         var files = [];
28212         
28213         Roo.each(this.files, function(file){
28214             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28215                 files.push(file);
28216                 return;
28217             }
28218
28219             o.target.remove();
28220
28221         }, this);
28222         
28223         this.files = files;
28224         
28225         this.refresh();
28226     },
28227     
28228     clear : function()
28229     {
28230         Roo.each(this.files, function(file){
28231             if(!file.target){
28232                 return;
28233             }
28234             
28235             file.target.remove();
28236
28237         }, this);
28238         
28239         this.files = [];
28240         
28241         this.refresh();
28242     },
28243     
28244     onClick : function(e, el, o)
28245     {
28246         e.preventDefault();
28247         
28248         this.fireEvent('click', this, o);
28249         
28250     },
28251     
28252     closable : function(closable)
28253     {
28254         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28255             
28256             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28257             
28258             if(closable){
28259                 el.show();
28260                 return;
28261             }
28262             
28263             el.hide();
28264             
28265         }, this);
28266     },
28267     
28268     xhrOnLoad : function(xhr)
28269     {
28270         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28271             el.remove();
28272         }, this);
28273         
28274         if (xhr.readyState !== 4) {
28275             this.arrange();
28276             this.fireEvent('exception', this, xhr);
28277             return;
28278         }
28279
28280         var response = Roo.decode(xhr.responseText);
28281         
28282         if(!response.success){
28283             this.arrange();
28284             this.fireEvent('exception', this, xhr);
28285             return;
28286         }
28287         
28288         var file = this.renderPreview(response.data);
28289         
28290         this.files.push(file);
28291         
28292         this.arrange();
28293         
28294         this.fireEvent('afterupload', this, xhr);
28295         
28296     },
28297     
28298     xhrOnError : function(xhr)
28299     {
28300         Roo.log('xhr on error');
28301         
28302         var response = Roo.decode(xhr.responseText);
28303           
28304         Roo.log(response);
28305         
28306         this.arrange();
28307     },
28308     
28309     process : function(file)
28310     {
28311         if(this.fireEvent('process', this, file) !== false){
28312             if(this.editable && file.type.indexOf('image') != -1){
28313                 this.fireEvent('edit', this, file);
28314                 return;
28315             }
28316
28317             this.uploadStart(file, false);
28318
28319             return;
28320         }
28321         
28322     },
28323     
28324     uploadStart : function(file, crop)
28325     {
28326         this.xhr = new XMLHttpRequest();
28327         
28328         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28329             this.arrange();
28330             return;
28331         }
28332         
28333         file.xhr = this.xhr;
28334             
28335         this.managerEl.createChild({
28336             tag : 'div',
28337             cls : 'roo-document-manager-loading',
28338             cn : [
28339                 {
28340                     tag : 'div',
28341                     tooltip : file.name,
28342                     cls : 'roo-document-manager-thumb',
28343                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28344                 }
28345             ]
28346
28347         });
28348
28349         this.xhr.open(this.method, this.url, true);
28350         
28351         var headers = {
28352             "Accept": "application/json",
28353             "Cache-Control": "no-cache",
28354             "X-Requested-With": "XMLHttpRequest"
28355         };
28356         
28357         for (var headerName in headers) {
28358             var headerValue = headers[headerName];
28359             if (headerValue) {
28360                 this.xhr.setRequestHeader(headerName, headerValue);
28361             }
28362         }
28363         
28364         var _this = this;
28365         
28366         this.xhr.onload = function()
28367         {
28368             _this.xhrOnLoad(_this.xhr);
28369         }
28370         
28371         this.xhr.onerror = function()
28372         {
28373             _this.xhrOnError(_this.xhr);
28374         }
28375         
28376         var formData = new FormData();
28377
28378         formData.append('returnHTML', 'NO');
28379         
28380         if(crop){
28381             formData.append('crop', crop);
28382         }
28383         
28384         formData.append(this.paramName, file, file.name);
28385         
28386         var options = {
28387             file : file, 
28388             manually : false
28389         };
28390         
28391         if(this.fireEvent('prepare', this, formData, options) != false){
28392             
28393             if(options.manually){
28394                 return;
28395             }
28396             
28397             this.xhr.send(formData);
28398             return;
28399         };
28400         
28401         this.uploadCancel();
28402     },
28403     
28404     uploadCancel : function()
28405     {
28406         if (this.xhr) {
28407             this.xhr.abort();
28408         }
28409         
28410         this.delegates = [];
28411         
28412         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28413             el.remove();
28414         }, this);
28415         
28416         this.arrange();
28417     },
28418     
28419     renderPreview : function(file)
28420     {
28421         if(typeof(file.target) != 'undefined' && file.target){
28422             return file;
28423         }
28424         
28425         var previewEl = this.managerEl.createChild({
28426             tag : 'div',
28427             cls : 'roo-document-manager-preview',
28428             cn : [
28429                 {
28430                     tag : 'div',
28431                     tooltip : file[this.toolTipName],
28432                     cls : 'roo-document-manager-thumb',
28433                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28434                 },
28435                 {
28436                     tag : 'button',
28437                     cls : 'close',
28438                     html : '<i class="fa fa-times-circle"></i>'
28439                 }
28440             ]
28441         });
28442
28443         var close = previewEl.select('button.close', true).first();
28444
28445         close.on('click', this.onRemove, this, file);
28446
28447         file.target = previewEl;
28448
28449         var image = previewEl.select('img', true).first();
28450         
28451         var _this = this;
28452         
28453         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28454         
28455         image.on('click', this.onClick, this, file);
28456         
28457         return file;
28458         
28459     },
28460     
28461     onPreviewLoad : function(file, image)
28462     {
28463         if(typeof(file.target) == 'undefined' || !file.target){
28464             return;
28465         }
28466         
28467         var width = image.dom.naturalWidth || image.dom.width;
28468         var height = image.dom.naturalHeight || image.dom.height;
28469         
28470         if(width > height){
28471             file.target.addClass('wide');
28472             return;
28473         }
28474         
28475         file.target.addClass('tall');
28476         return;
28477         
28478     },
28479     
28480     uploadFromSource : function(file, crop)
28481     {
28482         this.xhr = new XMLHttpRequest();
28483         
28484         this.managerEl.createChild({
28485             tag : 'div',
28486             cls : 'roo-document-manager-loading',
28487             cn : [
28488                 {
28489                     tag : 'div',
28490                     tooltip : file.name,
28491                     cls : 'roo-document-manager-thumb',
28492                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28493                 }
28494             ]
28495
28496         });
28497
28498         this.xhr.open(this.method, this.url, true);
28499         
28500         var headers = {
28501             "Accept": "application/json",
28502             "Cache-Control": "no-cache",
28503             "X-Requested-With": "XMLHttpRequest"
28504         };
28505         
28506         for (var headerName in headers) {
28507             var headerValue = headers[headerName];
28508             if (headerValue) {
28509                 this.xhr.setRequestHeader(headerName, headerValue);
28510             }
28511         }
28512         
28513         var _this = this;
28514         
28515         this.xhr.onload = function()
28516         {
28517             _this.xhrOnLoad(_this.xhr);
28518         }
28519         
28520         this.xhr.onerror = function()
28521         {
28522             _this.xhrOnError(_this.xhr);
28523         }
28524         
28525         var formData = new FormData();
28526
28527         formData.append('returnHTML', 'NO');
28528         
28529         formData.append('crop', crop);
28530         
28531         if(typeof(file.filename) != 'undefined'){
28532             formData.append('filename', file.filename);
28533         }
28534         
28535         if(typeof(file.mimetype) != 'undefined'){
28536             formData.append('mimetype', file.mimetype);
28537         }
28538         
28539         Roo.log(formData);
28540         
28541         if(this.fireEvent('prepare', this, formData) != false){
28542             this.xhr.send(formData);
28543         };
28544     }
28545 });
28546
28547 /*
28548 * Licence: LGPL
28549 */
28550
28551 /**
28552  * @class Roo.bootstrap.DocumentViewer
28553  * @extends Roo.bootstrap.Component
28554  * Bootstrap DocumentViewer class
28555  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28556  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28557  * 
28558  * @constructor
28559  * Create a new DocumentViewer
28560  * @param {Object} config The config object
28561  */
28562
28563 Roo.bootstrap.DocumentViewer = function(config){
28564     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28565     
28566     this.addEvents({
28567         /**
28568          * @event initial
28569          * Fire after initEvent
28570          * @param {Roo.bootstrap.DocumentViewer} this
28571          */
28572         "initial" : true,
28573         /**
28574          * @event click
28575          * Fire after click
28576          * @param {Roo.bootstrap.DocumentViewer} this
28577          */
28578         "click" : true,
28579         /**
28580          * @event download
28581          * Fire after download button
28582          * @param {Roo.bootstrap.DocumentViewer} this
28583          */
28584         "download" : true,
28585         /**
28586          * @event trash
28587          * Fire after trash button
28588          * @param {Roo.bootstrap.DocumentViewer} this
28589          */
28590         "trash" : true
28591         
28592     });
28593 };
28594
28595 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28596     
28597     showDownload : true,
28598     
28599     showTrash : true,
28600     
28601     getAutoCreate : function()
28602     {
28603         var cfg = {
28604             tag : 'div',
28605             cls : 'roo-document-viewer',
28606             cn : [
28607                 {
28608                     tag : 'div',
28609                     cls : 'roo-document-viewer-body',
28610                     cn : [
28611                         {
28612                             tag : 'div',
28613                             cls : 'roo-document-viewer-thumb',
28614                             cn : [
28615                                 {
28616                                     tag : 'img',
28617                                     cls : 'roo-document-viewer-image'
28618                                 }
28619                             ]
28620                         }
28621                     ]
28622                 },
28623                 {
28624                     tag : 'div',
28625                     cls : 'roo-document-viewer-footer',
28626                     cn : {
28627                         tag : 'div',
28628                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28629                         cn : [
28630                             {
28631                                 tag : 'div',
28632                                 cls : 'btn-group roo-document-viewer-download',
28633                                 cn : [
28634                                     {
28635                                         tag : 'button',
28636                                         cls : 'btn btn-default',
28637                                         html : '<i class="fa fa-download"></i>'
28638                                     }
28639                                 ]
28640                             },
28641                             {
28642                                 tag : 'div',
28643                                 cls : 'btn-group roo-document-viewer-trash',
28644                                 cn : [
28645                                     {
28646                                         tag : 'button',
28647                                         cls : 'btn btn-default',
28648                                         html : '<i class="fa fa-trash"></i>'
28649                                     }
28650                                 ]
28651                             }
28652                         ]
28653                     }
28654                 }
28655             ]
28656         };
28657         
28658         return cfg;
28659     },
28660     
28661     initEvents : function()
28662     {
28663         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28664         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28665         
28666         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28667         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28668         
28669         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28670         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28671         
28672         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28673         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28674         
28675         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28676         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28677         
28678         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28679         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28680         
28681         this.bodyEl.on('click', this.onClick, this);
28682         this.downloadBtn.on('click', this.onDownload, this);
28683         this.trashBtn.on('click', this.onTrash, this);
28684         
28685         this.downloadBtn.hide();
28686         this.trashBtn.hide();
28687         
28688         if(this.showDownload){
28689             this.downloadBtn.show();
28690         }
28691         
28692         if(this.showTrash){
28693             this.trashBtn.show();
28694         }
28695         
28696         if(!this.showDownload && !this.showTrash) {
28697             this.footerEl.hide();
28698         }
28699         
28700     },
28701     
28702     initial : function()
28703     {
28704         this.fireEvent('initial', this);
28705         
28706     },
28707     
28708     onClick : function(e)
28709     {
28710         e.preventDefault();
28711         
28712         this.fireEvent('click', this);
28713     },
28714     
28715     onDownload : function(e)
28716     {
28717         e.preventDefault();
28718         
28719         this.fireEvent('download', this);
28720     },
28721     
28722     onTrash : function(e)
28723     {
28724         e.preventDefault();
28725         
28726         this.fireEvent('trash', this);
28727     }
28728     
28729 });
28730 /*
28731  * - LGPL
28732  *
28733  * nav progress bar
28734  * 
28735  */
28736
28737 /**
28738  * @class Roo.bootstrap.NavProgressBar
28739  * @extends Roo.bootstrap.Component
28740  * Bootstrap NavProgressBar class
28741  * 
28742  * @constructor
28743  * Create a new nav progress bar
28744  * @param {Object} config The config object
28745  */
28746
28747 Roo.bootstrap.NavProgressBar = function(config){
28748     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28749
28750     this.bullets = this.bullets || [];
28751    
28752 //    Roo.bootstrap.NavProgressBar.register(this);
28753      this.addEvents({
28754         /**
28755              * @event changed
28756              * Fires when the active item changes
28757              * @param {Roo.bootstrap.NavProgressBar} this
28758              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28759              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28760          */
28761         'changed': true
28762      });
28763     
28764 };
28765
28766 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28767     
28768     bullets : [],
28769     barItems : [],
28770     
28771     getAutoCreate : function()
28772     {
28773         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28774         
28775         cfg = {
28776             tag : 'div',
28777             cls : 'roo-navigation-bar-group',
28778             cn : [
28779                 {
28780                     tag : 'div',
28781                     cls : 'roo-navigation-top-bar'
28782                 },
28783                 {
28784                     tag : 'div',
28785                     cls : 'roo-navigation-bullets-bar',
28786                     cn : [
28787                         {
28788                             tag : 'ul',
28789                             cls : 'roo-navigation-bar'
28790                         }
28791                     ]
28792                 },
28793                 
28794                 {
28795                     tag : 'div',
28796                     cls : 'roo-navigation-bottom-bar'
28797                 }
28798             ]
28799             
28800         };
28801         
28802         return cfg;
28803         
28804     },
28805     
28806     initEvents: function() 
28807     {
28808         
28809     },
28810     
28811     onRender : function(ct, position) 
28812     {
28813         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28814         
28815         if(this.bullets.length){
28816             Roo.each(this.bullets, function(b){
28817                this.addItem(b);
28818             }, this);
28819         }
28820         
28821         this.format();
28822         
28823     },
28824     
28825     addItem : function(cfg)
28826     {
28827         var item = new Roo.bootstrap.NavProgressItem(cfg);
28828         
28829         item.parentId = this.id;
28830         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28831         
28832         if(cfg.html){
28833             var top = new Roo.bootstrap.Element({
28834                 tag : 'div',
28835                 cls : 'roo-navigation-bar-text'
28836             });
28837             
28838             var bottom = new Roo.bootstrap.Element({
28839                 tag : 'div',
28840                 cls : 'roo-navigation-bar-text'
28841             });
28842             
28843             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28844             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28845             
28846             var topText = new Roo.bootstrap.Element({
28847                 tag : 'span',
28848                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28849             });
28850             
28851             var bottomText = new Roo.bootstrap.Element({
28852                 tag : 'span',
28853                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28854             });
28855             
28856             topText.onRender(top.el, null);
28857             bottomText.onRender(bottom.el, null);
28858             
28859             item.topEl = top;
28860             item.bottomEl = bottom;
28861         }
28862         
28863         this.barItems.push(item);
28864         
28865         return item;
28866     },
28867     
28868     getActive : function()
28869     {
28870         var active = false;
28871         
28872         Roo.each(this.barItems, function(v){
28873             
28874             if (!v.isActive()) {
28875                 return;
28876             }
28877             
28878             active = v;
28879             return false;
28880             
28881         });
28882         
28883         return active;
28884     },
28885     
28886     setActiveItem : function(item)
28887     {
28888         var prev = false;
28889         
28890         Roo.each(this.barItems, function(v){
28891             if (v.rid == item.rid) {
28892                 return ;
28893             }
28894             
28895             if (v.isActive()) {
28896                 v.setActive(false);
28897                 prev = v;
28898             }
28899         });
28900
28901         item.setActive(true);
28902         
28903         this.fireEvent('changed', this, item, prev);
28904     },
28905     
28906     getBarItem: function(rid)
28907     {
28908         var ret = false;
28909         
28910         Roo.each(this.barItems, function(e) {
28911             if (e.rid != rid) {
28912                 return;
28913             }
28914             
28915             ret =  e;
28916             return false;
28917         });
28918         
28919         return ret;
28920     },
28921     
28922     indexOfItem : function(item)
28923     {
28924         var index = false;
28925         
28926         Roo.each(this.barItems, function(v, i){
28927             
28928             if (v.rid != item.rid) {
28929                 return;
28930             }
28931             
28932             index = i;
28933             return false
28934         });
28935         
28936         return index;
28937     },
28938     
28939     setActiveNext : function()
28940     {
28941         var i = this.indexOfItem(this.getActive());
28942         
28943         if (i > this.barItems.length) {
28944             return;
28945         }
28946         
28947         this.setActiveItem(this.barItems[i+1]);
28948     },
28949     
28950     setActivePrev : function()
28951     {
28952         var i = this.indexOfItem(this.getActive());
28953         
28954         if (i  < 1) {
28955             return;
28956         }
28957         
28958         this.setActiveItem(this.barItems[i-1]);
28959     },
28960     
28961     format : function()
28962     {
28963         if(!this.barItems.length){
28964             return;
28965         }
28966      
28967         var width = 100 / this.barItems.length;
28968         
28969         Roo.each(this.barItems, function(i){
28970             i.el.setStyle('width', width + '%');
28971             i.topEl.el.setStyle('width', width + '%');
28972             i.bottomEl.el.setStyle('width', width + '%');
28973         }, this);
28974         
28975     }
28976     
28977 });
28978 /*
28979  * - LGPL
28980  *
28981  * Nav Progress Item
28982  * 
28983  */
28984
28985 /**
28986  * @class Roo.bootstrap.NavProgressItem
28987  * @extends Roo.bootstrap.Component
28988  * Bootstrap NavProgressItem class
28989  * @cfg {String} rid the reference id
28990  * @cfg {Boolean} active (true|false) Is item active default false
28991  * @cfg {Boolean} disabled (true|false) Is item active default false
28992  * @cfg {String} html
28993  * @cfg {String} position (top|bottom) text position default bottom
28994  * @cfg {String} icon show icon instead of number
28995  * 
28996  * @constructor
28997  * Create a new NavProgressItem
28998  * @param {Object} config The config object
28999  */
29000 Roo.bootstrap.NavProgressItem = function(config){
29001     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29002     this.addEvents({
29003         // raw events
29004         /**
29005          * @event click
29006          * The raw click event for the entire grid.
29007          * @param {Roo.bootstrap.NavProgressItem} this
29008          * @param {Roo.EventObject} e
29009          */
29010         "click" : true
29011     });
29012    
29013 };
29014
29015 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29016     
29017     rid : '',
29018     active : false,
29019     disabled : false,
29020     html : '',
29021     position : 'bottom',
29022     icon : false,
29023     
29024     getAutoCreate : function()
29025     {
29026         var iconCls = 'roo-navigation-bar-item-icon';
29027         
29028         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29029         
29030         var cfg = {
29031             tag: 'li',
29032             cls: 'roo-navigation-bar-item',
29033             cn : [
29034                 {
29035                     tag : 'i',
29036                     cls : iconCls
29037                 }
29038             ]
29039         };
29040         
29041         if(this.active){
29042             cfg.cls += ' active';
29043         }
29044         if(this.disabled){
29045             cfg.cls += ' disabled';
29046         }
29047         
29048         return cfg;
29049     },
29050     
29051     disable : function()
29052     {
29053         this.setDisabled(true);
29054     },
29055     
29056     enable : function()
29057     {
29058         this.setDisabled(false);
29059     },
29060     
29061     initEvents: function() 
29062     {
29063         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29064         
29065         this.iconEl.on('click', this.onClick, this);
29066     },
29067     
29068     onClick : function(e)
29069     {
29070         e.preventDefault();
29071         
29072         if(this.disabled){
29073             return;
29074         }
29075         
29076         if(this.fireEvent('click', this, e) === false){
29077             return;
29078         };
29079         
29080         this.parent().setActiveItem(this);
29081     },
29082     
29083     isActive: function () 
29084     {
29085         return this.active;
29086     },
29087     
29088     setActive : function(state)
29089     {
29090         if(this.active == state){
29091             return;
29092         }
29093         
29094         this.active = state;
29095         
29096         if (state) {
29097             this.el.addClass('active');
29098             return;
29099         }
29100         
29101         this.el.removeClass('active');
29102         
29103         return;
29104     },
29105     
29106     setDisabled : function(state)
29107     {
29108         if(this.disabled == state){
29109             return;
29110         }
29111         
29112         this.disabled = state;
29113         
29114         if (state) {
29115             this.el.addClass('disabled');
29116             return;
29117         }
29118         
29119         this.el.removeClass('disabled');
29120     },
29121     
29122     tooltipEl : function()
29123     {
29124         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29125     }
29126 });
29127  
29128
29129  /*
29130  * - LGPL
29131  *
29132  * FieldLabel
29133  * 
29134  */
29135
29136 /**
29137  * @class Roo.bootstrap.FieldLabel
29138  * @extends Roo.bootstrap.Component
29139  * Bootstrap FieldLabel class
29140  * @cfg {String} html contents of the element
29141  * @cfg {String} tag tag of the element default label
29142  * @cfg {String} cls class of the element
29143  * @cfg {String} target label target 
29144  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29145  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29146  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29147  * @cfg {String} iconTooltip default "This field is required"
29148  * 
29149  * @constructor
29150  * Create a new FieldLabel
29151  * @param {Object} config The config object
29152  */
29153
29154 Roo.bootstrap.FieldLabel = function(config){
29155     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29156     
29157     this.addEvents({
29158             /**
29159              * @event invalid
29160              * Fires after the field has been marked as invalid.
29161              * @param {Roo.form.FieldLabel} this
29162              * @param {String} msg The validation message
29163              */
29164             invalid : true,
29165             /**
29166              * @event valid
29167              * Fires after the field has been validated with no errors.
29168              * @param {Roo.form.FieldLabel} this
29169              */
29170             valid : true
29171         });
29172 };
29173
29174 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29175     
29176     tag: 'label',
29177     cls: '',
29178     html: '',
29179     target: '',
29180     allowBlank : true,
29181     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29182     validClass : 'text-success fa fa-lg fa-check',
29183     iconTooltip : 'This field is required',
29184     
29185     getAutoCreate : function(){
29186         
29187         var cfg = {
29188             tag : this.tag,
29189             cls : 'roo-bootstrap-field-label ' + this.cls,
29190             for : this.target,
29191             cn : [
29192                 {
29193                     tag : 'i',
29194                     cls : '',
29195                     tooltip : this.iconTooltip
29196                 },
29197                 {
29198                     tag : 'span',
29199                     html : this.html
29200                 }
29201             ] 
29202         };
29203         
29204         return cfg;
29205     },
29206     
29207     initEvents: function() 
29208     {
29209         Roo.bootstrap.Element.superclass.initEvents.call(this);
29210         
29211         this.iconEl = this.el.select('i', true).first();
29212         
29213         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29214         
29215         Roo.bootstrap.FieldLabel.register(this);
29216     },
29217     
29218     /**
29219      * Mark this field as valid
29220      */
29221     markValid : function()
29222     {
29223         this.iconEl.show();
29224         
29225         this.iconEl.removeClass(this.invalidClass);
29226         
29227         this.iconEl.addClass(this.validClass);
29228         
29229         this.fireEvent('valid', this);
29230     },
29231     
29232     /**
29233      * Mark this field as invalid
29234      * @param {String} msg The validation message
29235      */
29236     markInvalid : function(msg)
29237     {
29238         this.iconEl.show();
29239         
29240         this.iconEl.removeClass(this.validClass);
29241         
29242         this.iconEl.addClass(this.invalidClass);
29243         
29244         this.fireEvent('invalid', this, msg);
29245     }
29246     
29247    
29248 });
29249
29250 Roo.apply(Roo.bootstrap.FieldLabel, {
29251     
29252     groups: {},
29253     
29254      /**
29255     * register a FieldLabel Group
29256     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29257     */
29258     register : function(label)
29259     {
29260         if(this.groups.hasOwnProperty(label.target)){
29261             return;
29262         }
29263      
29264         this.groups[label.target] = label;
29265         
29266     },
29267     /**
29268     * fetch a FieldLabel Group based on the target
29269     * @param {string} target
29270     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29271     */
29272     get: function(target) {
29273         if (typeof(this.groups[target]) == 'undefined') {
29274             return false;
29275         }
29276         
29277         return this.groups[target] ;
29278     }
29279 });
29280
29281  
29282
29283  /*
29284  * - LGPL
29285  *
29286  * page DateSplitField.
29287  * 
29288  */
29289
29290
29291 /**
29292  * @class Roo.bootstrap.DateSplitField
29293  * @extends Roo.bootstrap.Component
29294  * Bootstrap DateSplitField class
29295  * @cfg {string} fieldLabel - the label associated
29296  * @cfg {Number} labelWidth set the width of label (0-12)
29297  * @cfg {String} labelAlign (top|left)
29298  * @cfg {Boolean} dayAllowBlank (true|false) default false
29299  * @cfg {Boolean} monthAllowBlank (true|false) default false
29300  * @cfg {Boolean} yearAllowBlank (true|false) default false
29301  * @cfg {string} dayPlaceholder 
29302  * @cfg {string} monthPlaceholder
29303  * @cfg {string} yearPlaceholder
29304  * @cfg {string} dayFormat default 'd'
29305  * @cfg {string} monthFormat default 'm'
29306  * @cfg {string} yearFormat default 'Y'
29307  * @cfg {Number} labellg set the width of label (1-12)
29308  * @cfg {Number} labelmd set the width of label (1-12)
29309  * @cfg {Number} labelsm set the width of label (1-12)
29310  * @cfg {Number} labelxs set the width of label (1-12)
29311
29312  *     
29313  * @constructor
29314  * Create a new DateSplitField
29315  * @param {Object} config The config object
29316  */
29317
29318 Roo.bootstrap.DateSplitField = function(config){
29319     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29320     
29321     this.addEvents({
29322         // raw events
29323          /**
29324          * @event years
29325          * getting the data of years
29326          * @param {Roo.bootstrap.DateSplitField} this
29327          * @param {Object} years
29328          */
29329         "years" : true,
29330         /**
29331          * @event days
29332          * getting the data of days
29333          * @param {Roo.bootstrap.DateSplitField} this
29334          * @param {Object} days
29335          */
29336         "days" : true,
29337         /**
29338          * @event invalid
29339          * Fires after the field has been marked as invalid.
29340          * @param {Roo.form.Field} this
29341          * @param {String} msg The validation message
29342          */
29343         invalid : true,
29344        /**
29345          * @event valid
29346          * Fires after the field has been validated with no errors.
29347          * @param {Roo.form.Field} this
29348          */
29349         valid : true
29350     });
29351 };
29352
29353 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29354     
29355     fieldLabel : '',
29356     labelAlign : 'top',
29357     labelWidth : 3,
29358     dayAllowBlank : false,
29359     monthAllowBlank : false,
29360     yearAllowBlank : false,
29361     dayPlaceholder : '',
29362     monthPlaceholder : '',
29363     yearPlaceholder : '',
29364     dayFormat : 'd',
29365     monthFormat : 'm',
29366     yearFormat : 'Y',
29367     isFormField : true,
29368     labellg : 0,
29369     labelmd : 0,
29370     labelsm : 0,
29371     labelxs : 0,
29372     
29373     getAutoCreate : function()
29374     {
29375         var cfg = {
29376             tag : 'div',
29377             cls : 'row roo-date-split-field-group',
29378             cn : [
29379                 {
29380                     tag : 'input',
29381                     type : 'hidden',
29382                     cls : 'form-hidden-field roo-date-split-field-group-value',
29383                     name : this.name
29384                 }
29385             ]
29386         };
29387         
29388         var labelCls = 'col-md-12';
29389         var contentCls = 'col-md-4';
29390         
29391         if(this.fieldLabel){
29392             
29393             var label = {
29394                 tag : 'div',
29395                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29396                 cn : [
29397                     {
29398                         tag : 'label',
29399                         html : this.fieldLabel
29400                     }
29401                 ]
29402             };
29403             
29404             if(this.labelAlign == 'left'){
29405             
29406                 if(this.labelWidth > 12){
29407                     label.style = "width: " + this.labelWidth + 'px';
29408                 }
29409
29410                 if(this.labelWidth < 13 && this.labelmd == 0){
29411                     this.labelmd = this.labelWidth;
29412                 }
29413
29414                 if(this.labellg > 0){
29415                     labelCls = ' col-lg-' + this.labellg;
29416                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29417                 }
29418
29419                 if(this.labelmd > 0){
29420                     labelCls = ' col-md-' + this.labelmd;
29421                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29422                 }
29423
29424                 if(this.labelsm > 0){
29425                     labelCls = ' col-sm-' + this.labelsm;
29426                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29427                 }
29428
29429                 if(this.labelxs > 0){
29430                     labelCls = ' col-xs-' + this.labelxs;
29431                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29432                 }
29433             }
29434             
29435             label.cls += ' ' + labelCls;
29436             
29437             cfg.cn.push(label);
29438         }
29439         
29440         Roo.each(['day', 'month', 'year'], function(t){
29441             cfg.cn.push({
29442                 tag : 'div',
29443                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29444             });
29445         }, this);
29446         
29447         return cfg;
29448     },
29449     
29450     inputEl: function ()
29451     {
29452         return this.el.select('.roo-date-split-field-group-value', true).first();
29453     },
29454     
29455     onRender : function(ct, position) 
29456     {
29457         var _this = this;
29458         
29459         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29460         
29461         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29462         
29463         this.dayField = new Roo.bootstrap.ComboBox({
29464             allowBlank : this.dayAllowBlank,
29465             alwaysQuery : true,
29466             displayField : 'value',
29467             editable : false,
29468             fieldLabel : '',
29469             forceSelection : true,
29470             mode : 'local',
29471             placeholder : this.dayPlaceholder,
29472             selectOnFocus : true,
29473             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29474             triggerAction : 'all',
29475             typeAhead : true,
29476             valueField : 'value',
29477             store : new Roo.data.SimpleStore({
29478                 data : (function() {    
29479                     var days = [];
29480                     _this.fireEvent('days', _this, days);
29481                     return days;
29482                 })(),
29483                 fields : [ 'value' ]
29484             }),
29485             listeners : {
29486                 select : function (_self, record, index)
29487                 {
29488                     _this.setValue(_this.getValue());
29489                 }
29490             }
29491         });
29492
29493         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29494         
29495         this.monthField = new Roo.bootstrap.MonthField({
29496             after : '<i class=\"fa fa-calendar\"></i>',
29497             allowBlank : this.monthAllowBlank,
29498             placeholder : this.monthPlaceholder,
29499             readOnly : true,
29500             listeners : {
29501                 render : function (_self)
29502                 {
29503                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29504                         e.preventDefault();
29505                         _self.focus();
29506                     });
29507                 },
29508                 select : function (_self, oldvalue, newvalue)
29509                 {
29510                     _this.setValue(_this.getValue());
29511                 }
29512             }
29513         });
29514         
29515         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29516         
29517         this.yearField = new Roo.bootstrap.ComboBox({
29518             allowBlank : this.yearAllowBlank,
29519             alwaysQuery : true,
29520             displayField : 'value',
29521             editable : false,
29522             fieldLabel : '',
29523             forceSelection : true,
29524             mode : 'local',
29525             placeholder : this.yearPlaceholder,
29526             selectOnFocus : true,
29527             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29528             triggerAction : 'all',
29529             typeAhead : true,
29530             valueField : 'value',
29531             store : new Roo.data.SimpleStore({
29532                 data : (function() {
29533                     var years = [];
29534                     _this.fireEvent('years', _this, years);
29535                     return years;
29536                 })(),
29537                 fields : [ 'value' ]
29538             }),
29539             listeners : {
29540                 select : function (_self, record, index)
29541                 {
29542                     _this.setValue(_this.getValue());
29543                 }
29544             }
29545         });
29546
29547         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29548     },
29549     
29550     setValue : function(v, format)
29551     {
29552         this.inputEl.dom.value = v;
29553         
29554         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29555         
29556         var d = Date.parseDate(v, f);
29557         
29558         if(!d){
29559             this.validate();
29560             return;
29561         }
29562         
29563         this.setDay(d.format(this.dayFormat));
29564         this.setMonth(d.format(this.monthFormat));
29565         this.setYear(d.format(this.yearFormat));
29566         
29567         this.validate();
29568         
29569         return;
29570     },
29571     
29572     setDay : function(v)
29573     {
29574         this.dayField.setValue(v);
29575         this.inputEl.dom.value = this.getValue();
29576         this.validate();
29577         return;
29578     },
29579     
29580     setMonth : function(v)
29581     {
29582         this.monthField.setValue(v, true);
29583         this.inputEl.dom.value = this.getValue();
29584         this.validate();
29585         return;
29586     },
29587     
29588     setYear : function(v)
29589     {
29590         this.yearField.setValue(v);
29591         this.inputEl.dom.value = this.getValue();
29592         this.validate();
29593         return;
29594     },
29595     
29596     getDay : function()
29597     {
29598         return this.dayField.getValue();
29599     },
29600     
29601     getMonth : function()
29602     {
29603         return this.monthField.getValue();
29604     },
29605     
29606     getYear : function()
29607     {
29608         return this.yearField.getValue();
29609     },
29610     
29611     getValue : function()
29612     {
29613         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29614         
29615         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29616         
29617         return date;
29618     },
29619     
29620     reset : function()
29621     {
29622         this.setDay('');
29623         this.setMonth('');
29624         this.setYear('');
29625         this.inputEl.dom.value = '';
29626         this.validate();
29627         return;
29628     },
29629     
29630     validate : function()
29631     {
29632         var d = this.dayField.validate();
29633         var m = this.monthField.validate();
29634         var y = this.yearField.validate();
29635         
29636         var valid = true;
29637         
29638         if(
29639                 (!this.dayAllowBlank && !d) ||
29640                 (!this.monthAllowBlank && !m) ||
29641                 (!this.yearAllowBlank && !y)
29642         ){
29643             valid = false;
29644         }
29645         
29646         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29647             return valid;
29648         }
29649         
29650         if(valid){
29651             this.markValid();
29652             return valid;
29653         }
29654         
29655         this.markInvalid();
29656         
29657         return valid;
29658     },
29659     
29660     markValid : function()
29661     {
29662         
29663         var label = this.el.select('label', true).first();
29664         var icon = this.el.select('i.fa-star', true).first();
29665
29666         if(label && icon){
29667             icon.remove();
29668         }
29669         
29670         this.fireEvent('valid', this);
29671     },
29672     
29673      /**
29674      * Mark this field as invalid
29675      * @param {String} msg The validation message
29676      */
29677     markInvalid : function(msg)
29678     {
29679         
29680         var label = this.el.select('label', true).first();
29681         var icon = this.el.select('i.fa-star', true).first();
29682
29683         if(label && !icon){
29684             this.el.select('.roo-date-split-field-label', true).createChild({
29685                 tag : 'i',
29686                 cls : 'text-danger fa fa-lg fa-star',
29687                 tooltip : 'This field is required',
29688                 style : 'margin-right:5px;'
29689             }, label, true);
29690         }
29691         
29692         this.fireEvent('invalid', this, msg);
29693     },
29694     
29695     clearInvalid : function()
29696     {
29697         var label = this.el.select('label', true).first();
29698         var icon = this.el.select('i.fa-star', true).first();
29699
29700         if(label && icon){
29701             icon.remove();
29702         }
29703         
29704         this.fireEvent('valid', this);
29705     },
29706     
29707     getName: function()
29708     {
29709         return this.name;
29710     }
29711     
29712 });
29713
29714  /**
29715  *
29716  * This is based on 
29717  * http://masonry.desandro.com
29718  *
29719  * The idea is to render all the bricks based on vertical width...
29720  *
29721  * The original code extends 'outlayer' - we might need to use that....
29722  * 
29723  */
29724
29725
29726 /**
29727  * @class Roo.bootstrap.LayoutMasonry
29728  * @extends Roo.bootstrap.Component
29729  * Bootstrap Layout Masonry class
29730  * 
29731  * @constructor
29732  * Create a new Element
29733  * @param {Object} config The config object
29734  */
29735
29736 Roo.bootstrap.LayoutMasonry = function(config){
29737     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29738     
29739     this.bricks = [];
29740     
29741     this.addEvents({
29742         // raw events
29743         /**
29744          * @event layout
29745          * Fire after layout the items
29746          * @param {Roo.bootstrap.LayoutMasonry} this
29747          * @param {Roo.EventObject} e
29748          */
29749         "layout" : true
29750     });
29751     
29752 };
29753
29754 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29755     
29756     /**
29757      * @cfg {Boolean} isLayoutInstant = no animation?
29758      */   
29759     isLayoutInstant : false, // needed?
29760    
29761     /**
29762      * @cfg {Number} boxWidth  width of the columns
29763      */   
29764     boxWidth : 450,
29765     
29766       /**
29767      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29768      */   
29769     boxHeight : 0,
29770     
29771     /**
29772      * @cfg {Number} padWidth padding below box..
29773      */   
29774     padWidth : 10, 
29775     
29776     /**
29777      * @cfg {Number} gutter gutter width..
29778      */   
29779     gutter : 10,
29780     
29781      /**
29782      * @cfg {Number} maxCols maximum number of columns
29783      */   
29784     
29785     maxCols: 0,
29786     
29787     /**
29788      * @cfg {Boolean} isAutoInitial defalut true
29789      */   
29790     isAutoInitial : true, 
29791     
29792     containerWidth: 0,
29793     
29794     /**
29795      * @cfg {Boolean} isHorizontal defalut false
29796      */   
29797     isHorizontal : false, 
29798
29799     currentSize : null,
29800     
29801     tag: 'div',
29802     
29803     cls: '',
29804     
29805     bricks: null, //CompositeElement
29806     
29807     cols : 1,
29808     
29809     _isLayoutInited : false,
29810     
29811 //    isAlternative : false, // only use for vertical layout...
29812     
29813     /**
29814      * @cfg {Number} alternativePadWidth padding below box..
29815      */   
29816     alternativePadWidth : 50, 
29817     
29818     getAutoCreate : function(){
29819         
29820         var cfg = {
29821             tag: this.tag,
29822             cls: 'blog-masonary-wrapper ' + this.cls,
29823             cn : {
29824                 cls : 'mas-boxes masonary'
29825             }
29826         };
29827         
29828         return cfg;
29829     },
29830     
29831     getChildContainer: function( )
29832     {
29833         if (this.boxesEl) {
29834             return this.boxesEl;
29835         }
29836         
29837         this.boxesEl = this.el.select('.mas-boxes').first();
29838         
29839         return this.boxesEl;
29840     },
29841     
29842     
29843     initEvents : function()
29844     {
29845         var _this = this;
29846         
29847         if(this.isAutoInitial){
29848             Roo.log('hook children rendered');
29849             this.on('childrenrendered', function() {
29850                 Roo.log('children rendered');
29851                 _this.initial();
29852             } ,this);
29853         }
29854     },
29855     
29856     initial : function()
29857     {
29858         this.currentSize = this.el.getBox(true);
29859         
29860         Roo.EventManager.onWindowResize(this.resize, this); 
29861
29862         if(!this.isAutoInitial){
29863             this.layout();
29864             return;
29865         }
29866         
29867         this.layout();
29868         
29869         return;
29870         //this.layout.defer(500,this);
29871         
29872     },
29873     
29874     resize : function()
29875     {
29876         var cs = this.el.getBox(true);
29877         
29878         if (
29879                 this.currentSize.width == cs.width && 
29880                 this.currentSize.x == cs.x && 
29881                 this.currentSize.height == cs.height && 
29882                 this.currentSize.y == cs.y 
29883         ) {
29884             Roo.log("no change in with or X or Y");
29885             return;
29886         }
29887         
29888         this.currentSize = cs;
29889         
29890         this.layout();
29891         
29892     },
29893     
29894     layout : function()
29895     {   
29896         this._resetLayout();
29897         
29898         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29899         
29900         this.layoutItems( isInstant );
29901       
29902         this._isLayoutInited = true;
29903         
29904         this.fireEvent('layout', this);
29905         
29906     },
29907     
29908     _resetLayout : function()
29909     {
29910         if(this.isHorizontal){
29911             this.horizontalMeasureColumns();
29912             return;
29913         }
29914         
29915         this.verticalMeasureColumns();
29916         
29917     },
29918     
29919     verticalMeasureColumns : function()
29920     {
29921         this.getContainerWidth();
29922         
29923 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29924 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29925 //            return;
29926 //        }
29927         
29928         var boxWidth = this.boxWidth + this.padWidth;
29929         
29930         if(this.containerWidth < this.boxWidth){
29931             boxWidth = this.containerWidth
29932         }
29933         
29934         var containerWidth = this.containerWidth;
29935         
29936         var cols = Math.floor(containerWidth / boxWidth);
29937         
29938         this.cols = Math.max( cols, 1 );
29939         
29940         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29941         
29942         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29943         
29944         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29945         
29946         this.colWidth = boxWidth + avail - this.padWidth;
29947         
29948         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29949         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29950     },
29951     
29952     horizontalMeasureColumns : function()
29953     {
29954         this.getContainerWidth();
29955         
29956         var boxWidth = this.boxWidth;
29957         
29958         if(this.containerWidth < boxWidth){
29959             boxWidth = this.containerWidth;
29960         }
29961         
29962         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29963         
29964         this.el.setHeight(boxWidth);
29965         
29966     },
29967     
29968     getContainerWidth : function()
29969     {
29970         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29971     },
29972     
29973     layoutItems : function( isInstant )
29974     {
29975         var items = Roo.apply([], this.bricks);
29976         
29977         if(this.isHorizontal){
29978             this._horizontalLayoutItems( items , isInstant );
29979             return;
29980         }
29981         
29982 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29983 //            this._verticalAlternativeLayoutItems( items , isInstant );
29984 //            return;
29985 //        }
29986         
29987         this._verticalLayoutItems( items , isInstant );
29988         
29989     },
29990     
29991     _verticalLayoutItems : function ( items , isInstant)
29992     {
29993         if ( !items || !items.length ) {
29994             return;
29995         }
29996         
29997         var standard = [
29998             ['xs', 'xs', 'xs', 'tall'],
29999             ['xs', 'xs', 'tall'],
30000             ['xs', 'xs', 'sm'],
30001             ['xs', 'xs', 'xs'],
30002             ['xs', 'tall'],
30003             ['xs', 'sm'],
30004             ['xs', 'xs'],
30005             ['xs'],
30006             
30007             ['sm', 'xs', 'xs'],
30008             ['sm', 'xs'],
30009             ['sm'],
30010             
30011             ['tall', 'xs', 'xs', 'xs'],
30012             ['tall', 'xs', 'xs'],
30013             ['tall', 'xs'],
30014             ['tall']
30015             
30016         ];
30017         
30018         var queue = [];
30019         
30020         var boxes = [];
30021         
30022         var box = [];
30023         
30024         Roo.each(items, function(item, k){
30025             
30026             switch (item.size) {
30027                 // these layouts take up a full box,
30028                 case 'md' :
30029                 case 'md-left' :
30030                 case 'md-right' :
30031                 case 'wide' :
30032                     
30033                     if(box.length){
30034                         boxes.push(box);
30035                         box = [];
30036                     }
30037                     
30038                     boxes.push([item]);
30039                     
30040                     break;
30041                     
30042                 case 'xs' :
30043                 case 'sm' :
30044                 case 'tall' :
30045                     
30046                     box.push(item);
30047                     
30048                     break;
30049                 default :
30050                     break;
30051                     
30052             }
30053             
30054         }, this);
30055         
30056         if(box.length){
30057             boxes.push(box);
30058             box = [];
30059         }
30060         
30061         var filterPattern = function(box, length)
30062         {
30063             if(!box.length){
30064                 return;
30065             }
30066             
30067             var match = false;
30068             
30069             var pattern = box.slice(0, length);
30070             
30071             var format = [];
30072             
30073             Roo.each(pattern, function(i){
30074                 format.push(i.size);
30075             }, this);
30076             
30077             Roo.each(standard, function(s){
30078                 
30079                 if(String(s) != String(format)){
30080                     return;
30081                 }
30082                 
30083                 match = true;
30084                 return false;
30085                 
30086             }, this);
30087             
30088             if(!match && length == 1){
30089                 return;
30090             }
30091             
30092             if(!match){
30093                 filterPattern(box, length - 1);
30094                 return;
30095             }
30096                 
30097             queue.push(pattern);
30098
30099             box = box.slice(length, box.length);
30100
30101             filterPattern(box, 4);
30102
30103             return;
30104             
30105         }
30106         
30107         Roo.each(boxes, function(box, k){
30108             
30109             if(!box.length){
30110                 return;
30111             }
30112             
30113             if(box.length == 1){
30114                 queue.push(box);
30115                 return;
30116             }
30117             
30118             filterPattern(box, 4);
30119             
30120         }, this);
30121         
30122         this._processVerticalLayoutQueue( queue, isInstant );
30123         
30124     },
30125     
30126 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30127 //    {
30128 //        if ( !items || !items.length ) {
30129 //            return;
30130 //        }
30131 //
30132 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30133 //        
30134 //    },
30135     
30136     _horizontalLayoutItems : function ( items , isInstant)
30137     {
30138         if ( !items || !items.length || items.length < 3) {
30139             return;
30140         }
30141         
30142         items.reverse();
30143         
30144         var eItems = items.slice(0, 3);
30145         
30146         items = items.slice(3, items.length);
30147         
30148         var standard = [
30149             ['xs', 'xs', 'xs', 'wide'],
30150             ['xs', 'xs', 'wide'],
30151             ['xs', 'xs', 'sm'],
30152             ['xs', 'xs', 'xs'],
30153             ['xs', 'wide'],
30154             ['xs', 'sm'],
30155             ['xs', 'xs'],
30156             ['xs'],
30157             
30158             ['sm', 'xs', 'xs'],
30159             ['sm', 'xs'],
30160             ['sm'],
30161             
30162             ['wide', 'xs', 'xs', 'xs'],
30163             ['wide', 'xs', 'xs'],
30164             ['wide', 'xs'],
30165             ['wide'],
30166             
30167             ['wide-thin']
30168         ];
30169         
30170         var queue = [];
30171         
30172         var boxes = [];
30173         
30174         var box = [];
30175         
30176         Roo.each(items, function(item, k){
30177             
30178             switch (item.size) {
30179                 case 'md' :
30180                 case 'md-left' :
30181                 case 'md-right' :
30182                 case 'tall' :
30183                     
30184                     if(box.length){
30185                         boxes.push(box);
30186                         box = [];
30187                     }
30188                     
30189                     boxes.push([item]);
30190                     
30191                     break;
30192                     
30193                 case 'xs' :
30194                 case 'sm' :
30195                 case 'wide' :
30196                 case 'wide-thin' :
30197                     
30198                     box.push(item);
30199                     
30200                     break;
30201                 default :
30202                     break;
30203                     
30204             }
30205             
30206         }, this);
30207         
30208         if(box.length){
30209             boxes.push(box);
30210             box = [];
30211         }
30212         
30213         var filterPattern = function(box, length)
30214         {
30215             if(!box.length){
30216                 return;
30217             }
30218             
30219             var match = false;
30220             
30221             var pattern = box.slice(0, length);
30222             
30223             var format = [];
30224             
30225             Roo.each(pattern, function(i){
30226                 format.push(i.size);
30227             }, this);
30228             
30229             Roo.each(standard, function(s){
30230                 
30231                 if(String(s) != String(format)){
30232                     return;
30233                 }
30234                 
30235                 match = true;
30236                 return false;
30237                 
30238             }, this);
30239             
30240             if(!match && length == 1){
30241                 return;
30242             }
30243             
30244             if(!match){
30245                 filterPattern(box, length - 1);
30246                 return;
30247             }
30248                 
30249             queue.push(pattern);
30250
30251             box = box.slice(length, box.length);
30252
30253             filterPattern(box, 4);
30254
30255             return;
30256             
30257         }
30258         
30259         Roo.each(boxes, function(box, k){
30260             
30261             if(!box.length){
30262                 return;
30263             }
30264             
30265             if(box.length == 1){
30266                 queue.push(box);
30267                 return;
30268             }
30269             
30270             filterPattern(box, 4);
30271             
30272         }, this);
30273         
30274         
30275         var prune = [];
30276         
30277         var pos = this.el.getBox(true);
30278         
30279         var minX = pos.x;
30280         
30281         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30282         
30283         var hit_end = false;
30284         
30285         Roo.each(queue, function(box){
30286             
30287             if(hit_end){
30288                 
30289                 Roo.each(box, function(b){
30290                 
30291                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30292                     b.el.hide();
30293
30294                 }, this);
30295
30296                 return;
30297             }
30298             
30299             var mx = 0;
30300             
30301             Roo.each(box, function(b){
30302                 
30303                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30304                 b.el.show();
30305
30306                 mx = Math.max(mx, b.x);
30307                 
30308             }, this);
30309             
30310             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30311             
30312             if(maxX < minX){
30313                 
30314                 Roo.each(box, function(b){
30315                 
30316                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30317                     b.el.hide();
30318                     
30319                 }, this);
30320                 
30321                 hit_end = true;
30322                 
30323                 return;
30324             }
30325             
30326             prune.push(box);
30327             
30328         }, this);
30329         
30330         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30331     },
30332     
30333     /** Sets position of item in DOM
30334     * @param {Element} item
30335     * @param {Number} x - horizontal position
30336     * @param {Number} y - vertical position
30337     * @param {Boolean} isInstant - disables transitions
30338     */
30339     _processVerticalLayoutQueue : function( queue, isInstant )
30340     {
30341         var pos = this.el.getBox(true);
30342         var x = pos.x;
30343         var y = pos.y;
30344         var maxY = [];
30345         
30346         for (var i = 0; i < this.cols; i++){
30347             maxY[i] = pos.y;
30348         }
30349         
30350         Roo.each(queue, function(box, k){
30351             
30352             var col = k % this.cols;
30353             
30354             Roo.each(box, function(b,kk){
30355                 
30356                 b.el.position('absolute');
30357                 
30358                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30359                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30360                 
30361                 if(b.size == 'md-left' || b.size == 'md-right'){
30362                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30363                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30364                 }
30365                 
30366                 b.el.setWidth(width);
30367                 b.el.setHeight(height);
30368                 // iframe?
30369                 b.el.select('iframe',true).setSize(width,height);
30370                 
30371             }, this);
30372             
30373             for (var i = 0; i < this.cols; i++){
30374                 
30375                 if(maxY[i] < maxY[col]){
30376                     col = i;
30377                     continue;
30378                 }
30379                 
30380                 col = Math.min(col, i);
30381                 
30382             }
30383             
30384             x = pos.x + col * (this.colWidth + this.padWidth);
30385             
30386             y = maxY[col];
30387             
30388             var positions = [];
30389             
30390             switch (box.length){
30391                 case 1 :
30392                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30393                     break;
30394                 case 2 :
30395                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30396                     break;
30397                 case 3 :
30398                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30399                     break;
30400                 case 4 :
30401                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30402                     break;
30403                 default :
30404                     break;
30405             }
30406             
30407             Roo.each(box, function(b,kk){
30408                 
30409                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30410                 
30411                 var sz = b.el.getSize();
30412                 
30413                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30414                 
30415             }, this);
30416             
30417         }, this);
30418         
30419         var mY = 0;
30420         
30421         for (var i = 0; i < this.cols; i++){
30422             mY = Math.max(mY, maxY[i]);
30423         }
30424         
30425         this.el.setHeight(mY - pos.y);
30426         
30427     },
30428     
30429 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30430 //    {
30431 //        var pos = this.el.getBox(true);
30432 //        var x = pos.x;
30433 //        var y = pos.y;
30434 //        var maxX = pos.right;
30435 //        
30436 //        var maxHeight = 0;
30437 //        
30438 //        Roo.each(items, function(item, k){
30439 //            
30440 //            var c = k % 2;
30441 //            
30442 //            item.el.position('absolute');
30443 //                
30444 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30445 //
30446 //            item.el.setWidth(width);
30447 //
30448 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30449 //
30450 //            item.el.setHeight(height);
30451 //            
30452 //            if(c == 0){
30453 //                item.el.setXY([x, y], isInstant ? false : true);
30454 //            } else {
30455 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30456 //            }
30457 //            
30458 //            y = y + height + this.alternativePadWidth;
30459 //            
30460 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30461 //            
30462 //        }, this);
30463 //        
30464 //        this.el.setHeight(maxHeight);
30465 //        
30466 //    },
30467     
30468     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30469     {
30470         var pos = this.el.getBox(true);
30471         
30472         var minX = pos.x;
30473         var minY = pos.y;
30474         
30475         var maxX = pos.right;
30476         
30477         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30478         
30479         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30480         
30481         Roo.each(queue, function(box, k){
30482             
30483             Roo.each(box, function(b, kk){
30484                 
30485                 b.el.position('absolute');
30486                 
30487                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30488                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30489                 
30490                 if(b.size == 'md-left' || b.size == 'md-right'){
30491                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30492                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30493                 }
30494                 
30495                 b.el.setWidth(width);
30496                 b.el.setHeight(height);
30497                 
30498             }, this);
30499             
30500             if(!box.length){
30501                 return;
30502             }
30503             
30504             var positions = [];
30505             
30506             switch (box.length){
30507                 case 1 :
30508                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30509                     break;
30510                 case 2 :
30511                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30512                     break;
30513                 case 3 :
30514                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30515                     break;
30516                 case 4 :
30517                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30518                     break;
30519                 default :
30520                     break;
30521             }
30522             
30523             Roo.each(box, function(b,kk){
30524                 
30525                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30526                 
30527                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30528                 
30529             }, this);
30530             
30531         }, this);
30532         
30533     },
30534     
30535     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30536     {
30537         Roo.each(eItems, function(b,k){
30538             
30539             b.size = (k == 0) ? 'sm' : 'xs';
30540             b.x = (k == 0) ? 2 : 1;
30541             b.y = (k == 0) ? 2 : 1;
30542             
30543             b.el.position('absolute');
30544             
30545             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30546                 
30547             b.el.setWidth(width);
30548             
30549             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30550             
30551             b.el.setHeight(height);
30552             
30553         }, this);
30554
30555         var positions = [];
30556         
30557         positions.push({
30558             x : maxX - this.unitWidth * 2 - this.gutter,
30559             y : minY
30560         });
30561         
30562         positions.push({
30563             x : maxX - this.unitWidth,
30564             y : minY + (this.unitWidth + this.gutter) * 2
30565         });
30566         
30567         positions.push({
30568             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30569             y : minY
30570         });
30571         
30572         Roo.each(eItems, function(b,k){
30573             
30574             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30575
30576         }, this);
30577         
30578     },
30579     
30580     getVerticalOneBoxColPositions : function(x, y, box)
30581     {
30582         var pos = [];
30583         
30584         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30585         
30586         if(box[0].size == 'md-left'){
30587             rand = 0;
30588         }
30589         
30590         if(box[0].size == 'md-right'){
30591             rand = 1;
30592         }
30593         
30594         pos.push({
30595             x : x + (this.unitWidth + this.gutter) * rand,
30596             y : y
30597         });
30598         
30599         return pos;
30600     },
30601     
30602     getVerticalTwoBoxColPositions : function(x, y, box)
30603     {
30604         var pos = [];
30605         
30606         if(box[0].size == 'xs'){
30607             
30608             pos.push({
30609                 x : x,
30610                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30611             });
30612
30613             pos.push({
30614                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30615                 y : y
30616             });
30617             
30618             return pos;
30619             
30620         }
30621         
30622         pos.push({
30623             x : x,
30624             y : y
30625         });
30626
30627         pos.push({
30628             x : x + (this.unitWidth + this.gutter) * 2,
30629             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30630         });
30631         
30632         return pos;
30633         
30634     },
30635     
30636     getVerticalThreeBoxColPositions : function(x, y, box)
30637     {
30638         var pos = [];
30639         
30640         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30641             
30642             pos.push({
30643                 x : x,
30644                 y : y
30645             });
30646
30647             pos.push({
30648                 x : x + (this.unitWidth + this.gutter) * 1,
30649                 y : y
30650             });
30651             
30652             pos.push({
30653                 x : x + (this.unitWidth + this.gutter) * 2,
30654                 y : y
30655             });
30656             
30657             return pos;
30658             
30659         }
30660         
30661         if(box[0].size == 'xs' && box[1].size == 'xs'){
30662             
30663             pos.push({
30664                 x : x,
30665                 y : y
30666             });
30667
30668             pos.push({
30669                 x : x,
30670                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30671             });
30672             
30673             pos.push({
30674                 x : x + (this.unitWidth + this.gutter) * 1,
30675                 y : y
30676             });
30677             
30678             return pos;
30679             
30680         }
30681         
30682         pos.push({
30683             x : x,
30684             y : y
30685         });
30686
30687         pos.push({
30688             x : x + (this.unitWidth + this.gutter) * 2,
30689             y : y
30690         });
30691
30692         pos.push({
30693             x : x + (this.unitWidth + this.gutter) * 2,
30694             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30695         });
30696             
30697         return pos;
30698         
30699     },
30700     
30701     getVerticalFourBoxColPositions : function(x, y, box)
30702     {
30703         var pos = [];
30704         
30705         if(box[0].size == 'xs'){
30706             
30707             pos.push({
30708                 x : x,
30709                 y : y
30710             });
30711
30712             pos.push({
30713                 x : x,
30714                 y : y + (this.unitHeight + this.gutter) * 1
30715             });
30716             
30717             pos.push({
30718                 x : x,
30719                 y : y + (this.unitHeight + this.gutter) * 2
30720             });
30721             
30722             pos.push({
30723                 x : x + (this.unitWidth + this.gutter) * 1,
30724                 y : y
30725             });
30726             
30727             return pos;
30728             
30729         }
30730         
30731         pos.push({
30732             x : x,
30733             y : y
30734         });
30735
30736         pos.push({
30737             x : x + (this.unitWidth + this.gutter) * 2,
30738             y : y
30739         });
30740
30741         pos.push({
30742             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30743             y : y + (this.unitHeight + this.gutter) * 1
30744         });
30745
30746         pos.push({
30747             x : x + (this.unitWidth + this.gutter) * 2,
30748             y : y + (this.unitWidth + this.gutter) * 2
30749         });
30750
30751         return pos;
30752         
30753     },
30754     
30755     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30756     {
30757         var pos = [];
30758         
30759         if(box[0].size == 'md-left'){
30760             pos.push({
30761                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30762                 y : minY
30763             });
30764             
30765             return pos;
30766         }
30767         
30768         if(box[0].size == 'md-right'){
30769             pos.push({
30770                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30771                 y : minY + (this.unitWidth + this.gutter) * 1
30772             });
30773             
30774             return pos;
30775         }
30776         
30777         var rand = Math.floor(Math.random() * (4 - box[0].y));
30778         
30779         pos.push({
30780             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30781             y : minY + (this.unitWidth + this.gutter) * rand
30782         });
30783         
30784         return pos;
30785         
30786     },
30787     
30788     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30789     {
30790         var pos = [];
30791         
30792         if(box[0].size == 'xs'){
30793             
30794             pos.push({
30795                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30796                 y : minY
30797             });
30798
30799             pos.push({
30800                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30801                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30802             });
30803             
30804             return pos;
30805             
30806         }
30807         
30808         pos.push({
30809             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30810             y : minY
30811         });
30812
30813         pos.push({
30814             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30815             y : minY + (this.unitWidth + this.gutter) * 2
30816         });
30817         
30818         return pos;
30819         
30820     },
30821     
30822     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30823     {
30824         var pos = [];
30825         
30826         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30827             
30828             pos.push({
30829                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30830                 y : minY
30831             });
30832
30833             pos.push({
30834                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30835                 y : minY + (this.unitWidth + this.gutter) * 1
30836             });
30837             
30838             pos.push({
30839                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30840                 y : minY + (this.unitWidth + this.gutter) * 2
30841             });
30842             
30843             return pos;
30844             
30845         }
30846         
30847         if(box[0].size == 'xs' && box[1].size == 'xs'){
30848             
30849             pos.push({
30850                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30851                 y : minY
30852             });
30853
30854             pos.push({
30855                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30856                 y : minY
30857             });
30858             
30859             pos.push({
30860                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30861                 y : minY + (this.unitWidth + this.gutter) * 1
30862             });
30863             
30864             return pos;
30865             
30866         }
30867         
30868         pos.push({
30869             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30870             y : minY
30871         });
30872
30873         pos.push({
30874             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30875             y : minY + (this.unitWidth + this.gutter) * 2
30876         });
30877
30878         pos.push({
30879             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30880             y : minY + (this.unitWidth + this.gutter) * 2
30881         });
30882             
30883         return pos;
30884         
30885     },
30886     
30887     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30888     {
30889         var pos = [];
30890         
30891         if(box[0].size == 'xs'){
30892             
30893             pos.push({
30894                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30895                 y : minY
30896             });
30897
30898             pos.push({
30899                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30900                 y : minY
30901             });
30902             
30903             pos.push({
30904                 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),
30905                 y : minY
30906             });
30907             
30908             pos.push({
30909                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30910                 y : minY + (this.unitWidth + this.gutter) * 1
30911             });
30912             
30913             return pos;
30914             
30915         }
30916         
30917         pos.push({
30918             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30919             y : minY
30920         });
30921         
30922         pos.push({
30923             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30924             y : minY + (this.unitWidth + this.gutter) * 2
30925         });
30926         
30927         pos.push({
30928             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30929             y : minY + (this.unitWidth + this.gutter) * 2
30930         });
30931         
30932         pos.push({
30933             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),
30934             y : minY + (this.unitWidth + this.gutter) * 2
30935         });
30936
30937         return pos;
30938         
30939     }
30940     
30941 });
30942
30943  
30944
30945  /**
30946  *
30947  * This is based on 
30948  * http://masonry.desandro.com
30949  *
30950  * The idea is to render all the bricks based on vertical width...
30951  *
30952  * The original code extends 'outlayer' - we might need to use that....
30953  * 
30954  */
30955
30956
30957 /**
30958  * @class Roo.bootstrap.LayoutMasonryAuto
30959  * @extends Roo.bootstrap.Component
30960  * Bootstrap Layout Masonry class
30961  * 
30962  * @constructor
30963  * Create a new Element
30964  * @param {Object} config The config object
30965  */
30966
30967 Roo.bootstrap.LayoutMasonryAuto = function(config){
30968     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30969 };
30970
30971 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30972     
30973       /**
30974      * @cfg {Boolean} isFitWidth  - resize the width..
30975      */   
30976     isFitWidth : false,  // options..
30977     /**
30978      * @cfg {Boolean} isOriginLeft = left align?
30979      */   
30980     isOriginLeft : true,
30981     /**
30982      * @cfg {Boolean} isOriginTop = top align?
30983      */   
30984     isOriginTop : false,
30985     /**
30986      * @cfg {Boolean} isLayoutInstant = no animation?
30987      */   
30988     isLayoutInstant : false, // needed?
30989     /**
30990      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30991      */   
30992     isResizingContainer : true,
30993     /**
30994      * @cfg {Number} columnWidth  width of the columns 
30995      */   
30996     
30997     columnWidth : 0,
30998     
30999     /**
31000      * @cfg {Number} maxCols maximum number of columns
31001      */   
31002     
31003     maxCols: 0,
31004     /**
31005      * @cfg {Number} padHeight padding below box..
31006      */   
31007     
31008     padHeight : 10, 
31009     
31010     /**
31011      * @cfg {Boolean} isAutoInitial defalut true
31012      */   
31013     
31014     isAutoInitial : true, 
31015     
31016     // private?
31017     gutter : 0,
31018     
31019     containerWidth: 0,
31020     initialColumnWidth : 0,
31021     currentSize : null,
31022     
31023     colYs : null, // array.
31024     maxY : 0,
31025     padWidth: 10,
31026     
31027     
31028     tag: 'div',
31029     cls: '',
31030     bricks: null, //CompositeElement
31031     cols : 0, // array?
31032     // element : null, // wrapped now this.el
31033     _isLayoutInited : null, 
31034     
31035     
31036     getAutoCreate : function(){
31037         
31038         var cfg = {
31039             tag: this.tag,
31040             cls: 'blog-masonary-wrapper ' + this.cls,
31041             cn : {
31042                 cls : 'mas-boxes masonary'
31043             }
31044         };
31045         
31046         return cfg;
31047     },
31048     
31049     getChildContainer: function( )
31050     {
31051         if (this.boxesEl) {
31052             return this.boxesEl;
31053         }
31054         
31055         this.boxesEl = this.el.select('.mas-boxes').first();
31056         
31057         return this.boxesEl;
31058     },
31059     
31060     
31061     initEvents : function()
31062     {
31063         var _this = this;
31064         
31065         if(this.isAutoInitial){
31066             Roo.log('hook children rendered');
31067             this.on('childrenrendered', function() {
31068                 Roo.log('children rendered');
31069                 _this.initial();
31070             } ,this);
31071         }
31072         
31073     },
31074     
31075     initial : function()
31076     {
31077         this.reloadItems();
31078
31079         this.currentSize = this.el.getBox(true);
31080
31081         /// was window resize... - let's see if this works..
31082         Roo.EventManager.onWindowResize(this.resize, this); 
31083
31084         if(!this.isAutoInitial){
31085             this.layout();
31086             return;
31087         }
31088         
31089         this.layout.defer(500,this);
31090     },
31091     
31092     reloadItems: function()
31093     {
31094         this.bricks = this.el.select('.masonry-brick', true);
31095         
31096         this.bricks.each(function(b) {
31097             //Roo.log(b.getSize());
31098             if (!b.attr('originalwidth')) {
31099                 b.attr('originalwidth',  b.getSize().width);
31100             }
31101             
31102         });
31103         
31104         Roo.log(this.bricks.elements.length);
31105     },
31106     
31107     resize : function()
31108     {
31109         Roo.log('resize');
31110         var cs = this.el.getBox(true);
31111         
31112         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31113             Roo.log("no change in with or X");
31114             return;
31115         }
31116         this.currentSize = cs;
31117         this.layout();
31118     },
31119     
31120     layout : function()
31121     {
31122          Roo.log('layout');
31123         this._resetLayout();
31124         //this._manageStamps();
31125       
31126         // don't animate first layout
31127         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31128         this.layoutItems( isInstant );
31129       
31130         // flag for initalized
31131         this._isLayoutInited = true;
31132     },
31133     
31134     layoutItems : function( isInstant )
31135     {
31136         //var items = this._getItemsForLayout( this.items );
31137         // original code supports filtering layout items.. we just ignore it..
31138         
31139         this._layoutItems( this.bricks , isInstant );
31140       
31141         this._postLayout();
31142     },
31143     _layoutItems : function ( items , isInstant)
31144     {
31145        //this.fireEvent( 'layout', this, items );
31146     
31147
31148         if ( !items || !items.elements.length ) {
31149           // no items, emit event with empty array
31150             return;
31151         }
31152
31153         var queue = [];
31154         items.each(function(item) {
31155             Roo.log("layout item");
31156             Roo.log(item);
31157             // get x/y object from method
31158             var position = this._getItemLayoutPosition( item );
31159             // enqueue
31160             position.item = item;
31161             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31162             queue.push( position );
31163         }, this);
31164       
31165         this._processLayoutQueue( queue );
31166     },
31167     /** Sets position of item in DOM
31168     * @param {Element} item
31169     * @param {Number} x - horizontal position
31170     * @param {Number} y - vertical position
31171     * @param {Boolean} isInstant - disables transitions
31172     */
31173     _processLayoutQueue : function( queue )
31174     {
31175         for ( var i=0, len = queue.length; i < len; i++ ) {
31176             var obj = queue[i];
31177             obj.item.position('absolute');
31178             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31179         }
31180     },
31181       
31182     
31183     /**
31184     * Any logic you want to do after each layout,
31185     * i.e. size the container
31186     */
31187     _postLayout : function()
31188     {
31189         this.resizeContainer();
31190     },
31191     
31192     resizeContainer : function()
31193     {
31194         if ( !this.isResizingContainer ) {
31195             return;
31196         }
31197         var size = this._getContainerSize();
31198         if ( size ) {
31199             this.el.setSize(size.width,size.height);
31200             this.boxesEl.setSize(size.width,size.height);
31201         }
31202     },
31203     
31204     
31205     
31206     _resetLayout : function()
31207     {
31208         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31209         this.colWidth = this.el.getWidth();
31210         //this.gutter = this.el.getWidth(); 
31211         
31212         this.measureColumns();
31213
31214         // reset column Y
31215         var i = this.cols;
31216         this.colYs = [];
31217         while (i--) {
31218             this.colYs.push( 0 );
31219         }
31220     
31221         this.maxY = 0;
31222     },
31223
31224     measureColumns : function()
31225     {
31226         this.getContainerWidth();
31227       // if columnWidth is 0, default to outerWidth of first item
31228         if ( !this.columnWidth ) {
31229             var firstItem = this.bricks.first();
31230             Roo.log(firstItem);
31231             this.columnWidth  = this.containerWidth;
31232             if (firstItem && firstItem.attr('originalwidth') ) {
31233                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31234             }
31235             // columnWidth fall back to item of first element
31236             Roo.log("set column width?");
31237                         this.initialColumnWidth = this.columnWidth  ;
31238
31239             // if first elem has no width, default to size of container
31240             
31241         }
31242         
31243         
31244         if (this.initialColumnWidth) {
31245             this.columnWidth = this.initialColumnWidth;
31246         }
31247         
31248         
31249             
31250         // column width is fixed at the top - however if container width get's smaller we should
31251         // reduce it...
31252         
31253         // this bit calcs how man columns..
31254             
31255         var columnWidth = this.columnWidth += this.gutter;
31256       
31257         // calculate columns
31258         var containerWidth = this.containerWidth + this.gutter;
31259         
31260         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31261         // fix rounding errors, typically with gutters
31262         var excess = columnWidth - containerWidth % columnWidth;
31263         
31264         
31265         // if overshoot is less than a pixel, round up, otherwise floor it
31266         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31267         cols = Math[ mathMethod ]( cols );
31268         this.cols = Math.max( cols, 1 );
31269         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31270         
31271          // padding positioning..
31272         var totalColWidth = this.cols * this.columnWidth;
31273         var padavail = this.containerWidth - totalColWidth;
31274         // so for 2 columns - we need 3 'pads'
31275         
31276         var padNeeded = (1+this.cols) * this.padWidth;
31277         
31278         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31279         
31280         this.columnWidth += padExtra
31281         //this.padWidth = Math.floor(padavail /  ( this.cols));
31282         
31283         // adjust colum width so that padding is fixed??
31284         
31285         // we have 3 columns ... total = width * 3
31286         // we have X left over... that should be used by 
31287         
31288         //if (this.expandC) {
31289             
31290         //}
31291         
31292         
31293         
31294     },
31295     
31296     getContainerWidth : function()
31297     {
31298        /* // container is parent if fit width
31299         var container = this.isFitWidth ? this.element.parentNode : this.element;
31300         // check that this.size and size are there
31301         // IE8 triggers resize on body size change, so they might not be
31302         
31303         var size = getSize( container );  //FIXME
31304         this.containerWidth = size && size.innerWidth; //FIXME
31305         */
31306          
31307         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31308         
31309     },
31310     
31311     _getItemLayoutPosition : function( item )  // what is item?
31312     {
31313         // we resize the item to our columnWidth..
31314       
31315         item.setWidth(this.columnWidth);
31316         item.autoBoxAdjust  = false;
31317         
31318         var sz = item.getSize();
31319  
31320         // how many columns does this brick span
31321         var remainder = this.containerWidth % this.columnWidth;
31322         
31323         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31324         // round if off by 1 pixel, otherwise use ceil
31325         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31326         colSpan = Math.min( colSpan, this.cols );
31327         
31328         // normally this should be '1' as we dont' currently allow multi width columns..
31329         
31330         var colGroup = this._getColGroup( colSpan );
31331         // get the minimum Y value from the columns
31332         var minimumY = Math.min.apply( Math, colGroup );
31333         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31334         
31335         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31336          
31337         // position the brick
31338         var position = {
31339             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31340             y: this.currentSize.y + minimumY + this.padHeight
31341         };
31342         
31343         Roo.log(position);
31344         // apply setHeight to necessary columns
31345         var setHeight = minimumY + sz.height + this.padHeight;
31346         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31347         
31348         var setSpan = this.cols + 1 - colGroup.length;
31349         for ( var i = 0; i < setSpan; i++ ) {
31350           this.colYs[ shortColIndex + i ] = setHeight ;
31351         }
31352       
31353         return position;
31354     },
31355     
31356     /**
31357      * @param {Number} colSpan - number of columns the element spans
31358      * @returns {Array} colGroup
31359      */
31360     _getColGroup : function( colSpan )
31361     {
31362         if ( colSpan < 2 ) {
31363           // if brick spans only one column, use all the column Ys
31364           return this.colYs;
31365         }
31366       
31367         var colGroup = [];
31368         // how many different places could this brick fit horizontally
31369         var groupCount = this.cols + 1 - colSpan;
31370         // for each group potential horizontal position
31371         for ( var i = 0; i < groupCount; i++ ) {
31372           // make an array of colY values for that one group
31373           var groupColYs = this.colYs.slice( i, i + colSpan );
31374           // and get the max value of the array
31375           colGroup[i] = Math.max.apply( Math, groupColYs );
31376         }
31377         return colGroup;
31378     },
31379     /*
31380     _manageStamp : function( stamp )
31381     {
31382         var stampSize =  stamp.getSize();
31383         var offset = stamp.getBox();
31384         // get the columns that this stamp affects
31385         var firstX = this.isOriginLeft ? offset.x : offset.right;
31386         var lastX = firstX + stampSize.width;
31387         var firstCol = Math.floor( firstX / this.columnWidth );
31388         firstCol = Math.max( 0, firstCol );
31389         
31390         var lastCol = Math.floor( lastX / this.columnWidth );
31391         // lastCol should not go over if multiple of columnWidth #425
31392         lastCol -= lastX % this.columnWidth ? 0 : 1;
31393         lastCol = Math.min( this.cols - 1, lastCol );
31394         
31395         // set colYs to bottom of the stamp
31396         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31397             stampSize.height;
31398             
31399         for ( var i = firstCol; i <= lastCol; i++ ) {
31400           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31401         }
31402     },
31403     */
31404     
31405     _getContainerSize : function()
31406     {
31407         this.maxY = Math.max.apply( Math, this.colYs );
31408         var size = {
31409             height: this.maxY
31410         };
31411       
31412         if ( this.isFitWidth ) {
31413             size.width = this._getContainerFitWidth();
31414         }
31415       
31416         return size;
31417     },
31418     
31419     _getContainerFitWidth : function()
31420     {
31421         var unusedCols = 0;
31422         // count unused columns
31423         var i = this.cols;
31424         while ( --i ) {
31425           if ( this.colYs[i] !== 0 ) {
31426             break;
31427           }
31428           unusedCols++;
31429         }
31430         // fit container to columns that have been used
31431         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31432     },
31433     
31434     needsResizeLayout : function()
31435     {
31436         var previousWidth = this.containerWidth;
31437         this.getContainerWidth();
31438         return previousWidth !== this.containerWidth;
31439     }
31440  
31441 });
31442
31443  
31444
31445  /*
31446  * - LGPL
31447  *
31448  * element
31449  * 
31450  */
31451
31452 /**
31453  * @class Roo.bootstrap.MasonryBrick
31454  * @extends Roo.bootstrap.Component
31455  * Bootstrap MasonryBrick class
31456  * 
31457  * @constructor
31458  * Create a new MasonryBrick
31459  * @param {Object} config The config object
31460  */
31461
31462 Roo.bootstrap.MasonryBrick = function(config){
31463     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31464     
31465     this.addEvents({
31466         // raw events
31467         /**
31468          * @event click
31469          * When a MasonryBrick is clcik
31470          * @param {Roo.bootstrap.MasonryBrick} this
31471          * @param {Roo.EventObject} e
31472          */
31473         "click" : true
31474     });
31475 };
31476
31477 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31478     
31479     /**
31480      * @cfg {String} title
31481      */   
31482     title : '',
31483     /**
31484      * @cfg {String} html
31485      */   
31486     html : '',
31487     /**
31488      * @cfg {String} bgimage
31489      */   
31490     bgimage : '',
31491     /**
31492      * @cfg {String} videourl
31493      */   
31494     videourl : '',
31495     /**
31496      * @cfg {String} cls
31497      */   
31498     cls : '',
31499     /**
31500      * @cfg {String} href
31501      */   
31502     href : '',
31503     /**
31504      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
31505      */   
31506     size : 'xs',
31507     
31508     /**
31509      * @cfg {String} placetitle (center|bottom)
31510      */   
31511     placetitle : '',
31512     
31513     /**
31514      * @cfg {Boolean} isFitContainer defalut true
31515      */   
31516     isFitContainer : true, 
31517     
31518     /**
31519      * @cfg {Boolean} preventDefault defalut false
31520      */   
31521     preventDefault : false, 
31522     
31523     /**
31524      * @cfg {Boolean} inverse defalut false
31525      */   
31526     maskInverse : false, 
31527     
31528     getAutoCreate : function()
31529     {
31530         if(!this.isFitContainer){
31531             return this.getSplitAutoCreate();
31532         }
31533         
31534         var cls = 'masonry-brick masonry-brick-full';
31535         
31536         if(this.href.length){
31537             cls += ' masonry-brick-link';
31538         }
31539         
31540         if(this.bgimage.length){
31541             cls += ' masonry-brick-image';
31542         }
31543         
31544         if(this.maskInverse){
31545             cls += ' mask-inverse';
31546         }
31547         
31548         if(!this.html.length && !this.maskInverse){
31549             cls += ' enable-mask';
31550         }
31551         
31552         if(this.size){
31553             cls += ' masonry-' + this.size + '-brick';
31554         }
31555         
31556         if(this.placetitle.length){
31557             
31558             switch (this.placetitle) {
31559                 case 'center' :
31560                     cls += ' masonry-center-title';
31561                     break;
31562                 case 'bottom' :
31563                     cls += ' masonry-bottom-title';
31564                     break;
31565                 default:
31566                     break;
31567             }
31568             
31569         } else {
31570             if(!this.html.length && !this.bgimage.length){
31571                 cls += ' masonry-center-title';
31572             }
31573
31574             if(!this.html.length && this.bgimage.length){
31575                 cls += ' masonry-bottom-title';
31576             }
31577         }
31578         
31579         if(this.cls){
31580             cls += ' ' + this.cls;
31581         }
31582         
31583         var cfg = {
31584             tag: (this.href.length) ? 'a' : 'div',
31585             cls: cls,
31586             cn: [
31587                 {
31588                     tag: 'div',
31589                     cls: 'masonry-brick-paragraph',
31590                     cn: []
31591                 }
31592             ]
31593         };
31594         
31595         if(this.href.length){
31596             cfg.href = this.href;
31597         }
31598         
31599         var cn = cfg.cn[0].cn;
31600         
31601         if(this.title.length){
31602             cn.push({
31603                 tag: 'h4',
31604                 cls: 'masonry-brick-title',
31605                 html: this.title
31606             });
31607         }
31608         
31609         if(this.html.length){
31610             cn.push({
31611                 tag: 'p',
31612                 cls: 'masonry-brick-text',
31613                 html: this.html
31614             });
31615         }  
31616         if (!this.title.length && !this.html.length) {
31617             cfg.cn[0].cls += ' hide';
31618         }
31619         
31620         if(this.bgimage.length){
31621             cfg.cn.push({
31622                 tag: 'img',
31623                 cls: 'masonry-brick-image-view',
31624                 src: this.bgimage
31625             });
31626         }
31627         
31628         if(this.videourl.length){
31629             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31630             // youtube support only?
31631             cfg.cn.push({
31632                 tag: 'iframe',
31633                 cls: 'masonry-brick-image-view',
31634                 src: vurl,
31635                 frameborder : 0,
31636                 allowfullscreen : true
31637             });
31638             
31639             
31640         }
31641         
31642         cfg.cn.push({
31643             tag: 'div',
31644             cls: 'masonry-brick-mask'
31645         });
31646         
31647         return cfg;
31648         
31649     },
31650     
31651     getSplitAutoCreate : function()
31652     {
31653         var cls = 'masonry-brick masonry-brick-split';
31654         
31655         if(this.href.length){
31656             cls += ' masonry-brick-link';
31657         }
31658         
31659         if(this.bgimage.length){
31660             cls += ' masonry-brick-image';
31661         }
31662         
31663         if(this.size){
31664             cls += ' masonry-' + this.size + '-brick';
31665         }
31666         
31667         switch (this.placetitle) {
31668             case 'center' :
31669                 cls += ' masonry-center-title';
31670                 break;
31671             case 'bottom' :
31672                 cls += ' masonry-bottom-title';
31673                 break;
31674             default:
31675                 if(!this.bgimage.length){
31676                     cls += ' masonry-center-title';
31677                 }
31678
31679                 if(this.bgimage.length){
31680                     cls += ' masonry-bottom-title';
31681                 }
31682                 break;
31683         }
31684         
31685         if(this.cls){
31686             cls += ' ' + this.cls;
31687         }
31688         
31689         var cfg = {
31690             tag: (this.href.length) ? 'a' : 'div',
31691             cls: cls,
31692             cn: [
31693                 {
31694                     tag: 'div',
31695                     cls: 'masonry-brick-split-head',
31696                     cn: [
31697                         {
31698                             tag: 'div',
31699                             cls: 'masonry-brick-paragraph',
31700                             cn: []
31701                         }
31702                     ]
31703                 },
31704                 {
31705                     tag: 'div',
31706                     cls: 'masonry-brick-split-body',
31707                     cn: []
31708                 }
31709             ]
31710         };
31711         
31712         if(this.href.length){
31713             cfg.href = this.href;
31714         }
31715         
31716         if(this.title.length){
31717             cfg.cn[0].cn[0].cn.push({
31718                 tag: 'h4',
31719                 cls: 'masonry-brick-title',
31720                 html: this.title
31721             });
31722         }
31723         
31724         if(this.html.length){
31725             cfg.cn[1].cn.push({
31726                 tag: 'p',
31727                 cls: 'masonry-brick-text',
31728                 html: this.html
31729             });
31730         }
31731
31732         if(this.bgimage.length){
31733             cfg.cn[0].cn.push({
31734                 tag: 'img',
31735                 cls: 'masonry-brick-image-view',
31736                 src: this.bgimage
31737             });
31738         }
31739         
31740         if(this.videourl.length){
31741             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31742             // youtube support only?
31743             cfg.cn[0].cn.cn.push({
31744                 tag: 'iframe',
31745                 cls: 'masonry-brick-image-view',
31746                 src: vurl,
31747                 frameborder : 0,
31748                 allowfullscreen : true
31749             });
31750         }
31751         
31752         return cfg;
31753     },
31754     
31755     initEvents: function() 
31756     {
31757         switch (this.size) {
31758             case 'xs' :
31759                 this.x = 1;
31760                 this.y = 1;
31761                 break;
31762             case 'sm' :
31763                 this.x = 2;
31764                 this.y = 2;
31765                 break;
31766             case 'md' :
31767             case 'md-left' :
31768             case 'md-right' :
31769                 this.x = 3;
31770                 this.y = 3;
31771                 break;
31772             case 'tall' :
31773                 this.x = 2;
31774                 this.y = 3;
31775                 break;
31776             case 'wide' :
31777                 this.x = 3;
31778                 this.y = 2;
31779                 break;
31780             case 'wide-thin' :
31781                 this.x = 3;
31782                 this.y = 1;
31783                 break;
31784                         
31785             default :
31786                 break;
31787         }
31788         
31789         if(Roo.isTouch){
31790             this.el.on('touchstart', this.onTouchStart, this);
31791             this.el.on('touchmove', this.onTouchMove, this);
31792             this.el.on('touchend', this.onTouchEnd, this);
31793             this.el.on('contextmenu', this.onContextMenu, this);
31794         } else {
31795             this.el.on('mouseenter'  ,this.enter, this);
31796             this.el.on('mouseleave', this.leave, this);
31797             this.el.on('click', this.onClick, this);
31798         }
31799         
31800         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31801             this.parent().bricks.push(this);   
31802         }
31803         
31804     },
31805     
31806     onClick: function(e, el)
31807     {
31808         var time = this.endTimer - this.startTimer;
31809         
31810         if(Roo.isTouch){
31811             if(time > 1000){
31812                 e.preventDefault();
31813                 return;
31814             }
31815         }
31816         
31817         if(!this.preventDefault){
31818             return;
31819         }
31820         
31821         e.preventDefault();
31822         this.fireEvent('click', this);
31823     },
31824     
31825     enter: function(e, el)
31826     {
31827         e.preventDefault();
31828         
31829         if(!this.isFitContainer || this.maskInverse){
31830             return;
31831         }
31832         
31833         if(this.bgimage.length && this.html.length){
31834             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31835         }
31836     },
31837     
31838     leave: function(e, el)
31839     {
31840         e.preventDefault();
31841         
31842         if(!this.isFitContainer || this.maskInverse){
31843             return;
31844         }
31845         
31846         if(this.bgimage.length && this.html.length){
31847             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31848         }
31849     },
31850     
31851     onTouchStart: function(e, el)
31852     {
31853 //        e.preventDefault();
31854         
31855         this.touchmoved = false;
31856         
31857         if(!this.isFitContainer){
31858             return;
31859         }
31860         
31861         if(!this.bgimage.length || !this.html.length){
31862             return;
31863         }
31864         
31865         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31866         
31867         this.timer = new Date().getTime();
31868         
31869     },
31870     
31871     onTouchMove: function(e, el)
31872     {
31873         this.touchmoved = true;
31874     },
31875     
31876     onContextMenu : function(e,el)
31877     {
31878         e.preventDefault();
31879         e.stopPropagation();
31880         return false;
31881     },
31882     
31883     onTouchEnd: function(e, el)
31884     {
31885 //        e.preventDefault();
31886         
31887         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31888         
31889             this.leave(e,el);
31890             
31891             return;
31892         }
31893         
31894         if(!this.bgimage.length || !this.html.length){
31895             
31896             if(this.href.length){
31897                 window.location.href = this.href;
31898             }
31899             
31900             return;
31901         }
31902         
31903         if(!this.isFitContainer){
31904             return;
31905         }
31906         
31907         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31908         
31909         window.location.href = this.href;
31910     }
31911     
31912 });
31913
31914  
31915
31916  /*
31917  * - LGPL
31918  *
31919  * element
31920  * 
31921  */
31922
31923 /**
31924  * @class Roo.bootstrap.Brick
31925  * @extends Roo.bootstrap.Component
31926  * Bootstrap Brick class
31927  * 
31928  * @constructor
31929  * Create a new Brick
31930  * @param {Object} config The config object
31931  */
31932
31933 Roo.bootstrap.Brick = function(config){
31934     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31935     
31936     this.addEvents({
31937         // raw events
31938         /**
31939          * @event click
31940          * When a Brick is click
31941          * @param {Roo.bootstrap.Brick} this
31942          * @param {Roo.EventObject} e
31943          */
31944         "click" : true
31945     });
31946 };
31947
31948 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31949     
31950     /**
31951      * @cfg {String} title
31952      */   
31953     title : '',
31954     /**
31955      * @cfg {String} html
31956      */   
31957     html : '',
31958     /**
31959      * @cfg {String} bgimage
31960      */   
31961     bgimage : '',
31962     /**
31963      * @cfg {String} cls
31964      */   
31965     cls : '',
31966     /**
31967      * @cfg {String} href
31968      */   
31969     href : '',
31970     /**
31971      * @cfg {String} video
31972      */   
31973     video : '',
31974     /**
31975      * @cfg {Boolean} square
31976      */   
31977     square : true,
31978     
31979     getAutoCreate : function()
31980     {
31981         var cls = 'roo-brick';
31982         
31983         if(this.href.length){
31984             cls += ' roo-brick-link';
31985         }
31986         
31987         if(this.bgimage.length){
31988             cls += ' roo-brick-image';
31989         }
31990         
31991         if(!this.html.length && !this.bgimage.length){
31992             cls += ' roo-brick-center-title';
31993         }
31994         
31995         if(!this.html.length && this.bgimage.length){
31996             cls += ' roo-brick-bottom-title';
31997         }
31998         
31999         if(this.cls){
32000             cls += ' ' + this.cls;
32001         }
32002         
32003         var cfg = {
32004             tag: (this.href.length) ? 'a' : 'div',
32005             cls: cls,
32006             cn: [
32007                 {
32008                     tag: 'div',
32009                     cls: 'roo-brick-paragraph',
32010                     cn: []
32011                 }
32012             ]
32013         };
32014         
32015         if(this.href.length){
32016             cfg.href = this.href;
32017         }
32018         
32019         var cn = cfg.cn[0].cn;
32020         
32021         if(this.title.length){
32022             cn.push({
32023                 tag: 'h4',
32024                 cls: 'roo-brick-title',
32025                 html: this.title
32026             });
32027         }
32028         
32029         if(this.html.length){
32030             cn.push({
32031                 tag: 'p',
32032                 cls: 'roo-brick-text',
32033                 html: this.html
32034             });
32035         } else {
32036             cn.cls += ' hide';
32037         }
32038         
32039         if(this.bgimage.length){
32040             cfg.cn.push({
32041                 tag: 'img',
32042                 cls: 'roo-brick-image-view',
32043                 src: this.bgimage
32044             });
32045         }
32046         
32047         return cfg;
32048     },
32049     
32050     initEvents: function() 
32051     {
32052         if(this.title.length || this.html.length){
32053             this.el.on('mouseenter'  ,this.enter, this);
32054             this.el.on('mouseleave', this.leave, this);
32055         }
32056         
32057         
32058         Roo.EventManager.onWindowResize(this.resize, this); 
32059         
32060         this.resize();
32061     },
32062     
32063     resize : function()
32064     {
32065         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32066         
32067         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32068         
32069         if(this.bgimage.length){
32070             var image = this.el.select('.roo-brick-image-view', true).first();
32071             image.setWidth(paragraph.getWidth());
32072             image.setHeight(paragraph.getWidth());
32073             
32074             this.el.setHeight(paragraph.getWidth());
32075             
32076         }
32077         
32078     },
32079     
32080     enter: function(e, el)
32081     {
32082         e.preventDefault();
32083         
32084         if(this.bgimage.length){
32085             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32086             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32087         }
32088     },
32089     
32090     leave: function(e, el)
32091     {
32092         e.preventDefault();
32093         
32094         if(this.bgimage.length){
32095             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32096             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32097         }
32098     }
32099     
32100 });
32101
32102  
32103
32104  /*
32105  * - LGPL
32106  *
32107  * Input
32108  * 
32109  */
32110
32111 /**
32112  * @class Roo.bootstrap.NumberField
32113  * @extends Roo.bootstrap.Input
32114  * Bootstrap NumberField class
32115  * 
32116  * 
32117  * 
32118  * 
32119  * @constructor
32120  * Create a new NumberField
32121  * @param {Object} config The config object
32122  */
32123
32124 Roo.bootstrap.NumberField = function(config){
32125     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32126 };
32127
32128 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32129     
32130     /**
32131      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32132      */
32133     allowDecimals : true,
32134     /**
32135      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32136      */
32137     decimalSeparator : ".",
32138     /**
32139      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32140      */
32141     decimalPrecision : 2,
32142     /**
32143      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32144      */
32145     allowNegative : true,
32146     /**
32147      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32148      */
32149     minValue : Number.NEGATIVE_INFINITY,
32150     /**
32151      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32152      */
32153     maxValue : Number.MAX_VALUE,
32154     /**
32155      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32156      */
32157     minText : "The minimum value for this field is {0}",
32158     /**
32159      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32160      */
32161     maxText : "The maximum value for this field is {0}",
32162     /**
32163      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32164      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32165      */
32166     nanText : "{0} is not a valid number",
32167     /**
32168      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32169      */
32170     castInt : true,
32171
32172     // private
32173     initEvents : function()
32174     {   
32175         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32176         
32177         var allowed = "0123456789";
32178         
32179         if(this.allowDecimals){
32180             allowed += this.decimalSeparator;
32181         }
32182         
32183         if(this.allowNegative){
32184             allowed += "-";
32185         }
32186         
32187         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32188         
32189         var keyPress = function(e){
32190             
32191             var k = e.getKey();
32192             
32193             var c = e.getCharCode();
32194             
32195             if(
32196                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32197                     allowed.indexOf(String.fromCharCode(c)) === -1
32198             ){
32199                 e.stopEvent();
32200                 return;
32201             }
32202             
32203             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32204                 return;
32205             }
32206             
32207             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32208                 e.stopEvent();
32209             }
32210         };
32211         
32212         this.el.on("keypress", keyPress, this);
32213     },
32214     
32215     validateValue : function(value)
32216     {
32217         
32218         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32219             return false;
32220         }
32221         
32222         var num = this.parseValue(value);
32223         
32224         if(isNaN(num)){
32225             this.markInvalid(String.format(this.nanText, value));
32226             return false;
32227         }
32228         
32229         if(num < this.minValue){
32230             this.markInvalid(String.format(this.minText, this.minValue));
32231             return false;
32232         }
32233         
32234         if(num > this.maxValue){
32235             this.markInvalid(String.format(this.maxText, this.maxValue));
32236             return false;
32237         }
32238         
32239         return true;
32240     },
32241
32242     getValue : function()
32243     {
32244         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32245     },
32246
32247     parseValue : function(value)
32248     {
32249         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32250         return isNaN(value) ? '' : value;
32251     },
32252
32253     fixPrecision : function(value)
32254     {
32255         var nan = isNaN(value);
32256         
32257         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32258             return nan ? '' : value;
32259         }
32260         return parseFloat(value).toFixed(this.decimalPrecision);
32261     },
32262
32263     setValue : function(v)
32264     {
32265         v = this.fixPrecision(v);
32266         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32267     },
32268
32269     decimalPrecisionFcn : function(v)
32270     {
32271         return Math.floor(v);
32272     },
32273
32274     beforeBlur : function()
32275     {
32276         if(!this.castInt){
32277             return;
32278         }
32279         
32280         var v = this.parseValue(this.getRawValue());
32281         if(v){
32282             this.setValue(v);
32283         }
32284     }
32285     
32286 });
32287
32288  
32289
32290 /*
32291 * Licence: LGPL
32292 */
32293
32294 /**
32295  * @class Roo.bootstrap.DocumentSlider
32296  * @extends Roo.bootstrap.Component
32297  * Bootstrap DocumentSlider class
32298  * 
32299  * @constructor
32300  * Create a new DocumentViewer
32301  * @param {Object} config The config object
32302  */
32303
32304 Roo.bootstrap.DocumentSlider = function(config){
32305     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32306     
32307     this.files = [];
32308     
32309     this.addEvents({
32310         /**
32311          * @event initial
32312          * Fire after initEvent
32313          * @param {Roo.bootstrap.DocumentSlider} this
32314          */
32315         "initial" : true,
32316         /**
32317          * @event update
32318          * Fire after update
32319          * @param {Roo.bootstrap.DocumentSlider} this
32320          */
32321         "update" : true,
32322         /**
32323          * @event click
32324          * Fire after click
32325          * @param {Roo.bootstrap.DocumentSlider} this
32326          */
32327         "click" : true
32328     });
32329 };
32330
32331 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32332     
32333     files : false,
32334     
32335     indicator : 0,
32336     
32337     getAutoCreate : function()
32338     {
32339         var cfg = {
32340             tag : 'div',
32341             cls : 'roo-document-slider',
32342             cn : [
32343                 {
32344                     tag : 'div',
32345                     cls : 'roo-document-slider-header',
32346                     cn : [
32347                         {
32348                             tag : 'div',
32349                             cls : 'roo-document-slider-header-title'
32350                         }
32351                     ]
32352                 },
32353                 {
32354                     tag : 'div',
32355                     cls : 'roo-document-slider-body',
32356                     cn : [
32357                         {
32358                             tag : 'div',
32359                             cls : 'roo-document-slider-prev',
32360                             cn : [
32361                                 {
32362                                     tag : 'i',
32363                                     cls : 'fa fa-chevron-left'
32364                                 }
32365                             ]
32366                         },
32367                         {
32368                             tag : 'div',
32369                             cls : 'roo-document-slider-thumb',
32370                             cn : [
32371                                 {
32372                                     tag : 'img',
32373                                     cls : 'roo-document-slider-image'
32374                                 }
32375                             ]
32376                         },
32377                         {
32378                             tag : 'div',
32379                             cls : 'roo-document-slider-next',
32380                             cn : [
32381                                 {
32382                                     tag : 'i',
32383                                     cls : 'fa fa-chevron-right'
32384                                 }
32385                             ]
32386                         }
32387                     ]
32388                 }
32389             ]
32390         };
32391         
32392         return cfg;
32393     },
32394     
32395     initEvents : function()
32396     {
32397         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32398         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32399         
32400         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32401         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32402         
32403         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32404         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32405         
32406         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32407         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32408         
32409         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32410         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32411         
32412         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32413         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32414         
32415         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32416         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32417         
32418         this.thumbEl.on('click', this.onClick, this);
32419         
32420         this.prevIndicator.on('click', this.prev, this);
32421         
32422         this.nextIndicator.on('click', this.next, this);
32423         
32424     },
32425     
32426     initial : function()
32427     {
32428         if(this.files.length){
32429             this.indicator = 1;
32430             this.update()
32431         }
32432         
32433         this.fireEvent('initial', this);
32434     },
32435     
32436     update : function()
32437     {
32438         this.imageEl.attr('src', this.files[this.indicator - 1]);
32439         
32440         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32441         
32442         this.prevIndicator.show();
32443         
32444         if(this.indicator == 1){
32445             this.prevIndicator.hide();
32446         }
32447         
32448         this.nextIndicator.show();
32449         
32450         if(this.indicator == this.files.length){
32451             this.nextIndicator.hide();
32452         }
32453         
32454         this.thumbEl.scrollTo('top');
32455         
32456         this.fireEvent('update', this);
32457     },
32458     
32459     onClick : function(e)
32460     {
32461         e.preventDefault();
32462         
32463         this.fireEvent('click', this);
32464     },
32465     
32466     prev : function(e)
32467     {
32468         e.preventDefault();
32469         
32470         this.indicator = Math.max(1, this.indicator - 1);
32471         
32472         this.update();
32473     },
32474     
32475     next : function(e)
32476     {
32477         e.preventDefault();
32478         
32479         this.indicator = Math.min(this.files.length, this.indicator + 1);
32480         
32481         this.update();
32482     }
32483 });
32484 /*
32485  * - LGPL
32486  *
32487  * RadioSet
32488  *
32489  *
32490  */
32491
32492 /**
32493  * @class Roo.bootstrap.RadioSet
32494  * @extends Roo.bootstrap.Input
32495  * Bootstrap RadioSet class
32496  * @cfg {String} indicatorpos (left|right) default left
32497  * @cfg {Boolean} inline (true|false) inline the element (default true)
32498  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32499  * @constructor
32500  * Create a new RadioSet
32501  * @param {Object} config The config object
32502  */
32503
32504 Roo.bootstrap.RadioSet = function(config){
32505     
32506     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
32507     
32508     this.radioes = [];
32509     
32510     Roo.bootstrap.RadioSet.register(this);
32511     
32512     this.addEvents({
32513         /**
32514         * @event check
32515         * Fires when the element is checked or unchecked.
32516         * @param {Roo.bootstrap.RadioSet} this This radio
32517         * @param {Roo.bootstrap.Radio} item The checked item
32518         */
32519        check : true
32520     });
32521     
32522 };
32523
32524 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32525
32526     radioes : false,
32527     
32528     inline : true,
32529     
32530     weight : '',
32531     
32532     indicatorpos : 'left',
32533     
32534     getAutoCreate : function()
32535     {
32536         var label = {
32537             tag : 'label',
32538             cls : 'roo-radio-set-label',
32539             cn : [
32540                 {
32541                     tag : 'span',
32542                     html : this.fieldLabel
32543                 }
32544             ]
32545         };
32546         
32547         if(this.indicatorpos == 'left'){
32548             label.cn.unshift({
32549                 tag : 'i',
32550                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32551                 tooltip : 'This field is required'
32552             });
32553         } else {
32554             label.cn.push({
32555                 tag : 'i',
32556                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32557                 tooltip : 'This field is required'
32558             });
32559         }
32560         
32561         var items = {
32562             tag : 'div',
32563             cls : 'roo-radio-set-items'
32564         };
32565         
32566         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32567         
32568         if (align === 'left' && this.fieldLabel.length) {
32569             
32570             items = {
32571                 cls : "roo-radio-set-right", 
32572                 cn: [
32573                     items
32574                 ]
32575             };
32576             
32577             if(this.labelWidth > 12){
32578                 label.style = "width: " + this.labelWidth + 'px';
32579             }
32580             
32581             if(this.labelWidth < 13 && this.labelmd == 0){
32582                 this.labelmd = this.labelWidth;
32583             }
32584             
32585             if(this.labellg > 0){
32586                 label.cls += ' col-lg-' + this.labellg;
32587                 items.cls += ' col-lg-' + (12 - this.labellg);
32588             }
32589             
32590             if(this.labelmd > 0){
32591                 label.cls += ' col-md-' + this.labelmd;
32592                 items.cls += ' col-md-' + (12 - this.labelmd);
32593             }
32594             
32595             if(this.labelsm > 0){
32596                 label.cls += ' col-sm-' + this.labelsm;
32597                 items.cls += ' col-sm-' + (12 - this.labelsm);
32598             }
32599             
32600             if(this.labelxs > 0){
32601                 label.cls += ' col-xs-' + this.labelxs;
32602                 items.cls += ' col-xs-' + (12 - this.labelxs);
32603             }
32604         }
32605         
32606         var cfg = {
32607             tag : 'div',
32608             cls : 'roo-radio-set',
32609             cn : [
32610                 {
32611                     tag : 'input',
32612                     cls : 'roo-radio-set-input',
32613                     type : 'hidden',
32614                     name : this.name,
32615                     value : this.value ? this.value :  ''
32616                 },
32617                 label,
32618                 items
32619             ]
32620         };
32621         
32622         if(this.weight.length){
32623             cfg.cls += ' roo-radio-' + this.weight;
32624         }
32625         
32626         if(this.inline) {
32627             cfg.cls += ' roo-radio-set-inline';
32628         }
32629         
32630         return cfg;
32631         
32632     },
32633
32634     initEvents : function()
32635     {
32636         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32637         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32638         
32639         if(!this.fieldLabel.length){
32640             this.labelEl.hide();
32641         }
32642         
32643         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32644         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32645         
32646         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
32647         this.indicatorEl().hide();
32648         
32649         this.originalValue = this.getValue();
32650         
32651     },
32652     
32653     inputEl: function ()
32654     {
32655         return this.el.select('.roo-radio-set-input', true).first();
32656     },
32657     
32658     getChildContainer : function()
32659     {
32660         return this.itemsEl;
32661     },
32662     
32663     register : function(item)
32664     {
32665         this.radioes.push(item);
32666         
32667     },
32668     
32669     validate : function()
32670     {   
32671         var valid = false;
32672         
32673         Roo.each(this.radioes, function(i){
32674             if(!i.checked){
32675                 return;
32676             }
32677             
32678             valid = true;
32679             return false;
32680         });
32681         
32682         if(this.allowBlank) {
32683             return true;
32684         }
32685         
32686         if(this.disabled || valid){
32687             this.markValid();
32688             return true;
32689         }
32690         
32691         this.markInvalid();
32692         return false;
32693         
32694     },
32695     
32696     markValid : function()
32697     {
32698         if(this.labelEl.isVisible(true)){
32699             this.indicatorEl().hide();
32700         }
32701         
32702         this.el.removeClass([this.invalidClass, this.validClass]);
32703         this.el.addClass(this.validClass);
32704         
32705         this.fireEvent('valid', this);
32706     },
32707     
32708     markInvalid : function(msg)
32709     {
32710         if(this.allowBlank || this.disabled){
32711             return;
32712         }
32713         
32714         if(this.labelEl.isVisible(true)){
32715             this.indicatorEl().show();
32716         }
32717         
32718         this.el.removeClass([this.invalidClass, this.validClass]);
32719         this.el.addClass(this.invalidClass);
32720         
32721         this.fireEvent('invalid', this, msg);
32722         
32723     },
32724     
32725     setValue : function(v, suppressEvent)
32726     {   
32727         Roo.each(this.radioes, function(i){
32728             
32729             i.checked = false;
32730             i.el.removeClass('checked');
32731             
32732             if(i.value === v || i.value.toString() === v.toString()){
32733                 i.checked = true;
32734                 i.el.addClass('checked');
32735                 
32736                 if(suppressEvent !== true){
32737                     this.fireEvent('check', this, i);
32738                 }
32739             }
32740             
32741         }, this);
32742         
32743         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32744         
32745     },
32746     
32747     clearInvalid : function(){
32748         
32749         if(!this.el || this.preventMark){
32750             return;
32751         }
32752         
32753         if(this.labelEl.isVisible(true)){
32754             this.indicatorEl().hide();
32755         }
32756         
32757         this.el.removeClass([this.invalidClass]);
32758         
32759         this.fireEvent('valid', this);
32760     }
32761     
32762 });
32763
32764 Roo.apply(Roo.bootstrap.RadioSet, {
32765     
32766     groups: {},
32767     
32768     register : function(set)
32769     {
32770         this.groups[set.name] = set;
32771     },
32772     
32773     get: function(name) 
32774     {
32775         if (typeof(this.groups[name]) == 'undefined') {
32776             return false;
32777         }
32778         
32779         return this.groups[name] ;
32780     }
32781     
32782 });
32783 /*
32784  * Based on:
32785  * Ext JS Library 1.1.1
32786  * Copyright(c) 2006-2007, Ext JS, LLC.
32787  *
32788  * Originally Released Under LGPL - original licence link has changed is not relivant.
32789  *
32790  * Fork - LGPL
32791  * <script type="text/javascript">
32792  */
32793
32794
32795 /**
32796  * @class Roo.bootstrap.SplitBar
32797  * @extends Roo.util.Observable
32798  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32799  * <br><br>
32800  * Usage:
32801  * <pre><code>
32802 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32803                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32804 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32805 split.minSize = 100;
32806 split.maxSize = 600;
32807 split.animate = true;
32808 split.on('moved', splitterMoved);
32809 </code></pre>
32810  * @constructor
32811  * Create a new SplitBar
32812  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32813  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32814  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32815  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32816                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32817                         position of the SplitBar).
32818  */
32819 Roo.bootstrap.SplitBar = function(cfg){
32820     
32821     /** @private */
32822     
32823     //{
32824     //  dragElement : elm
32825     //  resizingElement: el,
32826         // optional..
32827     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32828     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32829         // existingProxy ???
32830     //}
32831     
32832     this.el = Roo.get(cfg.dragElement, true);
32833     this.el.dom.unselectable = "on";
32834     /** @private */
32835     this.resizingEl = Roo.get(cfg.resizingElement, true);
32836
32837     /**
32838      * @private
32839      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32840      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32841      * @type Number
32842      */
32843     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32844     
32845     /**
32846      * The minimum size of the resizing element. (Defaults to 0)
32847      * @type Number
32848      */
32849     this.minSize = 0;
32850     
32851     /**
32852      * The maximum size of the resizing element. (Defaults to 2000)
32853      * @type Number
32854      */
32855     this.maxSize = 2000;
32856     
32857     /**
32858      * Whether to animate the transition to the new size
32859      * @type Boolean
32860      */
32861     this.animate = false;
32862     
32863     /**
32864      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32865      * @type Boolean
32866      */
32867     this.useShim = false;
32868     
32869     /** @private */
32870     this.shim = null;
32871     
32872     if(!cfg.existingProxy){
32873         /** @private */
32874         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32875     }else{
32876         this.proxy = Roo.get(cfg.existingProxy).dom;
32877     }
32878     /** @private */
32879     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32880     
32881     /** @private */
32882     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32883     
32884     /** @private */
32885     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32886     
32887     /** @private */
32888     this.dragSpecs = {};
32889     
32890     /**
32891      * @private The adapter to use to positon and resize elements
32892      */
32893     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32894     this.adapter.init(this);
32895     
32896     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32897         /** @private */
32898         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32899         this.el.addClass("roo-splitbar-h");
32900     }else{
32901         /** @private */
32902         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32903         this.el.addClass("roo-splitbar-v");
32904     }
32905     
32906     this.addEvents({
32907         /**
32908          * @event resize
32909          * Fires when the splitter is moved (alias for {@link #event-moved})
32910          * @param {Roo.bootstrap.SplitBar} this
32911          * @param {Number} newSize the new width or height
32912          */
32913         "resize" : true,
32914         /**
32915          * @event moved
32916          * Fires when the splitter is moved
32917          * @param {Roo.bootstrap.SplitBar} this
32918          * @param {Number} newSize the new width or height
32919          */
32920         "moved" : true,
32921         /**
32922          * @event beforeresize
32923          * Fires before the splitter is dragged
32924          * @param {Roo.bootstrap.SplitBar} this
32925          */
32926         "beforeresize" : true,
32927
32928         "beforeapply" : true
32929     });
32930
32931     Roo.util.Observable.call(this);
32932 };
32933
32934 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32935     onStartProxyDrag : function(x, y){
32936         this.fireEvent("beforeresize", this);
32937         if(!this.overlay){
32938             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32939             o.unselectable();
32940             o.enableDisplayMode("block");
32941             // all splitbars share the same overlay
32942             Roo.bootstrap.SplitBar.prototype.overlay = o;
32943         }
32944         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32945         this.overlay.show();
32946         Roo.get(this.proxy).setDisplayed("block");
32947         var size = this.adapter.getElementSize(this);
32948         this.activeMinSize = this.getMinimumSize();;
32949         this.activeMaxSize = this.getMaximumSize();;
32950         var c1 = size - this.activeMinSize;
32951         var c2 = Math.max(this.activeMaxSize - size, 0);
32952         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32953             this.dd.resetConstraints();
32954             this.dd.setXConstraint(
32955                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32956                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32957             );
32958             this.dd.setYConstraint(0, 0);
32959         }else{
32960             this.dd.resetConstraints();
32961             this.dd.setXConstraint(0, 0);
32962             this.dd.setYConstraint(
32963                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32964                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32965             );
32966          }
32967         this.dragSpecs.startSize = size;
32968         this.dragSpecs.startPoint = [x, y];
32969         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32970     },
32971     
32972     /** 
32973      * @private Called after the drag operation by the DDProxy
32974      */
32975     onEndProxyDrag : function(e){
32976         Roo.get(this.proxy).setDisplayed(false);
32977         var endPoint = Roo.lib.Event.getXY(e);
32978         if(this.overlay){
32979             this.overlay.hide();
32980         }
32981         var newSize;
32982         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32983             newSize = this.dragSpecs.startSize + 
32984                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32985                     endPoint[0] - this.dragSpecs.startPoint[0] :
32986                     this.dragSpecs.startPoint[0] - endPoint[0]
32987                 );
32988         }else{
32989             newSize = this.dragSpecs.startSize + 
32990                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32991                     endPoint[1] - this.dragSpecs.startPoint[1] :
32992                     this.dragSpecs.startPoint[1] - endPoint[1]
32993                 );
32994         }
32995         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32996         if(newSize != this.dragSpecs.startSize){
32997             if(this.fireEvent('beforeapply', this, newSize) !== false){
32998                 this.adapter.setElementSize(this, newSize);
32999                 this.fireEvent("moved", this, newSize);
33000                 this.fireEvent("resize", this, newSize);
33001             }
33002         }
33003     },
33004     
33005     /**
33006      * Get the adapter this SplitBar uses
33007      * @return The adapter object
33008      */
33009     getAdapter : function(){
33010         return this.adapter;
33011     },
33012     
33013     /**
33014      * Set the adapter this SplitBar uses
33015      * @param {Object} adapter A SplitBar adapter object
33016      */
33017     setAdapter : function(adapter){
33018         this.adapter = adapter;
33019         this.adapter.init(this);
33020     },
33021     
33022     /**
33023      * Gets the minimum size for the resizing element
33024      * @return {Number} The minimum size
33025      */
33026     getMinimumSize : function(){
33027         return this.minSize;
33028     },
33029     
33030     /**
33031      * Sets the minimum size for the resizing element
33032      * @param {Number} minSize The minimum size
33033      */
33034     setMinimumSize : function(minSize){
33035         this.minSize = minSize;
33036     },
33037     
33038     /**
33039      * Gets the maximum size for the resizing element
33040      * @return {Number} The maximum size
33041      */
33042     getMaximumSize : function(){
33043         return this.maxSize;
33044     },
33045     
33046     /**
33047      * Sets the maximum size for the resizing element
33048      * @param {Number} maxSize The maximum size
33049      */
33050     setMaximumSize : function(maxSize){
33051         this.maxSize = maxSize;
33052     },
33053     
33054     /**
33055      * Sets the initialize size for the resizing element
33056      * @param {Number} size The initial size
33057      */
33058     setCurrentSize : function(size){
33059         var oldAnimate = this.animate;
33060         this.animate = false;
33061         this.adapter.setElementSize(this, size);
33062         this.animate = oldAnimate;
33063     },
33064     
33065     /**
33066      * Destroy this splitbar. 
33067      * @param {Boolean} removeEl True to remove the element
33068      */
33069     destroy : function(removeEl){
33070         if(this.shim){
33071             this.shim.remove();
33072         }
33073         this.dd.unreg();
33074         this.proxy.parentNode.removeChild(this.proxy);
33075         if(removeEl){
33076             this.el.remove();
33077         }
33078     }
33079 });
33080
33081 /**
33082  * @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.
33083  */
33084 Roo.bootstrap.SplitBar.createProxy = function(dir){
33085     var proxy = new Roo.Element(document.createElement("div"));
33086     proxy.unselectable();
33087     var cls = 'roo-splitbar-proxy';
33088     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33089     document.body.appendChild(proxy.dom);
33090     return proxy.dom;
33091 };
33092
33093 /** 
33094  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33095  * Default Adapter. It assumes the splitter and resizing element are not positioned
33096  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33097  */
33098 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33099 };
33100
33101 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33102     // do nothing for now
33103     init : function(s){
33104     
33105     },
33106     /**
33107      * Called before drag operations to get the current size of the resizing element. 
33108      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33109      */
33110      getElementSize : function(s){
33111         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33112             return s.resizingEl.getWidth();
33113         }else{
33114             return s.resizingEl.getHeight();
33115         }
33116     },
33117     
33118     /**
33119      * Called after drag operations to set the size of the resizing element.
33120      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33121      * @param {Number} newSize The new size to set
33122      * @param {Function} onComplete A function to be invoked when resizing is complete
33123      */
33124     setElementSize : function(s, newSize, onComplete){
33125         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33126             if(!s.animate){
33127                 s.resizingEl.setWidth(newSize);
33128                 if(onComplete){
33129                     onComplete(s, newSize);
33130                 }
33131             }else{
33132                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33133             }
33134         }else{
33135             
33136             if(!s.animate){
33137                 s.resizingEl.setHeight(newSize);
33138                 if(onComplete){
33139                     onComplete(s, newSize);
33140                 }
33141             }else{
33142                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33143             }
33144         }
33145     }
33146 };
33147
33148 /** 
33149  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33150  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33151  * Adapter that  moves the splitter element to align with the resized sizing element. 
33152  * Used with an absolute positioned SplitBar.
33153  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33154  * document.body, make sure you assign an id to the body element.
33155  */
33156 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33157     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33158     this.container = Roo.get(container);
33159 };
33160
33161 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33162     init : function(s){
33163         this.basic.init(s);
33164     },
33165     
33166     getElementSize : function(s){
33167         return this.basic.getElementSize(s);
33168     },
33169     
33170     setElementSize : function(s, newSize, onComplete){
33171         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33172     },
33173     
33174     moveSplitter : function(s){
33175         var yes = Roo.bootstrap.SplitBar;
33176         switch(s.placement){
33177             case yes.LEFT:
33178                 s.el.setX(s.resizingEl.getRight());
33179                 break;
33180             case yes.RIGHT:
33181                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33182                 break;
33183             case yes.TOP:
33184                 s.el.setY(s.resizingEl.getBottom());
33185                 break;
33186             case yes.BOTTOM:
33187                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33188                 break;
33189         }
33190     }
33191 };
33192
33193 /**
33194  * Orientation constant - Create a vertical SplitBar
33195  * @static
33196  * @type Number
33197  */
33198 Roo.bootstrap.SplitBar.VERTICAL = 1;
33199
33200 /**
33201  * Orientation constant - Create a horizontal SplitBar
33202  * @static
33203  * @type Number
33204  */
33205 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33206
33207 /**
33208  * Placement constant - The resizing element is to the left of the splitter element
33209  * @static
33210  * @type Number
33211  */
33212 Roo.bootstrap.SplitBar.LEFT = 1;
33213
33214 /**
33215  * Placement constant - The resizing element is to the right of the splitter element
33216  * @static
33217  * @type Number
33218  */
33219 Roo.bootstrap.SplitBar.RIGHT = 2;
33220
33221 /**
33222  * Placement constant - The resizing element is positioned above the splitter element
33223  * @static
33224  * @type Number
33225  */
33226 Roo.bootstrap.SplitBar.TOP = 3;
33227
33228 /**
33229  * Placement constant - The resizing element is positioned under splitter element
33230  * @static
33231  * @type Number
33232  */
33233 Roo.bootstrap.SplitBar.BOTTOM = 4;
33234 Roo.namespace("Roo.bootstrap.layout");/*
33235  * Based on:
33236  * Ext JS Library 1.1.1
33237  * Copyright(c) 2006-2007, Ext JS, LLC.
33238  *
33239  * Originally Released Under LGPL - original licence link has changed is not relivant.
33240  *
33241  * Fork - LGPL
33242  * <script type="text/javascript">
33243  */
33244
33245 /**
33246  * @class Roo.bootstrap.layout.Manager
33247  * @extends Roo.bootstrap.Component
33248  * Base class for layout managers.
33249  */
33250 Roo.bootstrap.layout.Manager = function(config)
33251 {
33252     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33253
33254
33255
33256
33257
33258     /** false to disable window resize monitoring @type Boolean */
33259     this.monitorWindowResize = true;
33260     this.regions = {};
33261     this.addEvents({
33262         /**
33263          * @event layout
33264          * Fires when a layout is performed.
33265          * @param {Roo.LayoutManager} this
33266          */
33267         "layout" : true,
33268         /**
33269          * @event regionresized
33270          * Fires when the user resizes a region.
33271          * @param {Roo.LayoutRegion} region The resized region
33272          * @param {Number} newSize The new size (width for east/west, height for north/south)
33273          */
33274         "regionresized" : true,
33275         /**
33276          * @event regioncollapsed
33277          * Fires when a region is collapsed.
33278          * @param {Roo.LayoutRegion} region The collapsed region
33279          */
33280         "regioncollapsed" : true,
33281         /**
33282          * @event regionexpanded
33283          * Fires when a region is expanded.
33284          * @param {Roo.LayoutRegion} region The expanded region
33285          */
33286         "regionexpanded" : true
33287     });
33288     this.updating = false;
33289
33290     if (config.el) {
33291         this.el = Roo.get(config.el);
33292         this.initEvents();
33293     }
33294
33295 };
33296
33297 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33298
33299
33300     regions : null,
33301
33302     monitorWindowResize : true,
33303
33304
33305     updating : false,
33306
33307
33308     onRender : function(ct, position)
33309     {
33310         if(!this.el){
33311             this.el = Roo.get(ct);
33312             this.initEvents();
33313         }
33314         //this.fireEvent('render',this);
33315     },
33316
33317
33318     initEvents: function()
33319     {
33320
33321
33322         // ie scrollbar fix
33323         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33324             document.body.scroll = "no";
33325         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33326             this.el.position('relative');
33327         }
33328         this.id = this.el.id;
33329         this.el.addClass("roo-layout-container");
33330         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33331         if(this.el.dom != document.body ) {
33332             this.el.on('resize', this.layout,this);
33333             this.el.on('show', this.layout,this);
33334         }
33335
33336     },
33337
33338     /**
33339      * Returns true if this layout is currently being updated
33340      * @return {Boolean}
33341      */
33342     isUpdating : function(){
33343         return this.updating;
33344     },
33345
33346     /**
33347      * Suspend the LayoutManager from doing auto-layouts while
33348      * making multiple add or remove calls
33349      */
33350     beginUpdate : function(){
33351         this.updating = true;
33352     },
33353
33354     /**
33355      * Restore auto-layouts and optionally disable the manager from performing a layout
33356      * @param {Boolean} noLayout true to disable a layout update
33357      */
33358     endUpdate : function(noLayout){
33359         this.updating = false;
33360         if(!noLayout){
33361             this.layout();
33362         }
33363     },
33364
33365     layout: function(){
33366         // abstract...
33367     },
33368
33369     onRegionResized : function(region, newSize){
33370         this.fireEvent("regionresized", region, newSize);
33371         this.layout();
33372     },
33373
33374     onRegionCollapsed : function(region){
33375         this.fireEvent("regioncollapsed", region);
33376     },
33377
33378     onRegionExpanded : function(region){
33379         this.fireEvent("regionexpanded", region);
33380     },
33381
33382     /**
33383      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33384      * performs box-model adjustments.
33385      * @return {Object} The size as an object {width: (the width), height: (the height)}
33386      */
33387     getViewSize : function()
33388     {
33389         var size;
33390         if(this.el.dom != document.body){
33391             size = this.el.getSize();
33392         }else{
33393             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33394         }
33395         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33396         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33397         return size;
33398     },
33399
33400     /**
33401      * Returns the Element this layout is bound to.
33402      * @return {Roo.Element}
33403      */
33404     getEl : function(){
33405         return this.el;
33406     },
33407
33408     /**
33409      * Returns the specified region.
33410      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33411      * @return {Roo.LayoutRegion}
33412      */
33413     getRegion : function(target){
33414         return this.regions[target.toLowerCase()];
33415     },
33416
33417     onWindowResize : function(){
33418         if(this.monitorWindowResize){
33419             this.layout();
33420         }
33421     }
33422 });
33423 /*
33424  * Based on:
33425  * Ext JS Library 1.1.1
33426  * Copyright(c) 2006-2007, Ext JS, LLC.
33427  *
33428  * Originally Released Under LGPL - original licence link has changed is not relivant.
33429  *
33430  * Fork - LGPL
33431  * <script type="text/javascript">
33432  */
33433 /**
33434  * @class Roo.bootstrap.layout.Border
33435  * @extends Roo.bootstrap.layout.Manager
33436  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33437  * please see: examples/bootstrap/nested.html<br><br>
33438  
33439 <b>The container the layout is rendered into can be either the body element or any other element.
33440 If it is not the body element, the container needs to either be an absolute positioned element,
33441 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33442 the container size if it is not the body element.</b>
33443
33444 * @constructor
33445 * Create a new Border
33446 * @param {Object} config Configuration options
33447  */
33448 Roo.bootstrap.layout.Border = function(config){
33449     config = config || {};
33450     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
33451     
33452     
33453     
33454     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33455         if(config[region]){
33456             config[region].region = region;
33457             this.addRegion(config[region]);
33458         }
33459     },this);
33460     
33461 };
33462
33463 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
33464
33465 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
33466     /**
33467      * Creates and adds a new region if it doesn't already exist.
33468      * @param {String} target The target region key (north, south, east, west or center).
33469      * @param {Object} config The regions config object
33470      * @return {BorderLayoutRegion} The new region
33471      */
33472     addRegion : function(config)
33473     {
33474         if(!this.regions[config.region]){
33475             var r = this.factory(config);
33476             this.bindRegion(r);
33477         }
33478         return this.regions[config.region];
33479     },
33480
33481     // private (kinda)
33482     bindRegion : function(r){
33483         this.regions[r.config.region] = r;
33484         
33485         r.on("visibilitychange",    this.layout, this);
33486         r.on("paneladded",          this.layout, this);
33487         r.on("panelremoved",        this.layout, this);
33488         r.on("invalidated",         this.layout, this);
33489         r.on("resized",             this.onRegionResized, this);
33490         r.on("collapsed",           this.onRegionCollapsed, this);
33491         r.on("expanded",            this.onRegionExpanded, this);
33492     },
33493
33494     /**
33495      * Performs a layout update.
33496      */
33497     layout : function()
33498     {
33499         if(this.updating) {
33500             return;
33501         }
33502         
33503         // render all the rebions if they have not been done alreayd?
33504         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33505             if(this.regions[region] && !this.regions[region].bodyEl){
33506                 this.regions[region].onRender(this.el)
33507             }
33508         },this);
33509         
33510         var size = this.getViewSize();
33511         var w = size.width;
33512         var h = size.height;
33513         var centerW = w;
33514         var centerH = h;
33515         var centerY = 0;
33516         var centerX = 0;
33517         //var x = 0, y = 0;
33518
33519         var rs = this.regions;
33520         var north = rs["north"];
33521         var south = rs["south"]; 
33522         var west = rs["west"];
33523         var east = rs["east"];
33524         var center = rs["center"];
33525         //if(this.hideOnLayout){ // not supported anymore
33526             //c.el.setStyle("display", "none");
33527         //}
33528         if(north && north.isVisible()){
33529             var b = north.getBox();
33530             var m = north.getMargins();
33531             b.width = w - (m.left+m.right);
33532             b.x = m.left;
33533             b.y = m.top;
33534             centerY = b.height + b.y + m.bottom;
33535             centerH -= centerY;
33536             north.updateBox(this.safeBox(b));
33537         }
33538         if(south && south.isVisible()){
33539             var b = south.getBox();
33540             var m = south.getMargins();
33541             b.width = w - (m.left+m.right);
33542             b.x = m.left;
33543             var totalHeight = (b.height + m.top + m.bottom);
33544             b.y = h - totalHeight + m.top;
33545             centerH -= totalHeight;
33546             south.updateBox(this.safeBox(b));
33547         }
33548         if(west && west.isVisible()){
33549             var b = west.getBox();
33550             var m = west.getMargins();
33551             b.height = centerH - (m.top+m.bottom);
33552             b.x = m.left;
33553             b.y = centerY + m.top;
33554             var totalWidth = (b.width + m.left + m.right);
33555             centerX += totalWidth;
33556             centerW -= totalWidth;
33557             west.updateBox(this.safeBox(b));
33558         }
33559         if(east && east.isVisible()){
33560             var b = east.getBox();
33561             var m = east.getMargins();
33562             b.height = centerH - (m.top+m.bottom);
33563             var totalWidth = (b.width + m.left + m.right);
33564             b.x = w - totalWidth + m.left;
33565             b.y = centerY + m.top;
33566             centerW -= totalWidth;
33567             east.updateBox(this.safeBox(b));
33568         }
33569         if(center){
33570             var m = center.getMargins();
33571             var centerBox = {
33572                 x: centerX + m.left,
33573                 y: centerY + m.top,
33574                 width: centerW - (m.left+m.right),
33575                 height: centerH - (m.top+m.bottom)
33576             };
33577             //if(this.hideOnLayout){
33578                 //center.el.setStyle("display", "block");
33579             //}
33580             center.updateBox(this.safeBox(centerBox));
33581         }
33582         this.el.repaint();
33583         this.fireEvent("layout", this);
33584     },
33585
33586     // private
33587     safeBox : function(box){
33588         box.width = Math.max(0, box.width);
33589         box.height = Math.max(0, box.height);
33590         return box;
33591     },
33592
33593     /**
33594      * Adds a ContentPanel (or subclass) to this layout.
33595      * @param {String} target The target region key (north, south, east, west or center).
33596      * @param {Roo.ContentPanel} panel The panel to add
33597      * @return {Roo.ContentPanel} The added panel
33598      */
33599     add : function(target, panel){
33600          
33601         target = target.toLowerCase();
33602         return this.regions[target].add(panel);
33603     },
33604
33605     /**
33606      * Remove a ContentPanel (or subclass) to this layout.
33607      * @param {String} target The target region key (north, south, east, west or center).
33608      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33609      * @return {Roo.ContentPanel} The removed panel
33610      */
33611     remove : function(target, panel){
33612         target = target.toLowerCase();
33613         return this.regions[target].remove(panel);
33614     },
33615
33616     /**
33617      * Searches all regions for a panel with the specified id
33618      * @param {String} panelId
33619      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33620      */
33621     findPanel : function(panelId){
33622         var rs = this.regions;
33623         for(var target in rs){
33624             if(typeof rs[target] != "function"){
33625                 var p = rs[target].getPanel(panelId);
33626                 if(p){
33627                     return p;
33628                 }
33629             }
33630         }
33631         return null;
33632     },
33633
33634     /**
33635      * Searches all regions for a panel with the specified id and activates (shows) it.
33636      * @param {String/ContentPanel} panelId The panels id or the panel itself
33637      * @return {Roo.ContentPanel} The shown panel or null
33638      */
33639     showPanel : function(panelId) {
33640       var rs = this.regions;
33641       for(var target in rs){
33642          var r = rs[target];
33643          if(typeof r != "function"){
33644             if(r.hasPanel(panelId)){
33645                return r.showPanel(panelId);
33646             }
33647          }
33648       }
33649       return null;
33650    },
33651
33652    /**
33653      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33654      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33655      */
33656    /*
33657     restoreState : function(provider){
33658         if(!provider){
33659             provider = Roo.state.Manager;
33660         }
33661         var sm = new Roo.LayoutStateManager();
33662         sm.init(this, provider);
33663     },
33664 */
33665  
33666  
33667     /**
33668      * Adds a xtype elements to the layout.
33669      * <pre><code>
33670
33671 layout.addxtype({
33672        xtype : 'ContentPanel',
33673        region: 'west',
33674        items: [ .... ]
33675    }
33676 );
33677
33678 layout.addxtype({
33679         xtype : 'NestedLayoutPanel',
33680         region: 'west',
33681         layout: {
33682            center: { },
33683            west: { }   
33684         },
33685         items : [ ... list of content panels or nested layout panels.. ]
33686    }
33687 );
33688 </code></pre>
33689      * @param {Object} cfg Xtype definition of item to add.
33690      */
33691     addxtype : function(cfg)
33692     {
33693         // basically accepts a pannel...
33694         // can accept a layout region..!?!?
33695         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33696         
33697         
33698         // theory?  children can only be panels??
33699         
33700         //if (!cfg.xtype.match(/Panel$/)) {
33701         //    return false;
33702         //}
33703         var ret = false;
33704         
33705         if (typeof(cfg.region) == 'undefined') {
33706             Roo.log("Failed to add Panel, region was not set");
33707             Roo.log(cfg);
33708             return false;
33709         }
33710         var region = cfg.region;
33711         delete cfg.region;
33712         
33713           
33714         var xitems = [];
33715         if (cfg.items) {
33716             xitems = cfg.items;
33717             delete cfg.items;
33718         }
33719         var nb = false;
33720         
33721         switch(cfg.xtype) 
33722         {
33723             case 'Content':  // ContentPanel (el, cfg)
33724             case 'Scroll':  // ContentPanel (el, cfg)
33725             case 'View': 
33726                 cfg.autoCreate = true;
33727                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33728                 //} else {
33729                 //    var el = this.el.createChild();
33730                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33731                 //}
33732                 
33733                 this.add(region, ret);
33734                 break;
33735             
33736             /*
33737             case 'TreePanel': // our new panel!
33738                 cfg.el = this.el.createChild();
33739                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33740                 this.add(region, ret);
33741                 break;
33742             */
33743             
33744             case 'Nest': 
33745                 // create a new Layout (which is  a Border Layout...
33746                 
33747                 var clayout = cfg.layout;
33748                 clayout.el  = this.el.createChild();
33749                 clayout.items   = clayout.items  || [];
33750                 
33751                 delete cfg.layout;
33752                 
33753                 // replace this exitems with the clayout ones..
33754                 xitems = clayout.items;
33755                  
33756                 // force background off if it's in center...
33757                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33758                     cfg.background = false;
33759                 }
33760                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33761                 
33762                 
33763                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33764                 //console.log('adding nested layout panel '  + cfg.toSource());
33765                 this.add(region, ret);
33766                 nb = {}; /// find first...
33767                 break;
33768             
33769             case 'Grid':
33770                 
33771                 // needs grid and region
33772                 
33773                 //var el = this.getRegion(region).el.createChild();
33774                 /*
33775                  *var el = this.el.createChild();
33776                 // create the grid first...
33777                 cfg.grid.container = el;
33778                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33779                 */
33780                 
33781                 if (region == 'center' && this.active ) {
33782                     cfg.background = false;
33783                 }
33784                 
33785                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33786                 
33787                 this.add(region, ret);
33788                 /*
33789                 if (cfg.background) {
33790                     // render grid on panel activation (if panel background)
33791                     ret.on('activate', function(gp) {
33792                         if (!gp.grid.rendered) {
33793                     //        gp.grid.render(el);
33794                         }
33795                     });
33796                 } else {
33797                   //  cfg.grid.render(el);
33798                 }
33799                 */
33800                 break;
33801            
33802            
33803             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33804                 // it was the old xcomponent building that caused this before.
33805                 // espeically if border is the top element in the tree.
33806                 ret = this;
33807                 break; 
33808                 
33809                     
33810                 
33811                 
33812                 
33813             default:
33814                 /*
33815                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33816                     
33817                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33818                     this.add(region, ret);
33819                 } else {
33820                 */
33821                     Roo.log(cfg);
33822                     throw "Can not add '" + cfg.xtype + "' to Border";
33823                     return null;
33824              
33825                                 
33826              
33827         }
33828         this.beginUpdate();
33829         // add children..
33830         var region = '';
33831         var abn = {};
33832         Roo.each(xitems, function(i)  {
33833             region = nb && i.region ? i.region : false;
33834             
33835             var add = ret.addxtype(i);
33836            
33837             if (region) {
33838                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33839                 if (!i.background) {
33840                     abn[region] = nb[region] ;
33841                 }
33842             }
33843             
33844         });
33845         this.endUpdate();
33846
33847         // make the last non-background panel active..
33848         //if (nb) { Roo.log(abn); }
33849         if (nb) {
33850             
33851             for(var r in abn) {
33852                 region = this.getRegion(r);
33853                 if (region) {
33854                     // tried using nb[r], but it does not work..
33855                      
33856                     region.showPanel(abn[r]);
33857                    
33858                 }
33859             }
33860         }
33861         return ret;
33862         
33863     },
33864     
33865     
33866 // private
33867     factory : function(cfg)
33868     {
33869         
33870         var validRegions = Roo.bootstrap.layout.Border.regions;
33871
33872         var target = cfg.region;
33873         cfg.mgr = this;
33874         
33875         var r = Roo.bootstrap.layout;
33876         Roo.log(target);
33877         switch(target){
33878             case "north":
33879                 return new r.North(cfg);
33880             case "south":
33881                 return new r.South(cfg);
33882             case "east":
33883                 return new r.East(cfg);
33884             case "west":
33885                 return new r.West(cfg);
33886             case "center":
33887                 return new r.Center(cfg);
33888         }
33889         throw 'Layout region "'+target+'" not supported.';
33890     }
33891     
33892     
33893 });
33894  /*
33895  * Based on:
33896  * Ext JS Library 1.1.1
33897  * Copyright(c) 2006-2007, Ext JS, LLC.
33898  *
33899  * Originally Released Under LGPL - original licence link has changed is not relivant.
33900  *
33901  * Fork - LGPL
33902  * <script type="text/javascript">
33903  */
33904  
33905 /**
33906  * @class Roo.bootstrap.layout.Basic
33907  * @extends Roo.util.Observable
33908  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33909  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33910  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33911  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33912  * @cfg {string}   region  the region that it inhabits..
33913  * @cfg {bool}   skipConfig skip config?
33914  * 
33915
33916  */
33917 Roo.bootstrap.layout.Basic = function(config){
33918     
33919     this.mgr = config.mgr;
33920     
33921     this.position = config.region;
33922     
33923     var skipConfig = config.skipConfig;
33924     
33925     this.events = {
33926         /**
33927          * @scope Roo.BasicLayoutRegion
33928          */
33929         
33930         /**
33931          * @event beforeremove
33932          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33933          * @param {Roo.LayoutRegion} this
33934          * @param {Roo.ContentPanel} panel The panel
33935          * @param {Object} e The cancel event object
33936          */
33937         "beforeremove" : true,
33938         /**
33939          * @event invalidated
33940          * Fires when the layout for this region is changed.
33941          * @param {Roo.LayoutRegion} this
33942          */
33943         "invalidated" : true,
33944         /**
33945          * @event visibilitychange
33946          * Fires when this region is shown or hidden 
33947          * @param {Roo.LayoutRegion} this
33948          * @param {Boolean} visibility true or false
33949          */
33950         "visibilitychange" : true,
33951         /**
33952          * @event paneladded
33953          * Fires when a panel is added. 
33954          * @param {Roo.LayoutRegion} this
33955          * @param {Roo.ContentPanel} panel The panel
33956          */
33957         "paneladded" : true,
33958         /**
33959          * @event panelremoved
33960          * Fires when a panel is removed. 
33961          * @param {Roo.LayoutRegion} this
33962          * @param {Roo.ContentPanel} panel The panel
33963          */
33964         "panelremoved" : true,
33965         /**
33966          * @event beforecollapse
33967          * Fires when this region before collapse.
33968          * @param {Roo.LayoutRegion} this
33969          */
33970         "beforecollapse" : true,
33971         /**
33972          * @event collapsed
33973          * Fires when this region is collapsed.
33974          * @param {Roo.LayoutRegion} this
33975          */
33976         "collapsed" : true,
33977         /**
33978          * @event expanded
33979          * Fires when this region is expanded.
33980          * @param {Roo.LayoutRegion} this
33981          */
33982         "expanded" : true,
33983         /**
33984          * @event slideshow
33985          * Fires when this region is slid into view.
33986          * @param {Roo.LayoutRegion} this
33987          */
33988         "slideshow" : true,
33989         /**
33990          * @event slidehide
33991          * Fires when this region slides out of view. 
33992          * @param {Roo.LayoutRegion} this
33993          */
33994         "slidehide" : true,
33995         /**
33996          * @event panelactivated
33997          * Fires when a panel is activated. 
33998          * @param {Roo.LayoutRegion} this
33999          * @param {Roo.ContentPanel} panel The activated panel
34000          */
34001         "panelactivated" : true,
34002         /**
34003          * @event resized
34004          * Fires when the user resizes this region. 
34005          * @param {Roo.LayoutRegion} this
34006          * @param {Number} newSize The new size (width for east/west, height for north/south)
34007          */
34008         "resized" : true
34009     };
34010     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34011     this.panels = new Roo.util.MixedCollection();
34012     this.panels.getKey = this.getPanelId.createDelegate(this);
34013     this.box = null;
34014     this.activePanel = null;
34015     // ensure listeners are added...
34016     
34017     if (config.listeners || config.events) {
34018         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34019             listeners : config.listeners || {},
34020             events : config.events || {}
34021         });
34022     }
34023     
34024     if(skipConfig !== true){
34025         this.applyConfig(config);
34026     }
34027 };
34028
34029 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34030 {
34031     getPanelId : function(p){
34032         return p.getId();
34033     },
34034     
34035     applyConfig : function(config){
34036         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34037         this.config = config;
34038         
34039     },
34040     
34041     /**
34042      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34043      * the width, for horizontal (north, south) the height.
34044      * @param {Number} newSize The new width or height
34045      */
34046     resizeTo : function(newSize){
34047         var el = this.el ? this.el :
34048                  (this.activePanel ? this.activePanel.getEl() : null);
34049         if(el){
34050             switch(this.position){
34051                 case "east":
34052                 case "west":
34053                     el.setWidth(newSize);
34054                     this.fireEvent("resized", this, newSize);
34055                 break;
34056                 case "north":
34057                 case "south":
34058                     el.setHeight(newSize);
34059                     this.fireEvent("resized", this, newSize);
34060                 break;                
34061             }
34062         }
34063     },
34064     
34065     getBox : function(){
34066         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34067     },
34068     
34069     getMargins : function(){
34070         return this.margins;
34071     },
34072     
34073     updateBox : function(box){
34074         this.box = box;
34075         var el = this.activePanel.getEl();
34076         el.dom.style.left = box.x + "px";
34077         el.dom.style.top = box.y + "px";
34078         this.activePanel.setSize(box.width, box.height);
34079     },
34080     
34081     /**
34082      * Returns the container element for this region.
34083      * @return {Roo.Element}
34084      */
34085     getEl : function(){
34086         return this.activePanel;
34087     },
34088     
34089     /**
34090      * Returns true if this region is currently visible.
34091      * @return {Boolean}
34092      */
34093     isVisible : function(){
34094         return this.activePanel ? true : false;
34095     },
34096     
34097     setActivePanel : function(panel){
34098         panel = this.getPanel(panel);
34099         if(this.activePanel && this.activePanel != panel){
34100             this.activePanel.setActiveState(false);
34101             this.activePanel.getEl().setLeftTop(-10000,-10000);
34102         }
34103         this.activePanel = panel;
34104         panel.setActiveState(true);
34105         if(this.box){
34106             panel.setSize(this.box.width, this.box.height);
34107         }
34108         this.fireEvent("panelactivated", this, panel);
34109         this.fireEvent("invalidated");
34110     },
34111     
34112     /**
34113      * Show the specified panel.
34114      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34115      * @return {Roo.ContentPanel} The shown panel or null
34116      */
34117     showPanel : function(panel){
34118         panel = this.getPanel(panel);
34119         if(panel){
34120             this.setActivePanel(panel);
34121         }
34122         return panel;
34123     },
34124     
34125     /**
34126      * Get the active panel for this region.
34127      * @return {Roo.ContentPanel} The active panel or null
34128      */
34129     getActivePanel : function(){
34130         return this.activePanel;
34131     },
34132     
34133     /**
34134      * Add the passed ContentPanel(s)
34135      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34136      * @return {Roo.ContentPanel} The panel added (if only one was added)
34137      */
34138     add : function(panel){
34139         if(arguments.length > 1){
34140             for(var i = 0, len = arguments.length; i < len; i++) {
34141                 this.add(arguments[i]);
34142             }
34143             return null;
34144         }
34145         if(this.hasPanel(panel)){
34146             this.showPanel(panel);
34147             return panel;
34148         }
34149         var el = panel.getEl();
34150         if(el.dom.parentNode != this.mgr.el.dom){
34151             this.mgr.el.dom.appendChild(el.dom);
34152         }
34153         if(panel.setRegion){
34154             panel.setRegion(this);
34155         }
34156         this.panels.add(panel);
34157         el.setStyle("position", "absolute");
34158         if(!panel.background){
34159             this.setActivePanel(panel);
34160             if(this.config.initialSize && this.panels.getCount()==1){
34161                 this.resizeTo(this.config.initialSize);
34162             }
34163         }
34164         this.fireEvent("paneladded", this, panel);
34165         return panel;
34166     },
34167     
34168     /**
34169      * Returns true if the panel is in this region.
34170      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34171      * @return {Boolean}
34172      */
34173     hasPanel : function(panel){
34174         if(typeof panel == "object"){ // must be panel obj
34175             panel = panel.getId();
34176         }
34177         return this.getPanel(panel) ? true : false;
34178     },
34179     
34180     /**
34181      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34182      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34183      * @param {Boolean} preservePanel Overrides the config preservePanel option
34184      * @return {Roo.ContentPanel} The panel that was removed
34185      */
34186     remove : function(panel, preservePanel){
34187         panel = this.getPanel(panel);
34188         if(!panel){
34189             return null;
34190         }
34191         var e = {};
34192         this.fireEvent("beforeremove", this, panel, e);
34193         if(e.cancel === true){
34194             return null;
34195         }
34196         var panelId = panel.getId();
34197         this.panels.removeKey(panelId);
34198         return panel;
34199     },
34200     
34201     /**
34202      * Returns the panel specified or null if it's not in this region.
34203      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34204      * @return {Roo.ContentPanel}
34205      */
34206     getPanel : function(id){
34207         if(typeof id == "object"){ // must be panel obj
34208             return id;
34209         }
34210         return this.panels.get(id);
34211     },
34212     
34213     /**
34214      * Returns this regions position (north/south/east/west/center).
34215      * @return {String} 
34216      */
34217     getPosition: function(){
34218         return this.position;    
34219     }
34220 });/*
34221  * Based on:
34222  * Ext JS Library 1.1.1
34223  * Copyright(c) 2006-2007, Ext JS, LLC.
34224  *
34225  * Originally Released Under LGPL - original licence link has changed is not relivant.
34226  *
34227  * Fork - LGPL
34228  * <script type="text/javascript">
34229  */
34230  
34231 /**
34232  * @class Roo.bootstrap.layout.Region
34233  * @extends Roo.bootstrap.layout.Basic
34234  * This class represents a region in a layout manager.
34235  
34236  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34237  * @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})
34238  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34239  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34240  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34241  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34242  * @cfg {String}    title           The title for the region (overrides panel titles)
34243  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34244  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34245  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34246  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34247  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34248  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34249  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34250  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34251  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34252  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34253
34254  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34255  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34256  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34257  * @cfg {Number}    width           For East/West panels
34258  * @cfg {Number}    height          For North/South panels
34259  * @cfg {Boolean}   split           To show the splitter
34260  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34261  * 
34262  * @cfg {string}   cls             Extra CSS classes to add to region
34263  * 
34264  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34265  * @cfg {string}   region  the region that it inhabits..
34266  *
34267
34268  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34269  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34270
34271  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34272  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34273  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34274  */
34275 Roo.bootstrap.layout.Region = function(config)
34276 {
34277     this.applyConfig(config);
34278
34279     var mgr = config.mgr;
34280     var pos = config.region;
34281     config.skipConfig = true;
34282     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34283     
34284     if (mgr.el) {
34285         this.onRender(mgr.el);   
34286     }
34287      
34288     this.visible = true;
34289     this.collapsed = false;
34290     this.unrendered_panels = [];
34291 };
34292
34293 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34294
34295     position: '', // set by wrapper (eg. north/south etc..)
34296     unrendered_panels : null,  // unrendered panels.
34297     createBody : function(){
34298         /** This region's body element 
34299         * @type Roo.Element */
34300         this.bodyEl = this.el.createChild({
34301                 tag: "div",
34302                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34303         });
34304     },
34305
34306     onRender: function(ctr, pos)
34307     {
34308         var dh = Roo.DomHelper;
34309         /** This region's container element 
34310         * @type Roo.Element */
34311         this.el = dh.append(ctr.dom, {
34312                 tag: "div",
34313                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34314             }, true);
34315         /** This region's title element 
34316         * @type Roo.Element */
34317     
34318         this.titleEl = dh.append(this.el.dom,
34319             {
34320                     tag: "div",
34321                     unselectable: "on",
34322                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34323                     children:[
34324                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34325                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34326                     ]}, true);
34327         
34328         this.titleEl.enableDisplayMode();
34329         /** This region's title text element 
34330         * @type HTMLElement */
34331         this.titleTextEl = this.titleEl.dom.firstChild;
34332         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34333         /*
34334         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34335         this.closeBtn.enableDisplayMode();
34336         this.closeBtn.on("click", this.closeClicked, this);
34337         this.closeBtn.hide();
34338     */
34339         this.createBody(this.config);
34340         if(this.config.hideWhenEmpty){
34341             this.hide();
34342             this.on("paneladded", this.validateVisibility, this);
34343             this.on("panelremoved", this.validateVisibility, this);
34344         }
34345         if(this.autoScroll){
34346             this.bodyEl.setStyle("overflow", "auto");
34347         }else{
34348             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34349         }
34350         //if(c.titlebar !== false){
34351             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34352                 this.titleEl.hide();
34353             }else{
34354                 this.titleEl.show();
34355                 if(this.config.title){
34356                     this.titleTextEl.innerHTML = this.config.title;
34357                 }
34358             }
34359         //}
34360         if(this.config.collapsed){
34361             this.collapse(true);
34362         }
34363         if(this.config.hidden){
34364             this.hide();
34365         }
34366         
34367         if (this.unrendered_panels && this.unrendered_panels.length) {
34368             for (var i =0;i< this.unrendered_panels.length; i++) {
34369                 this.add(this.unrendered_panels[i]);
34370             }
34371             this.unrendered_panels = null;
34372             
34373         }
34374         
34375     },
34376     
34377     applyConfig : function(c)
34378     {
34379         /*
34380          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34381             var dh = Roo.DomHelper;
34382             if(c.titlebar !== false){
34383                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34384                 this.collapseBtn.on("click", this.collapse, this);
34385                 this.collapseBtn.enableDisplayMode();
34386                 /*
34387                 if(c.showPin === true || this.showPin){
34388                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34389                     this.stickBtn.enableDisplayMode();
34390                     this.stickBtn.on("click", this.expand, this);
34391                     this.stickBtn.hide();
34392                 }
34393                 
34394             }
34395             */
34396             /** This region's collapsed element
34397             * @type Roo.Element */
34398             /*
34399              *
34400             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34401                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34402             ]}, true);
34403             
34404             if(c.floatable !== false){
34405                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34406                this.collapsedEl.on("click", this.collapseClick, this);
34407             }
34408
34409             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34410                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34411                    id: "message", unselectable: "on", style:{"float":"left"}});
34412                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34413              }
34414             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34415             this.expandBtn.on("click", this.expand, this);
34416             
34417         }
34418         
34419         if(this.collapseBtn){
34420             this.collapseBtn.setVisible(c.collapsible == true);
34421         }
34422         
34423         this.cmargins = c.cmargins || this.cmargins ||
34424                          (this.position == "west" || this.position == "east" ?
34425                              {top: 0, left: 2, right:2, bottom: 0} :
34426                              {top: 2, left: 0, right:0, bottom: 2});
34427         */
34428         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34429         
34430         
34431         this.bottomTabs = c.tabPosition != "top";
34432         
34433         this.autoScroll = c.autoScroll || false;
34434         
34435         
34436        
34437         
34438         this.duration = c.duration || .30;
34439         this.slideDuration = c.slideDuration || .45;
34440         this.config = c;
34441        
34442     },
34443     /**
34444      * Returns true if this region is currently visible.
34445      * @return {Boolean}
34446      */
34447     isVisible : function(){
34448         return this.visible;
34449     },
34450
34451     /**
34452      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34453      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34454      */
34455     //setCollapsedTitle : function(title){
34456     //    title = title || "&#160;";
34457      //   if(this.collapsedTitleTextEl){
34458       //      this.collapsedTitleTextEl.innerHTML = title;
34459        // }
34460     //},
34461
34462     getBox : function(){
34463         var b;
34464       //  if(!this.collapsed){
34465             b = this.el.getBox(false, true);
34466        // }else{
34467           //  b = this.collapsedEl.getBox(false, true);
34468         //}
34469         return b;
34470     },
34471
34472     getMargins : function(){
34473         return this.margins;
34474         //return this.collapsed ? this.cmargins : this.margins;
34475     },
34476 /*
34477     highlight : function(){
34478         this.el.addClass("x-layout-panel-dragover");
34479     },
34480
34481     unhighlight : function(){
34482         this.el.removeClass("x-layout-panel-dragover");
34483     },
34484 */
34485     updateBox : function(box)
34486     {
34487         if (!this.bodyEl) {
34488             return; // not rendered yet..
34489         }
34490         
34491         this.box = box;
34492         if(!this.collapsed){
34493             this.el.dom.style.left = box.x + "px";
34494             this.el.dom.style.top = box.y + "px";
34495             this.updateBody(box.width, box.height);
34496         }else{
34497             this.collapsedEl.dom.style.left = box.x + "px";
34498             this.collapsedEl.dom.style.top = box.y + "px";
34499             this.collapsedEl.setSize(box.width, box.height);
34500         }
34501         if(this.tabs){
34502             this.tabs.autoSizeTabs();
34503         }
34504     },
34505
34506     updateBody : function(w, h)
34507     {
34508         if(w !== null){
34509             this.el.setWidth(w);
34510             w -= this.el.getBorderWidth("rl");
34511             if(this.config.adjustments){
34512                 w += this.config.adjustments[0];
34513             }
34514         }
34515         if(h !== null && h > 0){
34516             this.el.setHeight(h);
34517             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34518             h -= this.el.getBorderWidth("tb");
34519             if(this.config.adjustments){
34520                 h += this.config.adjustments[1];
34521             }
34522             this.bodyEl.setHeight(h);
34523             if(this.tabs){
34524                 h = this.tabs.syncHeight(h);
34525             }
34526         }
34527         if(this.panelSize){
34528             w = w !== null ? w : this.panelSize.width;
34529             h = h !== null ? h : this.panelSize.height;
34530         }
34531         if(this.activePanel){
34532             var el = this.activePanel.getEl();
34533             w = w !== null ? w : el.getWidth();
34534             h = h !== null ? h : el.getHeight();
34535             this.panelSize = {width: w, height: h};
34536             this.activePanel.setSize(w, h);
34537         }
34538         if(Roo.isIE && this.tabs){
34539             this.tabs.el.repaint();
34540         }
34541     },
34542
34543     /**
34544      * Returns the container element for this region.
34545      * @return {Roo.Element}
34546      */
34547     getEl : function(){
34548         return this.el;
34549     },
34550
34551     /**
34552      * Hides this region.
34553      */
34554     hide : function(){
34555         //if(!this.collapsed){
34556             this.el.dom.style.left = "-2000px";
34557             this.el.hide();
34558         //}else{
34559          //   this.collapsedEl.dom.style.left = "-2000px";
34560          //   this.collapsedEl.hide();
34561        // }
34562         this.visible = false;
34563         this.fireEvent("visibilitychange", this, false);
34564     },
34565
34566     /**
34567      * Shows this region if it was previously hidden.
34568      */
34569     show : function(){
34570         //if(!this.collapsed){
34571             this.el.show();
34572         //}else{
34573         //    this.collapsedEl.show();
34574        // }
34575         this.visible = true;
34576         this.fireEvent("visibilitychange", this, true);
34577     },
34578 /*
34579     closeClicked : function(){
34580         if(this.activePanel){
34581             this.remove(this.activePanel);
34582         }
34583     },
34584
34585     collapseClick : function(e){
34586         if(this.isSlid){
34587            e.stopPropagation();
34588            this.slideIn();
34589         }else{
34590            e.stopPropagation();
34591            this.slideOut();
34592         }
34593     },
34594 */
34595     /**
34596      * Collapses this region.
34597      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34598      */
34599     /*
34600     collapse : function(skipAnim, skipCheck = false){
34601         if(this.collapsed) {
34602             return;
34603         }
34604         
34605         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34606             
34607             this.collapsed = true;
34608             if(this.split){
34609                 this.split.el.hide();
34610             }
34611             if(this.config.animate && skipAnim !== true){
34612                 this.fireEvent("invalidated", this);
34613                 this.animateCollapse();
34614             }else{
34615                 this.el.setLocation(-20000,-20000);
34616                 this.el.hide();
34617                 this.collapsedEl.show();
34618                 this.fireEvent("collapsed", this);
34619                 this.fireEvent("invalidated", this);
34620             }
34621         }
34622         
34623     },
34624 */
34625     animateCollapse : function(){
34626         // overridden
34627     },
34628
34629     /**
34630      * Expands this region if it was previously collapsed.
34631      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34632      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34633      */
34634     /*
34635     expand : function(e, skipAnim){
34636         if(e) {
34637             e.stopPropagation();
34638         }
34639         if(!this.collapsed || this.el.hasActiveFx()) {
34640             return;
34641         }
34642         if(this.isSlid){
34643             this.afterSlideIn();
34644             skipAnim = true;
34645         }
34646         this.collapsed = false;
34647         if(this.config.animate && skipAnim !== true){
34648             this.animateExpand();
34649         }else{
34650             this.el.show();
34651             if(this.split){
34652                 this.split.el.show();
34653             }
34654             this.collapsedEl.setLocation(-2000,-2000);
34655             this.collapsedEl.hide();
34656             this.fireEvent("invalidated", this);
34657             this.fireEvent("expanded", this);
34658         }
34659     },
34660 */
34661     animateExpand : function(){
34662         // overridden
34663     },
34664
34665     initTabs : function()
34666     {
34667         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34668         
34669         var ts = new Roo.bootstrap.panel.Tabs({
34670                 el: this.bodyEl.dom,
34671                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34672                 disableTooltips: this.config.disableTabTips,
34673                 toolbar : this.config.toolbar
34674             });
34675         
34676         if(this.config.hideTabs){
34677             ts.stripWrap.setDisplayed(false);
34678         }
34679         this.tabs = ts;
34680         ts.resizeTabs = this.config.resizeTabs === true;
34681         ts.minTabWidth = this.config.minTabWidth || 40;
34682         ts.maxTabWidth = this.config.maxTabWidth || 250;
34683         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34684         ts.monitorResize = false;
34685         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34686         ts.bodyEl.addClass('roo-layout-tabs-body');
34687         this.panels.each(this.initPanelAsTab, this);
34688     },
34689
34690     initPanelAsTab : function(panel){
34691         var ti = this.tabs.addTab(
34692             panel.getEl().id,
34693             panel.getTitle(),
34694             null,
34695             this.config.closeOnTab && panel.isClosable(),
34696             panel.tpl
34697         );
34698         if(panel.tabTip !== undefined){
34699             ti.setTooltip(panel.tabTip);
34700         }
34701         ti.on("activate", function(){
34702               this.setActivePanel(panel);
34703         }, this);
34704         
34705         if(this.config.closeOnTab){
34706             ti.on("beforeclose", function(t, e){
34707                 e.cancel = true;
34708                 this.remove(panel);
34709             }, this);
34710         }
34711         
34712         panel.tabItem = ti;
34713         
34714         return ti;
34715     },
34716
34717     updatePanelTitle : function(panel, title)
34718     {
34719         if(this.activePanel == panel){
34720             this.updateTitle(title);
34721         }
34722         if(this.tabs){
34723             var ti = this.tabs.getTab(panel.getEl().id);
34724             ti.setText(title);
34725             if(panel.tabTip !== undefined){
34726                 ti.setTooltip(panel.tabTip);
34727             }
34728         }
34729     },
34730
34731     updateTitle : function(title){
34732         if(this.titleTextEl && !this.config.title){
34733             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34734         }
34735     },
34736
34737     setActivePanel : function(panel)
34738     {
34739         panel = this.getPanel(panel);
34740         if(this.activePanel && this.activePanel != panel){
34741             this.activePanel.setActiveState(false);
34742         }
34743         this.activePanel = panel;
34744         panel.setActiveState(true);
34745         if(this.panelSize){
34746             panel.setSize(this.panelSize.width, this.panelSize.height);
34747         }
34748         if(this.closeBtn){
34749             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34750         }
34751         this.updateTitle(panel.getTitle());
34752         if(this.tabs){
34753             this.fireEvent("invalidated", this);
34754         }
34755         this.fireEvent("panelactivated", this, panel);
34756     },
34757
34758     /**
34759      * Shows the specified panel.
34760      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34761      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34762      */
34763     showPanel : function(panel)
34764     {
34765         panel = this.getPanel(panel);
34766         if(panel){
34767             if(this.tabs){
34768                 var tab = this.tabs.getTab(panel.getEl().id);
34769                 if(tab.isHidden()){
34770                     this.tabs.unhideTab(tab.id);
34771                 }
34772                 tab.activate();
34773             }else{
34774                 this.setActivePanel(panel);
34775             }
34776         }
34777         return panel;
34778     },
34779
34780     /**
34781      * Get the active panel for this region.
34782      * @return {Roo.ContentPanel} The active panel or null
34783      */
34784     getActivePanel : function(){
34785         return this.activePanel;
34786     },
34787
34788     validateVisibility : function(){
34789         if(this.panels.getCount() < 1){
34790             this.updateTitle("&#160;");
34791             this.closeBtn.hide();
34792             this.hide();
34793         }else{
34794             if(!this.isVisible()){
34795                 this.show();
34796             }
34797         }
34798     },
34799
34800     /**
34801      * Adds the passed ContentPanel(s) to this region.
34802      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34803      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34804      */
34805     add : function(panel)
34806     {
34807         if(arguments.length > 1){
34808             for(var i = 0, len = arguments.length; i < len; i++) {
34809                 this.add(arguments[i]);
34810             }
34811             return null;
34812         }
34813         
34814         // if we have not been rendered yet, then we can not really do much of this..
34815         if (!this.bodyEl) {
34816             this.unrendered_panels.push(panel);
34817             return panel;
34818         }
34819         
34820         
34821         
34822         
34823         if(this.hasPanel(panel)){
34824             this.showPanel(panel);
34825             return panel;
34826         }
34827         panel.setRegion(this);
34828         this.panels.add(panel);
34829        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34830             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34831             // and hide them... ???
34832             this.bodyEl.dom.appendChild(panel.getEl().dom);
34833             if(panel.background !== true){
34834                 this.setActivePanel(panel);
34835             }
34836             this.fireEvent("paneladded", this, panel);
34837             return panel;
34838         }
34839         */
34840         if(!this.tabs){
34841             this.initTabs();
34842         }else{
34843             this.initPanelAsTab(panel);
34844         }
34845         
34846         
34847         if(panel.background !== true){
34848             this.tabs.activate(panel.getEl().id);
34849         }
34850         this.fireEvent("paneladded", this, panel);
34851         return panel;
34852     },
34853
34854     /**
34855      * Hides the tab for the specified panel.
34856      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34857      */
34858     hidePanel : function(panel){
34859         if(this.tabs && (panel = this.getPanel(panel))){
34860             this.tabs.hideTab(panel.getEl().id);
34861         }
34862     },
34863
34864     /**
34865      * Unhides the tab for a previously hidden panel.
34866      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34867      */
34868     unhidePanel : function(panel){
34869         if(this.tabs && (panel = this.getPanel(panel))){
34870             this.tabs.unhideTab(panel.getEl().id);
34871         }
34872     },
34873
34874     clearPanels : function(){
34875         while(this.panels.getCount() > 0){
34876              this.remove(this.panels.first());
34877         }
34878     },
34879
34880     /**
34881      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34882      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34883      * @param {Boolean} preservePanel Overrides the config preservePanel option
34884      * @return {Roo.ContentPanel} The panel that was removed
34885      */
34886     remove : function(panel, preservePanel)
34887     {
34888         panel = this.getPanel(panel);
34889         if(!panel){
34890             return null;
34891         }
34892         var e = {};
34893         this.fireEvent("beforeremove", this, panel, e);
34894         if(e.cancel === true){
34895             return null;
34896         }
34897         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34898         var panelId = panel.getId();
34899         this.panels.removeKey(panelId);
34900         if(preservePanel){
34901             document.body.appendChild(panel.getEl().dom);
34902         }
34903         if(this.tabs){
34904             this.tabs.removeTab(panel.getEl().id);
34905         }else if (!preservePanel){
34906             this.bodyEl.dom.removeChild(panel.getEl().dom);
34907         }
34908         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34909             var p = this.panels.first();
34910             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34911             tempEl.appendChild(p.getEl().dom);
34912             this.bodyEl.update("");
34913             this.bodyEl.dom.appendChild(p.getEl().dom);
34914             tempEl = null;
34915             this.updateTitle(p.getTitle());
34916             this.tabs = null;
34917             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34918             this.setActivePanel(p);
34919         }
34920         panel.setRegion(null);
34921         if(this.activePanel == panel){
34922             this.activePanel = null;
34923         }
34924         if(this.config.autoDestroy !== false && preservePanel !== true){
34925             try{panel.destroy();}catch(e){}
34926         }
34927         this.fireEvent("panelremoved", this, panel);
34928         return panel;
34929     },
34930
34931     /**
34932      * Returns the TabPanel component used by this region
34933      * @return {Roo.TabPanel}
34934      */
34935     getTabs : function(){
34936         return this.tabs;
34937     },
34938
34939     createTool : function(parentEl, className){
34940         var btn = Roo.DomHelper.append(parentEl, {
34941             tag: "div",
34942             cls: "x-layout-tools-button",
34943             children: [ {
34944                 tag: "div",
34945                 cls: "roo-layout-tools-button-inner " + className,
34946                 html: "&#160;"
34947             }]
34948         }, true);
34949         btn.addClassOnOver("roo-layout-tools-button-over");
34950         return btn;
34951     }
34952 });/*
34953  * Based on:
34954  * Ext JS Library 1.1.1
34955  * Copyright(c) 2006-2007, Ext JS, LLC.
34956  *
34957  * Originally Released Under LGPL - original licence link has changed is not relivant.
34958  *
34959  * Fork - LGPL
34960  * <script type="text/javascript">
34961  */
34962  
34963
34964
34965 /**
34966  * @class Roo.SplitLayoutRegion
34967  * @extends Roo.LayoutRegion
34968  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34969  */
34970 Roo.bootstrap.layout.Split = function(config){
34971     this.cursor = config.cursor;
34972     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34973 };
34974
34975 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34976 {
34977     splitTip : "Drag to resize.",
34978     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34979     useSplitTips : false,
34980
34981     applyConfig : function(config){
34982         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34983     },
34984     
34985     onRender : function(ctr,pos) {
34986         
34987         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34988         if(!this.config.split){
34989             return;
34990         }
34991         if(!this.split){
34992             
34993             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34994                             tag: "div",
34995                             id: this.el.id + "-split",
34996                             cls: "roo-layout-split roo-layout-split-"+this.position,
34997                             html: "&#160;"
34998             });
34999             /** The SplitBar for this region 
35000             * @type Roo.SplitBar */
35001             // does not exist yet...
35002             Roo.log([this.position, this.orientation]);
35003             
35004             this.split = new Roo.bootstrap.SplitBar({
35005                 dragElement : splitEl,
35006                 resizingElement: this.el,
35007                 orientation : this.orientation
35008             });
35009             
35010             this.split.on("moved", this.onSplitMove, this);
35011             this.split.useShim = this.config.useShim === true;
35012             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35013             if(this.useSplitTips){
35014                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35015             }
35016             //if(config.collapsible){
35017             //    this.split.el.on("dblclick", this.collapse,  this);
35018             //}
35019         }
35020         if(typeof this.config.minSize != "undefined"){
35021             this.split.minSize = this.config.minSize;
35022         }
35023         if(typeof this.config.maxSize != "undefined"){
35024             this.split.maxSize = this.config.maxSize;
35025         }
35026         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35027             this.hideSplitter();
35028         }
35029         
35030     },
35031
35032     getHMaxSize : function(){
35033          var cmax = this.config.maxSize || 10000;
35034          var center = this.mgr.getRegion("center");
35035          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35036     },
35037
35038     getVMaxSize : function(){
35039          var cmax = this.config.maxSize || 10000;
35040          var center = this.mgr.getRegion("center");
35041          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35042     },
35043
35044     onSplitMove : function(split, newSize){
35045         this.fireEvent("resized", this, newSize);
35046     },
35047     
35048     /** 
35049      * Returns the {@link Roo.SplitBar} for this region.
35050      * @return {Roo.SplitBar}
35051      */
35052     getSplitBar : function(){
35053         return this.split;
35054     },
35055     
35056     hide : function(){
35057         this.hideSplitter();
35058         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35059     },
35060
35061     hideSplitter : function(){
35062         if(this.split){
35063             this.split.el.setLocation(-2000,-2000);
35064             this.split.el.hide();
35065         }
35066     },
35067
35068     show : function(){
35069         if(this.split){
35070             this.split.el.show();
35071         }
35072         Roo.bootstrap.layout.Split.superclass.show.call(this);
35073     },
35074     
35075     beforeSlide: function(){
35076         if(Roo.isGecko){// firefox overflow auto bug workaround
35077             this.bodyEl.clip();
35078             if(this.tabs) {
35079                 this.tabs.bodyEl.clip();
35080             }
35081             if(this.activePanel){
35082                 this.activePanel.getEl().clip();
35083                 
35084                 if(this.activePanel.beforeSlide){
35085                     this.activePanel.beforeSlide();
35086                 }
35087             }
35088         }
35089     },
35090     
35091     afterSlide : function(){
35092         if(Roo.isGecko){// firefox overflow auto bug workaround
35093             this.bodyEl.unclip();
35094             if(this.tabs) {
35095                 this.tabs.bodyEl.unclip();
35096             }
35097             if(this.activePanel){
35098                 this.activePanel.getEl().unclip();
35099                 if(this.activePanel.afterSlide){
35100                     this.activePanel.afterSlide();
35101                 }
35102             }
35103         }
35104     },
35105
35106     initAutoHide : function(){
35107         if(this.autoHide !== false){
35108             if(!this.autoHideHd){
35109                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35110                 this.autoHideHd = {
35111                     "mouseout": function(e){
35112                         if(!e.within(this.el, true)){
35113                             st.delay(500);
35114                         }
35115                     },
35116                     "mouseover" : function(e){
35117                         st.cancel();
35118                     },
35119                     scope : this
35120                 };
35121             }
35122             this.el.on(this.autoHideHd);
35123         }
35124     },
35125
35126     clearAutoHide : function(){
35127         if(this.autoHide !== false){
35128             this.el.un("mouseout", this.autoHideHd.mouseout);
35129             this.el.un("mouseover", this.autoHideHd.mouseover);
35130         }
35131     },
35132
35133     clearMonitor : function(){
35134         Roo.get(document).un("click", this.slideInIf, this);
35135     },
35136
35137     // these names are backwards but not changed for compat
35138     slideOut : function(){
35139         if(this.isSlid || this.el.hasActiveFx()){
35140             return;
35141         }
35142         this.isSlid = true;
35143         if(this.collapseBtn){
35144             this.collapseBtn.hide();
35145         }
35146         this.closeBtnState = this.closeBtn.getStyle('display');
35147         this.closeBtn.hide();
35148         if(this.stickBtn){
35149             this.stickBtn.show();
35150         }
35151         this.el.show();
35152         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35153         this.beforeSlide();
35154         this.el.setStyle("z-index", 10001);
35155         this.el.slideIn(this.getSlideAnchor(), {
35156             callback: function(){
35157                 this.afterSlide();
35158                 this.initAutoHide();
35159                 Roo.get(document).on("click", this.slideInIf, this);
35160                 this.fireEvent("slideshow", this);
35161             },
35162             scope: this,
35163             block: true
35164         });
35165     },
35166
35167     afterSlideIn : function(){
35168         this.clearAutoHide();
35169         this.isSlid = false;
35170         this.clearMonitor();
35171         this.el.setStyle("z-index", "");
35172         if(this.collapseBtn){
35173             this.collapseBtn.show();
35174         }
35175         this.closeBtn.setStyle('display', this.closeBtnState);
35176         if(this.stickBtn){
35177             this.stickBtn.hide();
35178         }
35179         this.fireEvent("slidehide", this);
35180     },
35181
35182     slideIn : function(cb){
35183         if(!this.isSlid || this.el.hasActiveFx()){
35184             Roo.callback(cb);
35185             return;
35186         }
35187         this.isSlid = false;
35188         this.beforeSlide();
35189         this.el.slideOut(this.getSlideAnchor(), {
35190             callback: function(){
35191                 this.el.setLeftTop(-10000, -10000);
35192                 this.afterSlide();
35193                 this.afterSlideIn();
35194                 Roo.callback(cb);
35195             },
35196             scope: this,
35197             block: true
35198         });
35199     },
35200     
35201     slideInIf : function(e){
35202         if(!e.within(this.el)){
35203             this.slideIn();
35204         }
35205     },
35206
35207     animateCollapse : function(){
35208         this.beforeSlide();
35209         this.el.setStyle("z-index", 20000);
35210         var anchor = this.getSlideAnchor();
35211         this.el.slideOut(anchor, {
35212             callback : function(){
35213                 this.el.setStyle("z-index", "");
35214                 this.collapsedEl.slideIn(anchor, {duration:.3});
35215                 this.afterSlide();
35216                 this.el.setLocation(-10000,-10000);
35217                 this.el.hide();
35218                 this.fireEvent("collapsed", this);
35219             },
35220             scope: this,
35221             block: true
35222         });
35223     },
35224
35225     animateExpand : function(){
35226         this.beforeSlide();
35227         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35228         this.el.setStyle("z-index", 20000);
35229         this.collapsedEl.hide({
35230             duration:.1
35231         });
35232         this.el.slideIn(this.getSlideAnchor(), {
35233             callback : function(){
35234                 this.el.setStyle("z-index", "");
35235                 this.afterSlide();
35236                 if(this.split){
35237                     this.split.el.show();
35238                 }
35239                 this.fireEvent("invalidated", this);
35240                 this.fireEvent("expanded", this);
35241             },
35242             scope: this,
35243             block: true
35244         });
35245     },
35246
35247     anchors : {
35248         "west" : "left",
35249         "east" : "right",
35250         "north" : "top",
35251         "south" : "bottom"
35252     },
35253
35254     sanchors : {
35255         "west" : "l",
35256         "east" : "r",
35257         "north" : "t",
35258         "south" : "b"
35259     },
35260
35261     canchors : {
35262         "west" : "tl-tr",
35263         "east" : "tr-tl",
35264         "north" : "tl-bl",
35265         "south" : "bl-tl"
35266     },
35267
35268     getAnchor : function(){
35269         return this.anchors[this.position];
35270     },
35271
35272     getCollapseAnchor : function(){
35273         return this.canchors[this.position];
35274     },
35275
35276     getSlideAnchor : function(){
35277         return this.sanchors[this.position];
35278     },
35279
35280     getAlignAdj : function(){
35281         var cm = this.cmargins;
35282         switch(this.position){
35283             case "west":
35284                 return [0, 0];
35285             break;
35286             case "east":
35287                 return [0, 0];
35288             break;
35289             case "north":
35290                 return [0, 0];
35291             break;
35292             case "south":
35293                 return [0, 0];
35294             break;
35295         }
35296     },
35297
35298     getExpandAdj : function(){
35299         var c = this.collapsedEl, cm = this.cmargins;
35300         switch(this.position){
35301             case "west":
35302                 return [-(cm.right+c.getWidth()+cm.left), 0];
35303             break;
35304             case "east":
35305                 return [cm.right+c.getWidth()+cm.left, 0];
35306             break;
35307             case "north":
35308                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35309             break;
35310             case "south":
35311                 return [0, cm.top+cm.bottom+c.getHeight()];
35312             break;
35313         }
35314     }
35315 });/*
35316  * Based on:
35317  * Ext JS Library 1.1.1
35318  * Copyright(c) 2006-2007, Ext JS, LLC.
35319  *
35320  * Originally Released Under LGPL - original licence link has changed is not relivant.
35321  *
35322  * Fork - LGPL
35323  * <script type="text/javascript">
35324  */
35325 /*
35326  * These classes are private internal classes
35327  */
35328 Roo.bootstrap.layout.Center = function(config){
35329     config.region = "center";
35330     Roo.bootstrap.layout.Region.call(this, config);
35331     this.visible = true;
35332     this.minWidth = config.minWidth || 20;
35333     this.minHeight = config.minHeight || 20;
35334 };
35335
35336 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35337     hide : function(){
35338         // center panel can't be hidden
35339     },
35340     
35341     show : function(){
35342         // center panel can't be hidden
35343     },
35344     
35345     getMinWidth: function(){
35346         return this.minWidth;
35347     },
35348     
35349     getMinHeight: function(){
35350         return this.minHeight;
35351     }
35352 });
35353
35354
35355
35356
35357  
35358
35359
35360
35361
35362
35363 Roo.bootstrap.layout.North = function(config)
35364 {
35365     config.region = 'north';
35366     config.cursor = 'n-resize';
35367     
35368     Roo.bootstrap.layout.Split.call(this, config);
35369     
35370     
35371     if(this.split){
35372         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35373         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35374         this.split.el.addClass("roo-layout-split-v");
35375     }
35376     var size = config.initialSize || config.height;
35377     if(typeof size != "undefined"){
35378         this.el.setHeight(size);
35379     }
35380 };
35381 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35382 {
35383     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35384     
35385     
35386     
35387     getBox : function(){
35388         if(this.collapsed){
35389             return this.collapsedEl.getBox();
35390         }
35391         var box = this.el.getBox();
35392         if(this.split){
35393             box.height += this.split.el.getHeight();
35394         }
35395         return box;
35396     },
35397     
35398     updateBox : function(box){
35399         if(this.split && !this.collapsed){
35400             box.height -= this.split.el.getHeight();
35401             this.split.el.setLeft(box.x);
35402             this.split.el.setTop(box.y+box.height);
35403             this.split.el.setWidth(box.width);
35404         }
35405         if(this.collapsed){
35406             this.updateBody(box.width, null);
35407         }
35408         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35409     }
35410 });
35411
35412
35413
35414
35415
35416 Roo.bootstrap.layout.South = function(config){
35417     config.region = 'south';
35418     config.cursor = 's-resize';
35419     Roo.bootstrap.layout.Split.call(this, config);
35420     if(this.split){
35421         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35422         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35423         this.split.el.addClass("roo-layout-split-v");
35424     }
35425     var size = config.initialSize || config.height;
35426     if(typeof size != "undefined"){
35427         this.el.setHeight(size);
35428     }
35429 };
35430
35431 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35432     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35433     getBox : function(){
35434         if(this.collapsed){
35435             return this.collapsedEl.getBox();
35436         }
35437         var box = this.el.getBox();
35438         if(this.split){
35439             var sh = this.split.el.getHeight();
35440             box.height += sh;
35441             box.y -= sh;
35442         }
35443         return box;
35444     },
35445     
35446     updateBox : function(box){
35447         if(this.split && !this.collapsed){
35448             var sh = this.split.el.getHeight();
35449             box.height -= sh;
35450             box.y += sh;
35451             this.split.el.setLeft(box.x);
35452             this.split.el.setTop(box.y-sh);
35453             this.split.el.setWidth(box.width);
35454         }
35455         if(this.collapsed){
35456             this.updateBody(box.width, null);
35457         }
35458         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35459     }
35460 });
35461
35462 Roo.bootstrap.layout.East = function(config){
35463     config.region = "east";
35464     config.cursor = "e-resize";
35465     Roo.bootstrap.layout.Split.call(this, config);
35466     if(this.split){
35467         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
35468         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35469         this.split.el.addClass("roo-layout-split-h");
35470     }
35471     var size = config.initialSize || config.width;
35472     if(typeof size != "undefined"){
35473         this.el.setWidth(size);
35474     }
35475 };
35476 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35477     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35478     getBox : function(){
35479         if(this.collapsed){
35480             return this.collapsedEl.getBox();
35481         }
35482         var box = this.el.getBox();
35483         if(this.split){
35484             var sw = this.split.el.getWidth();
35485             box.width += sw;
35486             box.x -= sw;
35487         }
35488         return box;
35489     },
35490
35491     updateBox : function(box){
35492         if(this.split && !this.collapsed){
35493             var sw = this.split.el.getWidth();
35494             box.width -= sw;
35495             this.split.el.setLeft(box.x);
35496             this.split.el.setTop(box.y);
35497             this.split.el.setHeight(box.height);
35498             box.x += sw;
35499         }
35500         if(this.collapsed){
35501             this.updateBody(null, box.height);
35502         }
35503         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35504     }
35505 });
35506
35507 Roo.bootstrap.layout.West = function(config){
35508     config.region = "west";
35509     config.cursor = "w-resize";
35510     
35511     Roo.bootstrap.layout.Split.call(this, config);
35512     if(this.split){
35513         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
35514         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35515         this.split.el.addClass("roo-layout-split-h");
35516     }
35517     
35518 };
35519 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
35520     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35521     
35522     onRender: function(ctr, pos)
35523     {
35524         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
35525         var size = this.config.initialSize || this.config.width;
35526         if(typeof size != "undefined"){
35527             this.el.setWidth(size);
35528         }
35529     },
35530     
35531     getBox : function(){
35532         if(this.collapsed){
35533             return this.collapsedEl.getBox();
35534         }
35535         var box = this.el.getBox();
35536         if(this.split){
35537             box.width += this.split.el.getWidth();
35538         }
35539         return box;
35540     },
35541     
35542     updateBox : function(box){
35543         if(this.split && !this.collapsed){
35544             var sw = this.split.el.getWidth();
35545             box.width -= sw;
35546             this.split.el.setLeft(box.x+box.width);
35547             this.split.el.setTop(box.y);
35548             this.split.el.setHeight(box.height);
35549         }
35550         if(this.collapsed){
35551             this.updateBody(null, box.height);
35552         }
35553         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35554     }
35555 });
35556 Roo.namespace("Roo.bootstrap.panel");/*
35557  * Based on:
35558  * Ext JS Library 1.1.1
35559  * Copyright(c) 2006-2007, Ext JS, LLC.
35560  *
35561  * Originally Released Under LGPL - original licence link has changed is not relivant.
35562  *
35563  * Fork - LGPL
35564  * <script type="text/javascript">
35565  */
35566 /**
35567  * @class Roo.ContentPanel
35568  * @extends Roo.util.Observable
35569  * A basic ContentPanel element.
35570  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35571  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35572  * @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
35573  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35574  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35575  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35576  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35577  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35578  * @cfg {String} title          The title for this panel
35579  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35580  * @cfg {String} url            Calls {@link #setUrl} with this value
35581  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35582  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35583  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35584  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35585  * @cfg {Boolean} badges render the badges
35586
35587  * @constructor
35588  * Create a new ContentPanel.
35589  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35590  * @param {String/Object} config A string to set only the title or a config object
35591  * @param {String} content (optional) Set the HTML content for this panel
35592  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35593  */
35594 Roo.bootstrap.panel.Content = function( config){
35595     
35596     this.tpl = config.tpl || false;
35597     
35598     var el = config.el;
35599     var content = config.content;
35600
35601     if(config.autoCreate){ // xtype is available if this is called from factory
35602         el = Roo.id();
35603     }
35604     this.el = Roo.get(el);
35605     if(!this.el && config && config.autoCreate){
35606         if(typeof config.autoCreate == "object"){
35607             if(!config.autoCreate.id){
35608                 config.autoCreate.id = config.id||el;
35609             }
35610             this.el = Roo.DomHelper.append(document.body,
35611                         config.autoCreate, true);
35612         }else{
35613             var elcfg =  {   tag: "div",
35614                             cls: "roo-layout-inactive-content",
35615                             id: config.id||el
35616                             };
35617             if (config.html) {
35618                 elcfg.html = config.html;
35619                 
35620             }
35621                         
35622             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35623         }
35624     } 
35625     this.closable = false;
35626     this.loaded = false;
35627     this.active = false;
35628    
35629       
35630     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35631         
35632         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35633         
35634         this.wrapEl = this.el; //this.el.wrap();
35635         var ti = [];
35636         if (config.toolbar.items) {
35637             ti = config.toolbar.items ;
35638             delete config.toolbar.items ;
35639         }
35640         
35641         var nitems = [];
35642         this.toolbar.render(this.wrapEl, 'before');
35643         for(var i =0;i < ti.length;i++) {
35644           //  Roo.log(['add child', items[i]]);
35645             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35646         }
35647         this.toolbar.items = nitems;
35648         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35649         delete config.toolbar;
35650         
35651     }
35652     /*
35653     // xtype created footer. - not sure if will work as we normally have to render first..
35654     if (this.footer && !this.footer.el && this.footer.xtype) {
35655         if (!this.wrapEl) {
35656             this.wrapEl = this.el.wrap();
35657         }
35658     
35659         this.footer.container = this.wrapEl.createChild();
35660          
35661         this.footer = Roo.factory(this.footer, Roo);
35662         
35663     }
35664     */
35665     
35666      if(typeof config == "string"){
35667         this.title = config;
35668     }else{
35669         Roo.apply(this, config);
35670     }
35671     
35672     if(this.resizeEl){
35673         this.resizeEl = Roo.get(this.resizeEl, true);
35674     }else{
35675         this.resizeEl = this.el;
35676     }
35677     // handle view.xtype
35678     
35679  
35680     
35681     
35682     this.addEvents({
35683         /**
35684          * @event activate
35685          * Fires when this panel is activated. 
35686          * @param {Roo.ContentPanel} this
35687          */
35688         "activate" : true,
35689         /**
35690          * @event deactivate
35691          * Fires when this panel is activated. 
35692          * @param {Roo.ContentPanel} this
35693          */
35694         "deactivate" : true,
35695
35696         /**
35697          * @event resize
35698          * Fires when this panel is resized if fitToFrame is true.
35699          * @param {Roo.ContentPanel} this
35700          * @param {Number} width The width after any component adjustments
35701          * @param {Number} height The height after any component adjustments
35702          */
35703         "resize" : true,
35704         
35705          /**
35706          * @event render
35707          * Fires when this tab is created
35708          * @param {Roo.ContentPanel} this
35709          */
35710         "render" : true
35711         
35712         
35713         
35714     });
35715     
35716
35717     
35718     
35719     if(this.autoScroll){
35720         this.resizeEl.setStyle("overflow", "auto");
35721     } else {
35722         // fix randome scrolling
35723         //this.el.on('scroll', function() {
35724         //    Roo.log('fix random scolling');
35725         //    this.scrollTo('top',0); 
35726         //});
35727     }
35728     content = content || this.content;
35729     if(content){
35730         this.setContent(content);
35731     }
35732     if(config && config.url){
35733         this.setUrl(this.url, this.params, this.loadOnce);
35734     }
35735     
35736     
35737     
35738     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35739     
35740     if (this.view && typeof(this.view.xtype) != 'undefined') {
35741         this.view.el = this.el.appendChild(document.createElement("div"));
35742         this.view = Roo.factory(this.view); 
35743         this.view.render  &&  this.view.render(false, '');  
35744     }
35745     
35746     
35747     this.fireEvent('render', this);
35748 };
35749
35750 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35751     
35752     tabTip : '',
35753     
35754     setRegion : function(region){
35755         this.region = region;
35756         this.setActiveClass(region && !this.background);
35757     },
35758     
35759     
35760     setActiveClass: function(state)
35761     {
35762         if(state){
35763            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35764            this.el.setStyle('position','relative');
35765         }else{
35766            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35767            this.el.setStyle('position', 'absolute');
35768         } 
35769     },
35770     
35771     /**
35772      * Returns the toolbar for this Panel if one was configured. 
35773      * @return {Roo.Toolbar} 
35774      */
35775     getToolbar : function(){
35776         return this.toolbar;
35777     },
35778     
35779     setActiveState : function(active)
35780     {
35781         this.active = active;
35782         this.setActiveClass(active);
35783         if(!active){
35784             this.fireEvent("deactivate", this);
35785         }else{
35786             this.fireEvent("activate", this);
35787         }
35788     },
35789     /**
35790      * Updates this panel's element
35791      * @param {String} content The new content
35792      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35793     */
35794     setContent : function(content, loadScripts){
35795         this.el.update(content, loadScripts);
35796     },
35797
35798     ignoreResize : function(w, h){
35799         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35800             return true;
35801         }else{
35802             this.lastSize = {width: w, height: h};
35803             return false;
35804         }
35805     },
35806     /**
35807      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35808      * @return {Roo.UpdateManager} The UpdateManager
35809      */
35810     getUpdateManager : function(){
35811         return this.el.getUpdateManager();
35812     },
35813      /**
35814      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35815      * @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:
35816 <pre><code>
35817 panel.load({
35818     url: "your-url.php",
35819     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35820     callback: yourFunction,
35821     scope: yourObject, //(optional scope)
35822     discardUrl: false,
35823     nocache: false,
35824     text: "Loading...",
35825     timeout: 30,
35826     scripts: false
35827 });
35828 </code></pre>
35829      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35830      * 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.
35831      * @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}
35832      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35833      * @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.
35834      * @return {Roo.ContentPanel} this
35835      */
35836     load : function(){
35837         var um = this.el.getUpdateManager();
35838         um.update.apply(um, arguments);
35839         return this;
35840     },
35841
35842
35843     /**
35844      * 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.
35845      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35846      * @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)
35847      * @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)
35848      * @return {Roo.UpdateManager} The UpdateManager
35849      */
35850     setUrl : function(url, params, loadOnce){
35851         if(this.refreshDelegate){
35852             this.removeListener("activate", this.refreshDelegate);
35853         }
35854         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35855         this.on("activate", this.refreshDelegate);
35856         return this.el.getUpdateManager();
35857     },
35858     
35859     _handleRefresh : function(url, params, loadOnce){
35860         if(!loadOnce || !this.loaded){
35861             var updater = this.el.getUpdateManager();
35862             updater.update(url, params, this._setLoaded.createDelegate(this));
35863         }
35864     },
35865     
35866     _setLoaded : function(){
35867         this.loaded = true;
35868     }, 
35869     
35870     /**
35871      * Returns this panel's id
35872      * @return {String} 
35873      */
35874     getId : function(){
35875         return this.el.id;
35876     },
35877     
35878     /** 
35879      * Returns this panel's element - used by regiosn to add.
35880      * @return {Roo.Element} 
35881      */
35882     getEl : function(){
35883         return this.wrapEl || this.el;
35884     },
35885     
35886    
35887     
35888     adjustForComponents : function(width, height)
35889     {
35890         //Roo.log('adjustForComponents ');
35891         if(this.resizeEl != this.el){
35892             width -= this.el.getFrameWidth('lr');
35893             height -= this.el.getFrameWidth('tb');
35894         }
35895         if(this.toolbar){
35896             var te = this.toolbar.getEl();
35897             te.setWidth(width);
35898             height -= te.getHeight();
35899         }
35900         if(this.footer){
35901             var te = this.footer.getEl();
35902             te.setWidth(width);
35903             height -= te.getHeight();
35904         }
35905         
35906         
35907         if(this.adjustments){
35908             width += this.adjustments[0];
35909             height += this.adjustments[1];
35910         }
35911         return {"width": width, "height": height};
35912     },
35913     
35914     setSize : function(width, height){
35915         if(this.fitToFrame && !this.ignoreResize(width, height)){
35916             if(this.fitContainer && this.resizeEl != this.el){
35917                 this.el.setSize(width, height);
35918             }
35919             var size = this.adjustForComponents(width, height);
35920             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35921             this.fireEvent('resize', this, size.width, size.height);
35922         }
35923     },
35924     
35925     /**
35926      * Returns this panel's title
35927      * @return {String} 
35928      */
35929     getTitle : function(){
35930         
35931         if (typeof(this.title) != 'object') {
35932             return this.title;
35933         }
35934         
35935         var t = '';
35936         for (var k in this.title) {
35937             if (!this.title.hasOwnProperty(k)) {
35938                 continue;
35939             }
35940             
35941             if (k.indexOf('-') >= 0) {
35942                 var s = k.split('-');
35943                 for (var i = 0; i<s.length; i++) {
35944                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
35945                 }
35946             } else {
35947                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
35948             }
35949         }
35950         return t;
35951     },
35952     
35953     /**
35954      * Set this panel's title
35955      * @param {String} title
35956      */
35957     setTitle : function(title){
35958         this.title = title;
35959         if(this.region){
35960             this.region.updatePanelTitle(this, title);
35961         }
35962     },
35963     
35964     /**
35965      * Returns true is this panel was configured to be closable
35966      * @return {Boolean} 
35967      */
35968     isClosable : function(){
35969         return this.closable;
35970     },
35971     
35972     beforeSlide : function(){
35973         this.el.clip();
35974         this.resizeEl.clip();
35975     },
35976     
35977     afterSlide : function(){
35978         this.el.unclip();
35979         this.resizeEl.unclip();
35980     },
35981     
35982     /**
35983      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35984      *   Will fail silently if the {@link #setUrl} method has not been called.
35985      *   This does not activate the panel, just updates its content.
35986      */
35987     refresh : function(){
35988         if(this.refreshDelegate){
35989            this.loaded = false;
35990            this.refreshDelegate();
35991         }
35992     },
35993     
35994     /**
35995      * Destroys this panel
35996      */
35997     destroy : function(){
35998         this.el.removeAllListeners();
35999         var tempEl = document.createElement("span");
36000         tempEl.appendChild(this.el.dom);
36001         tempEl.innerHTML = "";
36002         this.el.remove();
36003         this.el = null;
36004     },
36005     
36006     /**
36007      * form - if the content panel contains a form - this is a reference to it.
36008      * @type {Roo.form.Form}
36009      */
36010     form : false,
36011     /**
36012      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36013      *    This contains a reference to it.
36014      * @type {Roo.View}
36015      */
36016     view : false,
36017     
36018       /**
36019      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36020      * <pre><code>
36021
36022 layout.addxtype({
36023        xtype : 'Form',
36024        items: [ .... ]
36025    }
36026 );
36027
36028 </code></pre>
36029      * @param {Object} cfg Xtype definition of item to add.
36030      */
36031     
36032     
36033     getChildContainer: function () {
36034         return this.getEl();
36035     }
36036     
36037     
36038     /*
36039         var  ret = new Roo.factory(cfg);
36040         return ret;
36041         
36042         
36043         // add form..
36044         if (cfg.xtype.match(/^Form$/)) {
36045             
36046             var el;
36047             //if (this.footer) {
36048             //    el = this.footer.container.insertSibling(false, 'before');
36049             //} else {
36050                 el = this.el.createChild();
36051             //}
36052
36053             this.form = new  Roo.form.Form(cfg);
36054             
36055             
36056             if ( this.form.allItems.length) {
36057                 this.form.render(el.dom);
36058             }
36059             return this.form;
36060         }
36061         // should only have one of theses..
36062         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36063             // views.. should not be just added - used named prop 'view''
36064             
36065             cfg.el = this.el.appendChild(document.createElement("div"));
36066             // factory?
36067             
36068             var ret = new Roo.factory(cfg);
36069              
36070              ret.render && ret.render(false, ''); // render blank..
36071             this.view = ret;
36072             return ret;
36073         }
36074         return false;
36075     }
36076     \*/
36077 });
36078  
36079 /**
36080  * @class Roo.bootstrap.panel.Grid
36081  * @extends Roo.bootstrap.panel.Content
36082  * @constructor
36083  * Create a new GridPanel.
36084  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36085  * @param {Object} config A the config object
36086   
36087  */
36088
36089
36090
36091 Roo.bootstrap.panel.Grid = function(config)
36092 {
36093     
36094       
36095     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36096         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36097
36098     config.el = this.wrapper;
36099     //this.el = this.wrapper;
36100     
36101       if (config.container) {
36102         // ctor'ed from a Border/panel.grid
36103         
36104         
36105         this.wrapper.setStyle("overflow", "hidden");
36106         this.wrapper.addClass('roo-grid-container');
36107
36108     }
36109     
36110     
36111     if(config.toolbar){
36112         var tool_el = this.wrapper.createChild();    
36113         this.toolbar = Roo.factory(config.toolbar);
36114         var ti = [];
36115         if (config.toolbar.items) {
36116             ti = config.toolbar.items ;
36117             delete config.toolbar.items ;
36118         }
36119         
36120         var nitems = [];
36121         this.toolbar.render(tool_el);
36122         for(var i =0;i < ti.length;i++) {
36123           //  Roo.log(['add child', items[i]]);
36124             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36125         }
36126         this.toolbar.items = nitems;
36127         
36128         delete config.toolbar;
36129     }
36130     
36131     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36132     config.grid.scrollBody = true;;
36133     config.grid.monitorWindowResize = false; // turn off autosizing
36134     config.grid.autoHeight = false;
36135     config.grid.autoWidth = false;
36136     
36137     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36138     
36139     if (config.background) {
36140         // render grid on panel activation (if panel background)
36141         this.on('activate', function(gp) {
36142             if (!gp.grid.rendered) {
36143                 gp.grid.render(this.wrapper);
36144                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36145             }
36146         });
36147             
36148     } else {
36149         this.grid.render(this.wrapper);
36150         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36151
36152     }
36153     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36154     // ??? needed ??? config.el = this.wrapper;
36155     
36156     
36157     
36158   
36159     // xtype created footer. - not sure if will work as we normally have to render first..
36160     if (this.footer && !this.footer.el && this.footer.xtype) {
36161         
36162         var ctr = this.grid.getView().getFooterPanel(true);
36163         this.footer.dataSource = this.grid.dataSource;
36164         this.footer = Roo.factory(this.footer, Roo);
36165         this.footer.render(ctr);
36166         
36167     }
36168     
36169     
36170     
36171     
36172      
36173 };
36174
36175 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36176     getId : function(){
36177         return this.grid.id;
36178     },
36179     
36180     /**
36181      * Returns the grid for this panel
36182      * @return {Roo.bootstrap.Table} 
36183      */
36184     getGrid : function(){
36185         return this.grid;    
36186     },
36187     
36188     setSize : function(width, height){
36189         if(!this.ignoreResize(width, height)){
36190             var grid = this.grid;
36191             var size = this.adjustForComponents(width, height);
36192             var gridel = grid.getGridEl();
36193             gridel.setSize(size.width, size.height);
36194             /*
36195             var thd = grid.getGridEl().select('thead',true).first();
36196             var tbd = grid.getGridEl().select('tbody', true).first();
36197             if (tbd) {
36198                 tbd.setSize(width, height - thd.getHeight());
36199             }
36200             */
36201             grid.autoSize();
36202         }
36203     },
36204      
36205     
36206     
36207     beforeSlide : function(){
36208         this.grid.getView().scroller.clip();
36209     },
36210     
36211     afterSlide : function(){
36212         this.grid.getView().scroller.unclip();
36213     },
36214     
36215     destroy : function(){
36216         this.grid.destroy();
36217         delete this.grid;
36218         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36219     }
36220 });
36221
36222 /**
36223  * @class Roo.bootstrap.panel.Nest
36224  * @extends Roo.bootstrap.panel.Content
36225  * @constructor
36226  * Create a new Panel, that can contain a layout.Border.
36227  * 
36228  * 
36229  * @param {Roo.BorderLayout} layout The layout for this panel
36230  * @param {String/Object} config A string to set only the title or a config object
36231  */
36232 Roo.bootstrap.panel.Nest = function(config)
36233 {
36234     // construct with only one argument..
36235     /* FIXME - implement nicer consturctors
36236     if (layout.layout) {
36237         config = layout;
36238         layout = config.layout;
36239         delete config.layout;
36240     }
36241     if (layout.xtype && !layout.getEl) {
36242         // then layout needs constructing..
36243         layout = Roo.factory(layout, Roo);
36244     }
36245     */
36246     
36247     config.el =  config.layout.getEl();
36248     
36249     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36250     
36251     config.layout.monitorWindowResize = false; // turn off autosizing
36252     this.layout = config.layout;
36253     this.layout.getEl().addClass("roo-layout-nested-layout");
36254     
36255     
36256     
36257     
36258 };
36259
36260 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36261
36262     setSize : function(width, height){
36263         if(!this.ignoreResize(width, height)){
36264             var size = this.adjustForComponents(width, height);
36265             var el = this.layout.getEl();
36266             if (size.height < 1) {
36267                 el.setWidth(size.width);   
36268             } else {
36269                 el.setSize(size.width, size.height);
36270             }
36271             var touch = el.dom.offsetWidth;
36272             this.layout.layout();
36273             // ie requires a double layout on the first pass
36274             if(Roo.isIE && !this.initialized){
36275                 this.initialized = true;
36276                 this.layout.layout();
36277             }
36278         }
36279     },
36280     
36281     // activate all subpanels if not currently active..
36282     
36283     setActiveState : function(active){
36284         this.active = active;
36285         this.setActiveClass(active);
36286         
36287         if(!active){
36288             this.fireEvent("deactivate", this);
36289             return;
36290         }
36291         
36292         this.fireEvent("activate", this);
36293         // not sure if this should happen before or after..
36294         if (!this.layout) {
36295             return; // should not happen..
36296         }
36297         var reg = false;
36298         for (var r in this.layout.regions) {
36299             reg = this.layout.getRegion(r);
36300             if (reg.getActivePanel()) {
36301                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36302                 reg.setActivePanel(reg.getActivePanel());
36303                 continue;
36304             }
36305             if (!reg.panels.length) {
36306                 continue;
36307             }
36308             reg.showPanel(reg.getPanel(0));
36309         }
36310         
36311         
36312         
36313         
36314     },
36315     
36316     /**
36317      * Returns the nested BorderLayout for this panel
36318      * @return {Roo.BorderLayout} 
36319      */
36320     getLayout : function(){
36321         return this.layout;
36322     },
36323     
36324      /**
36325      * Adds a xtype elements to the layout of the nested panel
36326      * <pre><code>
36327
36328 panel.addxtype({
36329        xtype : 'ContentPanel',
36330        region: 'west',
36331        items: [ .... ]
36332    }
36333 );
36334
36335 panel.addxtype({
36336         xtype : 'NestedLayoutPanel',
36337         region: 'west',
36338         layout: {
36339            center: { },
36340            west: { }   
36341         },
36342         items : [ ... list of content panels or nested layout panels.. ]
36343    }
36344 );
36345 </code></pre>
36346      * @param {Object} cfg Xtype definition of item to add.
36347      */
36348     addxtype : function(cfg) {
36349         return this.layout.addxtype(cfg);
36350     
36351     }
36352 });        /*
36353  * Based on:
36354  * Ext JS Library 1.1.1
36355  * Copyright(c) 2006-2007, Ext JS, LLC.
36356  *
36357  * Originally Released Under LGPL - original licence link has changed is not relivant.
36358  *
36359  * Fork - LGPL
36360  * <script type="text/javascript">
36361  */
36362 /**
36363  * @class Roo.TabPanel
36364  * @extends Roo.util.Observable
36365  * A lightweight tab container.
36366  * <br><br>
36367  * Usage:
36368  * <pre><code>
36369 // basic tabs 1, built from existing content
36370 var tabs = new Roo.TabPanel("tabs1");
36371 tabs.addTab("script", "View Script");
36372 tabs.addTab("markup", "View Markup");
36373 tabs.activate("script");
36374
36375 // more advanced tabs, built from javascript
36376 var jtabs = new Roo.TabPanel("jtabs");
36377 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36378
36379 // set up the UpdateManager
36380 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36381 var updater = tab2.getUpdateManager();
36382 updater.setDefaultUrl("ajax1.htm");
36383 tab2.on('activate', updater.refresh, updater, true);
36384
36385 // Use setUrl for Ajax loading
36386 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36387 tab3.setUrl("ajax2.htm", null, true);
36388
36389 // Disabled tab
36390 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36391 tab4.disable();
36392
36393 jtabs.activate("jtabs-1");
36394  * </code></pre>
36395  * @constructor
36396  * Create a new TabPanel.
36397  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36398  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36399  */
36400 Roo.bootstrap.panel.Tabs = function(config){
36401     /**
36402     * The container element for this TabPanel.
36403     * @type Roo.Element
36404     */
36405     this.el = Roo.get(config.el);
36406     delete config.el;
36407     if(config){
36408         if(typeof config == "boolean"){
36409             this.tabPosition = config ? "bottom" : "top";
36410         }else{
36411             Roo.apply(this, config);
36412         }
36413     }
36414     
36415     if(this.tabPosition == "bottom"){
36416         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36417         this.el.addClass("roo-tabs-bottom");
36418     }
36419     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36420     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36421     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36422     if(Roo.isIE){
36423         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36424     }
36425     if(this.tabPosition != "bottom"){
36426         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36427          * @type Roo.Element
36428          */
36429         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36430         this.el.addClass("roo-tabs-top");
36431     }
36432     this.items = [];
36433
36434     this.bodyEl.setStyle("position", "relative");
36435
36436     this.active = null;
36437     this.activateDelegate = this.activate.createDelegate(this);
36438
36439     this.addEvents({
36440         /**
36441          * @event tabchange
36442          * Fires when the active tab changes
36443          * @param {Roo.TabPanel} this
36444          * @param {Roo.TabPanelItem} activePanel The new active tab
36445          */
36446         "tabchange": true,
36447         /**
36448          * @event beforetabchange
36449          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
36450          * @param {Roo.TabPanel} this
36451          * @param {Object} e Set cancel to true on this object to cancel the tab change
36452          * @param {Roo.TabPanelItem} tab The tab being changed to
36453          */
36454         "beforetabchange" : true
36455     });
36456
36457     Roo.EventManager.onWindowResize(this.onResize, this);
36458     this.cpad = this.el.getPadding("lr");
36459     this.hiddenCount = 0;
36460
36461
36462     // toolbar on the tabbar support...
36463     if (this.toolbar) {
36464         alert("no toolbar support yet");
36465         this.toolbar  = false;
36466         /*
36467         var tcfg = this.toolbar;
36468         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
36469         this.toolbar = new Roo.Toolbar(tcfg);
36470         if (Roo.isSafari) {
36471             var tbl = tcfg.container.child('table', true);
36472             tbl.setAttribute('width', '100%');
36473         }
36474         */
36475         
36476     }
36477    
36478
36479
36480     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
36481 };
36482
36483 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
36484     /*
36485      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
36486      */
36487     tabPosition : "top",
36488     /*
36489      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
36490      */
36491     currentTabWidth : 0,
36492     /*
36493      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
36494      */
36495     minTabWidth : 40,
36496     /*
36497      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36498      */
36499     maxTabWidth : 250,
36500     /*
36501      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
36502      */
36503     preferredTabWidth : 175,
36504     /*
36505      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
36506      */
36507     resizeTabs : false,
36508     /*
36509      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
36510      */
36511     monitorResize : true,
36512     /*
36513      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
36514      */
36515     toolbar : false,
36516
36517     /**
36518      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
36519      * @param {String} id The id of the div to use <b>or create</b>
36520      * @param {String} text The text for the tab
36521      * @param {String} content (optional) Content to put in the TabPanelItem body
36522      * @param {Boolean} closable (optional) True to create a close icon on the tab
36523      * @return {Roo.TabPanelItem} The created TabPanelItem
36524      */
36525     addTab : function(id, text, content, closable, tpl)
36526     {
36527         var item = new Roo.bootstrap.panel.TabItem({
36528             panel: this,
36529             id : id,
36530             text : text,
36531             closable : closable,
36532             tpl : tpl
36533         });
36534         this.addTabItem(item);
36535         if(content){
36536             item.setContent(content);
36537         }
36538         return item;
36539     },
36540
36541     /**
36542      * Returns the {@link Roo.TabPanelItem} with the specified id/index
36543      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
36544      * @return {Roo.TabPanelItem}
36545      */
36546     getTab : function(id){
36547         return this.items[id];
36548     },
36549
36550     /**
36551      * Hides the {@link Roo.TabPanelItem} with the specified id/index
36552      * @param {String/Number} id The id or index of the TabPanelItem to hide.
36553      */
36554     hideTab : function(id){
36555         var t = this.items[id];
36556         if(!t.isHidden()){
36557            t.setHidden(true);
36558            this.hiddenCount++;
36559            this.autoSizeTabs();
36560         }
36561     },
36562
36563     /**
36564      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36565      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36566      */
36567     unhideTab : function(id){
36568         var t = this.items[id];
36569         if(t.isHidden()){
36570            t.setHidden(false);
36571            this.hiddenCount--;
36572            this.autoSizeTabs();
36573         }
36574     },
36575
36576     /**
36577      * Adds an existing {@link Roo.TabPanelItem}.
36578      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36579      */
36580     addTabItem : function(item){
36581         this.items[item.id] = item;
36582         this.items.push(item);
36583       //  if(this.resizeTabs){
36584     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36585   //         this.autoSizeTabs();
36586 //        }else{
36587 //            item.autoSize();
36588        // }
36589     },
36590
36591     /**
36592      * Removes a {@link Roo.TabPanelItem}.
36593      * @param {String/Number} id The id or index of the TabPanelItem to remove.
36594      */
36595     removeTab : function(id){
36596         var items = this.items;
36597         var tab = items[id];
36598         if(!tab) { return; }
36599         var index = items.indexOf(tab);
36600         if(this.active == tab && items.length > 1){
36601             var newTab = this.getNextAvailable(index);
36602             if(newTab) {
36603                 newTab.activate();
36604             }
36605         }
36606         this.stripEl.dom.removeChild(tab.pnode.dom);
36607         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36608             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36609         }
36610         items.splice(index, 1);
36611         delete this.items[tab.id];
36612         tab.fireEvent("close", tab);
36613         tab.purgeListeners();
36614         this.autoSizeTabs();
36615     },
36616
36617     getNextAvailable : function(start){
36618         var items = this.items;
36619         var index = start;
36620         // look for a next tab that will slide over to
36621         // replace the one being removed
36622         while(index < items.length){
36623             var item = items[++index];
36624             if(item && !item.isHidden()){
36625                 return item;
36626             }
36627         }
36628         // if one isn't found select the previous tab (on the left)
36629         index = start;
36630         while(index >= 0){
36631             var item = items[--index];
36632             if(item && !item.isHidden()){
36633                 return item;
36634             }
36635         }
36636         return null;
36637     },
36638
36639     /**
36640      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36641      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36642      */
36643     disableTab : function(id){
36644         var tab = this.items[id];
36645         if(tab && this.active != tab){
36646             tab.disable();
36647         }
36648     },
36649
36650     /**
36651      * Enables a {@link Roo.TabPanelItem} that is disabled.
36652      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36653      */
36654     enableTab : function(id){
36655         var tab = this.items[id];
36656         tab.enable();
36657     },
36658
36659     /**
36660      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36661      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36662      * @return {Roo.TabPanelItem} The TabPanelItem.
36663      */
36664     activate : function(id){
36665         var tab = this.items[id];
36666         if(!tab){
36667             return null;
36668         }
36669         if(tab == this.active || tab.disabled){
36670             return tab;
36671         }
36672         var e = {};
36673         this.fireEvent("beforetabchange", this, e, tab);
36674         if(e.cancel !== true && !tab.disabled){
36675             if(this.active){
36676                 this.active.hide();
36677             }
36678             this.active = this.items[id];
36679             this.active.show();
36680             this.fireEvent("tabchange", this, this.active);
36681         }
36682         return tab;
36683     },
36684
36685     /**
36686      * Gets the active {@link Roo.TabPanelItem}.
36687      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36688      */
36689     getActiveTab : function(){
36690         return this.active;
36691     },
36692
36693     /**
36694      * Updates the tab body element to fit the height of the container element
36695      * for overflow scrolling
36696      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36697      */
36698     syncHeight : function(targetHeight){
36699         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36700         var bm = this.bodyEl.getMargins();
36701         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36702         this.bodyEl.setHeight(newHeight);
36703         return newHeight;
36704     },
36705
36706     onResize : function(){
36707         if(this.monitorResize){
36708             this.autoSizeTabs();
36709         }
36710     },
36711
36712     /**
36713      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36714      */
36715     beginUpdate : function(){
36716         this.updating = true;
36717     },
36718
36719     /**
36720      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36721      */
36722     endUpdate : function(){
36723         this.updating = false;
36724         this.autoSizeTabs();
36725     },
36726
36727     /**
36728      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36729      */
36730     autoSizeTabs : function(){
36731         var count = this.items.length;
36732         var vcount = count - this.hiddenCount;
36733         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36734             return;
36735         }
36736         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36737         var availWidth = Math.floor(w / vcount);
36738         var b = this.stripBody;
36739         if(b.getWidth() > w){
36740             var tabs = this.items;
36741             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36742             if(availWidth < this.minTabWidth){
36743                 /*if(!this.sleft){    // incomplete scrolling code
36744                     this.createScrollButtons();
36745                 }
36746                 this.showScroll();
36747                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36748             }
36749         }else{
36750             if(this.currentTabWidth < this.preferredTabWidth){
36751                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36752             }
36753         }
36754     },
36755
36756     /**
36757      * Returns the number of tabs in this TabPanel.
36758      * @return {Number}
36759      */
36760      getCount : function(){
36761          return this.items.length;
36762      },
36763
36764     /**
36765      * Resizes all the tabs to the passed width
36766      * @param {Number} The new width
36767      */
36768     setTabWidth : function(width){
36769         this.currentTabWidth = width;
36770         for(var i = 0, len = this.items.length; i < len; i++) {
36771                 if(!this.items[i].isHidden()) {
36772                 this.items[i].setWidth(width);
36773             }
36774         }
36775     },
36776
36777     /**
36778      * Destroys this TabPanel
36779      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36780      */
36781     destroy : function(removeEl){
36782         Roo.EventManager.removeResizeListener(this.onResize, this);
36783         for(var i = 0, len = this.items.length; i < len; i++){
36784             this.items[i].purgeListeners();
36785         }
36786         if(removeEl === true){
36787             this.el.update("");
36788             this.el.remove();
36789         }
36790     },
36791     
36792     createStrip : function(container)
36793     {
36794         var strip = document.createElement("nav");
36795         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36796         container.appendChild(strip);
36797         return strip;
36798     },
36799     
36800     createStripList : function(strip)
36801     {
36802         // div wrapper for retard IE
36803         // returns the "tr" element.
36804         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36805         //'<div class="x-tabs-strip-wrap">'+
36806           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36807           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36808         return strip.firstChild; //.firstChild.firstChild.firstChild;
36809     },
36810     createBody : function(container)
36811     {
36812         var body = document.createElement("div");
36813         Roo.id(body, "tab-body");
36814         //Roo.fly(body).addClass("x-tabs-body");
36815         Roo.fly(body).addClass("tab-content");
36816         container.appendChild(body);
36817         return body;
36818     },
36819     createItemBody :function(bodyEl, id){
36820         var body = Roo.getDom(id);
36821         if(!body){
36822             body = document.createElement("div");
36823             body.id = id;
36824         }
36825         //Roo.fly(body).addClass("x-tabs-item-body");
36826         Roo.fly(body).addClass("tab-pane");
36827          bodyEl.insertBefore(body, bodyEl.firstChild);
36828         return body;
36829     },
36830     /** @private */
36831     createStripElements :  function(stripEl, text, closable, tpl)
36832     {
36833         var td = document.createElement("li"); // was td..
36834         
36835         
36836         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36837         
36838         
36839         stripEl.appendChild(td);
36840         /*if(closable){
36841             td.className = "x-tabs-closable";
36842             if(!this.closeTpl){
36843                 this.closeTpl = new Roo.Template(
36844                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36845                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36846                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36847                 );
36848             }
36849             var el = this.closeTpl.overwrite(td, {"text": text});
36850             var close = el.getElementsByTagName("div")[0];
36851             var inner = el.getElementsByTagName("em")[0];
36852             return {"el": el, "close": close, "inner": inner};
36853         } else {
36854         */
36855         // not sure what this is..
36856 //            if(!this.tabTpl){
36857                 //this.tabTpl = new Roo.Template(
36858                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36859                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36860                 //);
36861 //                this.tabTpl = new Roo.Template(
36862 //                   '<a href="#">' +
36863 //                   '<span unselectable="on"' +
36864 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36865 //                            ' >{text}</span></a>'
36866 //                );
36867 //                
36868 //            }
36869
36870
36871             var template = tpl || this.tabTpl || false;
36872             
36873             if(!template){
36874                 
36875                 template = new Roo.Template(
36876                    '<a href="#">' +
36877                    '<span unselectable="on"' +
36878                             (this.disableTooltips ? '' : ' title="{text}"') +
36879                             ' >{text}</span></a>'
36880                 );
36881             }
36882             
36883             switch (typeof(template)) {
36884                 case 'object' :
36885                     break;
36886                 case 'string' :
36887                     template = new Roo.Template(template);
36888                     break;
36889                 default :
36890                     break;
36891             }
36892             
36893             var el = template.overwrite(td, {"text": text});
36894             
36895             var inner = el.getElementsByTagName("span")[0];
36896             
36897             return {"el": el, "inner": inner};
36898             
36899     }
36900         
36901     
36902 });
36903
36904 /**
36905  * @class Roo.TabPanelItem
36906  * @extends Roo.util.Observable
36907  * Represents an individual item (tab plus body) in a TabPanel.
36908  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36909  * @param {String} id The id of this TabPanelItem
36910  * @param {String} text The text for the tab of this TabPanelItem
36911  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36912  */
36913 Roo.bootstrap.panel.TabItem = function(config){
36914     /**
36915      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36916      * @type Roo.TabPanel
36917      */
36918     this.tabPanel = config.panel;
36919     /**
36920      * The id for this TabPanelItem
36921      * @type String
36922      */
36923     this.id = config.id;
36924     /** @private */
36925     this.disabled = false;
36926     /** @private */
36927     this.text = config.text;
36928     /** @private */
36929     this.loaded = false;
36930     this.closable = config.closable;
36931
36932     /**
36933      * The body element for this TabPanelItem.
36934      * @type Roo.Element
36935      */
36936     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36937     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36938     this.bodyEl.setStyle("display", "block");
36939     this.bodyEl.setStyle("zoom", "1");
36940     //this.hideAction();
36941
36942     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36943     /** @private */
36944     this.el = Roo.get(els.el);
36945     this.inner = Roo.get(els.inner, true);
36946     this.textEl = Roo.get(this.el.dom.firstChild, true);
36947     this.pnode = Roo.get(els.el.parentNode, true);
36948     this.el.on("mousedown", this.onTabMouseDown, this);
36949     this.el.on("click", this.onTabClick, this);
36950     /** @private */
36951     if(config.closable){
36952         var c = Roo.get(els.close, true);
36953         c.dom.title = this.closeText;
36954         c.addClassOnOver("close-over");
36955         c.on("click", this.closeClick, this);
36956      }
36957
36958     this.addEvents({
36959          /**
36960          * @event activate
36961          * Fires when this tab becomes the active tab.
36962          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36963          * @param {Roo.TabPanelItem} this
36964          */
36965         "activate": true,
36966         /**
36967          * @event beforeclose
36968          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36969          * @param {Roo.TabPanelItem} this
36970          * @param {Object} e Set cancel to true on this object to cancel the close.
36971          */
36972         "beforeclose": true,
36973         /**
36974          * @event close
36975          * Fires when this tab is closed.
36976          * @param {Roo.TabPanelItem} this
36977          */
36978          "close": true,
36979         /**
36980          * @event deactivate
36981          * Fires when this tab is no longer the active tab.
36982          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36983          * @param {Roo.TabPanelItem} this
36984          */
36985          "deactivate" : true
36986     });
36987     this.hidden = false;
36988
36989     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36990 };
36991
36992 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36993            {
36994     purgeListeners : function(){
36995        Roo.util.Observable.prototype.purgeListeners.call(this);
36996        this.el.removeAllListeners();
36997     },
36998     /**
36999      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37000      */
37001     show : function(){
37002         this.pnode.addClass("active");
37003         this.showAction();
37004         if(Roo.isOpera){
37005             this.tabPanel.stripWrap.repaint();
37006         }
37007         this.fireEvent("activate", this.tabPanel, this);
37008     },
37009
37010     /**
37011      * Returns true if this tab is the active tab.
37012      * @return {Boolean}
37013      */
37014     isActive : function(){
37015         return this.tabPanel.getActiveTab() == this;
37016     },
37017
37018     /**
37019      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37020      */
37021     hide : function(){
37022         this.pnode.removeClass("active");
37023         this.hideAction();
37024         this.fireEvent("deactivate", this.tabPanel, this);
37025     },
37026
37027     hideAction : function(){
37028         this.bodyEl.hide();
37029         this.bodyEl.setStyle("position", "absolute");
37030         this.bodyEl.setLeft("-20000px");
37031         this.bodyEl.setTop("-20000px");
37032     },
37033
37034     showAction : function(){
37035         this.bodyEl.setStyle("position", "relative");
37036         this.bodyEl.setTop("");
37037         this.bodyEl.setLeft("");
37038         this.bodyEl.show();
37039     },
37040
37041     /**
37042      * Set the tooltip for the tab.
37043      * @param {String} tooltip The tab's tooltip
37044      */
37045     setTooltip : function(text){
37046         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37047             this.textEl.dom.qtip = text;
37048             this.textEl.dom.removeAttribute('title');
37049         }else{
37050             this.textEl.dom.title = text;
37051         }
37052     },
37053
37054     onTabClick : function(e){
37055         e.preventDefault();
37056         this.tabPanel.activate(this.id);
37057     },
37058
37059     onTabMouseDown : function(e){
37060         e.preventDefault();
37061         this.tabPanel.activate(this.id);
37062     },
37063 /*
37064     getWidth : function(){
37065         return this.inner.getWidth();
37066     },
37067
37068     setWidth : function(width){
37069         var iwidth = width - this.pnode.getPadding("lr");
37070         this.inner.setWidth(iwidth);
37071         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37072         this.pnode.setWidth(width);
37073     },
37074 */
37075     /**
37076      * Show or hide the tab
37077      * @param {Boolean} hidden True to hide or false to show.
37078      */
37079     setHidden : function(hidden){
37080         this.hidden = hidden;
37081         this.pnode.setStyle("display", hidden ? "none" : "");
37082     },
37083
37084     /**
37085      * Returns true if this tab is "hidden"
37086      * @return {Boolean}
37087      */
37088     isHidden : function(){
37089         return this.hidden;
37090     },
37091
37092     /**
37093      * Returns the text for this tab
37094      * @return {String}
37095      */
37096     getText : function(){
37097         return this.text;
37098     },
37099     /*
37100     autoSize : function(){
37101         //this.el.beginMeasure();
37102         this.textEl.setWidth(1);
37103         /*
37104          *  #2804 [new] Tabs in Roojs
37105          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37106          */
37107         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37108         //this.el.endMeasure();
37109     //},
37110
37111     /**
37112      * Sets the text for the tab (Note: this also sets the tooltip text)
37113      * @param {String} text The tab's text and tooltip
37114      */
37115     setText : function(text){
37116         this.text = text;
37117         this.textEl.update(text);
37118         this.setTooltip(text);
37119         //if(!this.tabPanel.resizeTabs){
37120         //    this.autoSize();
37121         //}
37122     },
37123     /**
37124      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37125      */
37126     activate : function(){
37127         this.tabPanel.activate(this.id);
37128     },
37129
37130     /**
37131      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37132      */
37133     disable : function(){
37134         if(this.tabPanel.active != this){
37135             this.disabled = true;
37136             this.pnode.addClass("disabled");
37137         }
37138     },
37139
37140     /**
37141      * Enables this TabPanelItem if it was previously disabled.
37142      */
37143     enable : function(){
37144         this.disabled = false;
37145         this.pnode.removeClass("disabled");
37146     },
37147
37148     /**
37149      * Sets the content for this TabPanelItem.
37150      * @param {String} content The content
37151      * @param {Boolean} loadScripts true to look for and load scripts
37152      */
37153     setContent : function(content, loadScripts){
37154         this.bodyEl.update(content, loadScripts);
37155     },
37156
37157     /**
37158      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37159      * @return {Roo.UpdateManager} The UpdateManager
37160      */
37161     getUpdateManager : function(){
37162         return this.bodyEl.getUpdateManager();
37163     },
37164
37165     /**
37166      * Set a URL to be used to load the content for this TabPanelItem.
37167      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37168      * @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)
37169      * @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)
37170      * @return {Roo.UpdateManager} The UpdateManager
37171      */
37172     setUrl : function(url, params, loadOnce){
37173         if(this.refreshDelegate){
37174             this.un('activate', this.refreshDelegate);
37175         }
37176         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37177         this.on("activate", this.refreshDelegate);
37178         return this.bodyEl.getUpdateManager();
37179     },
37180
37181     /** @private */
37182     _handleRefresh : function(url, params, loadOnce){
37183         if(!loadOnce || !this.loaded){
37184             var updater = this.bodyEl.getUpdateManager();
37185             updater.update(url, params, this._setLoaded.createDelegate(this));
37186         }
37187     },
37188
37189     /**
37190      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37191      *   Will fail silently if the setUrl method has not been called.
37192      *   This does not activate the panel, just updates its content.
37193      */
37194     refresh : function(){
37195         if(this.refreshDelegate){
37196            this.loaded = false;
37197            this.refreshDelegate();
37198         }
37199     },
37200
37201     /** @private */
37202     _setLoaded : function(){
37203         this.loaded = true;
37204     },
37205
37206     /** @private */
37207     closeClick : function(e){
37208         var o = {};
37209         e.stopEvent();
37210         this.fireEvent("beforeclose", this, o);
37211         if(o.cancel !== true){
37212             this.tabPanel.removeTab(this.id);
37213         }
37214     },
37215     /**
37216      * The text displayed in the tooltip for the close icon.
37217      * @type String
37218      */
37219     closeText : "Close this tab"
37220 });